@@ -139,6 +139,7 @@ enum CAPABILITY {
139139 LITERALPLUS ,
140140 NAMESPACE ,
141141 STARTTLS ,
142+ AUTH_PLAIN ,
142143 AUTH_CRAM_MD5 ,
143144 AUTH_OAUTHBEARER ,
144145 AUTH_XOAUTH2
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
150151 "LITERAL+" ,
151152 "NAMESPACE" ,
152153 "STARTTLS" ,
154+ "AUTH=PLAIN" ,
153155 "AUTH=CRAM-MD5" ,
154156 "AUTH=OAUTHBEARER" ,
155157 "AUTH=XOAUTH2" ,
@@ -851,6 +853,40 @@ static char hexchar(unsigned int b)
851853}
852854
853855#define ENCODED_SIZE (n ) (4 * DIV_ROUND_UP((n), 3))
856+ static char * plain_base64 (const char * user , const char * pass )
857+ {
858+ int user_len = strlen (user );
859+ int pass_len = strlen (pass );
860+ int raw_len = 1 + user_len + 1 + pass_len ;
861+ int b64_len ;
862+ char * raw , * b64 ;
863+
864+ /* Compose the PLAIN string
865+ *
866+ * The username and password are combined to one string and base64 encoded.
867+ * "\0user\0pass"
868+ *
869+ * The method has been described in RFC4616.
870+ *
871+ * https://datatracker.ietf.org/doc/html/rfc4616
872+ */
873+ raw = xmallocz (raw_len );
874+ raw [0 ] = '\0' ;
875+ memcpy (raw + 1 , user , user_len );
876+ raw [1 + user_len ] = '\0' ;
877+ memcpy (raw + 2 + user_len , pass , pass_len );
878+
879+ b64 = xmallocz (ENCODED_SIZE (raw_len ));
880+ b64_len = EVP_EncodeBlock ((unsigned char * )b64 , (unsigned char * )raw , raw_len );
881+ free (raw );
882+
883+ if (b64_len < 0 ) {
884+ free (b64 );
885+ return NULL ;
886+ }
887+ return b64 ;
888+ }
889+
854890static char * cram (const char * challenge_64 , const char * user , const char * pass )
855891{
856892 int i , resp_len , encoded_len , decoded_len ;
@@ -951,6 +987,13 @@ static char *xoauth2_base64(const char *user, const char *access_token)
951987
952988#else
953989
990+ static char * plain_base64 (const char * user UNUSED ,
991+ const char * access_token UNUSED )
992+ {
993+ die ("You are trying to use PLAIN authenticate method "
994+ "with OpenSSL library, but its support has not been compiled in." );
995+ }
996+
954997static char * cram (const char * challenge_64 UNUSED ,
955998 const char * user UNUSED ,
956999 const char * pass UNUSED )
@@ -975,6 +1018,26 @@ static char *xoauth2_base64(const char *user UNUSED,
9751018
9761019#endif
9771020
1021+ static int auth_plain (struct imap_store * ctx , const char * prompt UNUSED )
1022+ {
1023+ int ret ;
1024+ char * b64 ;
1025+
1026+ b64 = plain_base64 (ctx -> cfg -> user , ctx -> cfg -> pass );
1027+ if (!b64 )
1028+ return error ("PLAIN: base64 encoding failed" );
1029+
1030+ /* Send the base64-encoded response */
1031+ ret = socket_write (& ctx -> imap -> buf .sock , b64 , strlen (b64 ));
1032+ if (ret != (int )strlen (b64 )) {
1033+ free (b64 );
1034+ return error ("IMAP error: sending PLAIN response failed" );
1035+ }
1036+
1037+ free (b64 );
1038+ return 0 ;
1039+ }
1040+
9781041static int auth_cram_md5 (struct imap_store * ctx , const char * prompt )
9791042{
9801043 int ret ;
@@ -1207,7 +1270,22 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
12071270 if (srvc -> auth_method ) {
12081271 struct imap_cmd_cb cb ;
12091272
1210- if (!strcmp (srvc -> auth_method , "CRAM-MD5" )) {
1273+ if (!strcmp (srvc -> auth_method , "PLAIN" )) {
1274+ if (!CAP (AUTH_PLAIN )) {
1275+ fprintf (stderr , "You specified "
1276+ "PLAIN as authentication method, "
1277+ "but %s doesn't support it.\n" , srvc -> host );
1278+ goto bail ;
1279+ }
1280+ /* PLAIN */
1281+
1282+ memset (& cb , 0 , sizeof (cb ));
1283+ cb .cont = auth_plain ;
1284+ if (imap_exec (ctx , & cb , "AUTHENTICATE PLAIN" ) != RESP_OK ) {
1285+ fprintf (stderr , "IMAP error: AUTHENTICATE PLAIN failed\n" );
1286+ goto bail ;
1287+ }
1288+ } else if (!strcmp (srvc -> auth_method , "CRAM-MD5" )) {
12111289 if (!CAP (AUTH_CRAM_MD5 )) {
12121290 fprintf (stderr , "You specified "
12131291 "CRAM-MD5 as authentication method, "
0 commit comments