@@ -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,41 @@ 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+ /*
865+ * Compose the PLAIN string
866+ *
867+ * The username and password are combined to one string and base64 encoded.
868+ * "\0user\0pass"
869+ *
870+ * The method has been described in RFC4616.
871+ *
872+ * https://datatracker.ietf.org/doc/html/rfc4616
873+ */
874+ raw = xmallocz (raw_len );
875+ raw [0 ] = '\0' ;
876+ memcpy (raw + 1 , user , user_len );
877+ raw [1 + user_len ] = '\0' ;
878+ memcpy (raw + 2 + user_len , pass , pass_len );
879+
880+ b64 = xmallocz (ENCODED_SIZE (raw_len ));
881+ b64_len = EVP_EncodeBlock ((unsigned char * )b64 , (unsigned char * )raw , raw_len );
882+ free (raw );
883+
884+ if (b64_len < 0 ) {
885+ free (b64 );
886+ return NULL ;
887+ }
888+ return b64 ;
889+ }
890+
854891static char * cram (const char * challenge_64 , const char * user , const char * pass )
855892{
856893 int i , resp_len , encoded_len , decoded_len ;
@@ -951,6 +988,26 @@ static char *xoauth2_base64(const char *user, const char *access_token)
951988 return b64 ;
952989}
953990
991+ static int auth_plain (struct imap_store * ctx , const char * prompt UNUSED )
992+ {
993+ int ret ;
994+ char * b64 ;
995+
996+ b64 = plain_base64 (ctx -> cfg -> user , ctx -> cfg -> pass );
997+ if (!b64 )
998+ return error ("PLAIN: base64 encoding failed" );
999+
1000+ /* Send the base64-encoded response */
1001+ ret = socket_write (& ctx -> imap -> buf .sock , b64 , strlen (b64 ));
1002+ if (ret != (int )strlen (b64 )) {
1003+ free (b64 );
1004+ return error ("IMAP error: sending PLAIN response failed" );
1005+ }
1006+
1007+ free (b64 );
1008+ return 0 ;
1009+ }
1010+
9541011static int auth_oauthbearer (struct imap_store * ctx , const char * prompt UNUSED )
9551012{
9561013 int ret ;
@@ -1001,6 +1058,7 @@ static char *cram(const char *challenge_64 UNUSED,
10011058 "you have to build git-imap-send with OpenSSL library." );
10021059}
10031060
1061+ #define auth_plain NULL
10041062#define auth_oauthbearer NULL
10051063#define auth_xoauth2 NULL
10061064
@@ -1198,7 +1256,29 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
11981256 if (srvc -> auth_method ) {
11991257 struct imap_cmd_cb cb ;
12001258
1201- if (!strcmp (srvc -> auth_method , "CRAM-MD5" )) {
1259+ if (!strcmp (srvc -> auth_method , "PLAIN" )) {
1260+ if (!CAP (AUTH_PLAIN )) {
1261+ fprintf (stderr , "You specified "
1262+ "PLAIN as authentication method, "
1263+ "but %s doesn't support it.\n" , srvc -> host );
1264+ goto bail ;
1265+ }
1266+
1267+ #ifdef NO_OPENSSL
1268+ fprintf (stderr , "You are trying to use PLAIN authentication mechanism "
1269+ "with OpenSSL library, but its support has not been compiled in." );
1270+ goto bail ;
1271+ #endif
1272+
1273+ /* PLAIN */
1274+
1275+ memset (& cb , 0 , sizeof (cb ));
1276+ cb .cont = auth_plain ;
1277+ if (imap_exec (ctx , & cb , "AUTHENTICATE PLAIN" ) != RESP_OK ) {
1278+ fprintf (stderr , "IMAP error: AUTHENTICATE PLAIN failed\n" );
1279+ goto bail ;
1280+ }
1281+ } else if (!strcmp (srvc -> auth_method , "CRAM-MD5" )) {
12021282 if (!CAP (AUTH_CRAM_MD5 )) {
12031283 fprintf (stderr , "You specified "
12041284 "CRAM-MD5 as authentication method, "
0 commit comments