@@ -139,6 +139,7 @@ enum CAPABILITY {
139
139
LITERALPLUS ,
140
140
NAMESPACE ,
141
141
STARTTLS ,
142
+ AUTH_PLAIN ,
142
143
AUTH_CRAM_MD5 ,
143
144
AUTH_OAUTHBEARER ,
144
145
AUTH_XOAUTH2 ,
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
150
151
"LITERAL+" ,
151
152
"NAMESPACE" ,
152
153
"STARTTLS" ,
154
+ "AUTH=PLAIN" ,
153
155
"AUTH=CRAM-MD5" ,
154
156
"AUTH=OAUTHBEARER" ,
155
157
"AUTH=XOAUTH2" ,
@@ -851,6 +853,41 @@ static char hexchar(unsigned int b)
851
853
}
852
854
853
855
#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
+
854
891
static char * cram (const char * challenge_64 , const char * user , const char * pass )
855
892
{
856
893
int i , resp_len , encoded_len , decoded_len ;
@@ -951,6 +988,26 @@ static char *xoauth2_base64(const char *user, const char *access_token)
951
988
return b64 ;
952
989
}
953
990
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
+
954
1011
static int auth_oauthbearer (struct imap_store * ctx , const char * prompt UNUSED )
955
1012
{
956
1013
int ret ;
@@ -1001,6 +1058,7 @@ static char *cram(const char *challenge_64 UNUSED,
1001
1058
"you have to build git-imap-send with OpenSSL library." );
1002
1059
}
1003
1060
1061
+ #define auth_plain NULL
1004
1062
#define auth_oauthbearer NULL
1005
1063
#define auth_xoauth2 NULL
1006
1064
@@ -1198,7 +1256,29 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
1198
1256
if (srvc -> auth_method ) {
1199
1257
struct imap_cmd_cb cb ;
1200
1258
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" )) {
1202
1282
if (!CAP (AUTH_CRAM_MD5 )) {
1203
1283
fprintf (stderr , "You specified "
1204
1284
"CRAM-MD5 as authentication method, "
0 commit comments