2727#include "run-command.h"
2828#ifdef NO_OPENSSL
2929typedef void * SSL ;
30+ #else
31+ #include <openssl/evp.h>
32+ #include <openssl/hmac.h>
3033#endif
3134
3235struct store_conf {
@@ -140,6 +143,20 @@ struct imap_server_conf {
140143 int use_ssl ;
141144 int ssl_verify ;
142145 int use_html ;
146+ char * auth_method ;
147+ };
148+
149+ static struct imap_server_conf server = {
150+ NULL , /* name */
151+ NULL , /* tunnel */
152+ NULL , /* host */
153+ 0 , /* port */
154+ NULL , /* user */
155+ NULL , /* pass */
156+ 0 , /* use_ssl */
157+ 1 , /* ssl_verify */
158+ 0 , /* use_html */
159+ NULL , /* auth_method */
143160};
144161
145162struct imap_store_conf {
@@ -214,6 +231,7 @@ enum CAPABILITY {
214231 LITERALPLUS ,
215232 NAMESPACE ,
216233 STARTTLS ,
234+ AUTH_CRAM_MD5 ,
217235};
218236
219237static const char * cap_list [] = {
@@ -222,6 +240,7 @@ static const char *cap_list[] = {
222240 "LITERAL+" ,
223241 "NAMESPACE" ,
224242 "STARTTLS" ,
243+ "AUTH=CRAM-MD5" ,
225244};
226245
227246#define RESP_OK 0
@@ -949,6 +968,87 @@ static void imap_close_store(struct store *ctx)
949968 free (ctx );
950969}
951970
971+ #ifndef NO_OPENSSL
972+
973+ /*
974+ * hexchar() and cram() functions are based on the code from the isync
975+ * project (http://isync.sf.net/).
976+ */
977+ static char hexchar (unsigned int b )
978+ {
979+ return b < 10 ? '0' + b : 'a' + (b - 10 );
980+ }
981+
982+ #define ENCODED_SIZE (n ) (4*((n+2)/3))
983+ static char * cram (const char * challenge_64 , const char * user , const char * pass )
984+ {
985+ int i , resp_len , encoded_len , decoded_len ;
986+ HMAC_CTX hmac ;
987+ unsigned char hash [16 ];
988+ char hex [33 ];
989+ char * response , * response_64 , * challenge ;
990+
991+ /*
992+ * length of challenge_64 (i.e. base-64 encoded string) is a good
993+ * enough upper bound for challenge (decoded result).
994+ */
995+ encoded_len = strlen (challenge_64 );
996+ challenge = xmalloc (encoded_len );
997+ decoded_len = EVP_DecodeBlock ((unsigned char * )challenge ,
998+ (unsigned char * )challenge_64 , encoded_len );
999+ if (decoded_len < 0 )
1000+ die ("invalid challenge %s" , challenge_64 );
1001+ HMAC_Init (& hmac , (unsigned char * )pass , strlen (pass ), EVP_md5 ());
1002+ HMAC_Update (& hmac , (unsigned char * )challenge , decoded_len );
1003+ HMAC_Final (& hmac , hash , NULL );
1004+ HMAC_CTX_cleanup (& hmac );
1005+
1006+ hex [32 ] = 0 ;
1007+ for (i = 0 ; i < 16 ; i ++ ) {
1008+ hex [2 * i ] = hexchar ((hash [i ] >> 4 ) & 0xf );
1009+ hex [2 * i + 1 ] = hexchar (hash [i ] & 0xf );
1010+ }
1011+
1012+ /* response: "<user> <digest in hex>" */
1013+ resp_len = strlen (user ) + 1 + strlen (hex ) + 1 ;
1014+ response = xmalloc (resp_len );
1015+ sprintf (response , "%s %s" , user , hex );
1016+
1017+ response_64 = xmalloc (ENCODED_SIZE (resp_len ) + 1 );
1018+ encoded_len = EVP_EncodeBlock ((unsigned char * )response_64 ,
1019+ (unsigned char * )response , resp_len );
1020+ if (encoded_len < 0 )
1021+ die ("EVP_EncodeBlock error" );
1022+ response_64 [encoded_len ] = '\0' ;
1023+ return (char * )response_64 ;
1024+ }
1025+
1026+ #else
1027+
1028+ static char * cram (const char * challenge_64 , const char * user , const char * pass )
1029+ {
1030+ die ("If you want to use CRAM-MD5 authenticate method, "
1031+ "you have to build git-imap-send with OpenSSL library." );
1032+ }
1033+
1034+ #endif
1035+
1036+ static int auth_cram_md5 (struct imap_store * ctx , struct imap_cmd * cmd , const char * prompt )
1037+ {
1038+ int ret ;
1039+ char * response ;
1040+
1041+ response = cram (prompt , server .user , server .pass );
1042+
1043+ ret = socket_write (& ctx -> imap -> buf .sock , response , strlen (response ));
1044+ if (ret != strlen (response ))
1045+ return error ("IMAP error: sending response failed\n" );
1046+
1047+ free (response );
1048+
1049+ return 0 ;
1050+ }
1051+
9521052static struct store * imap_open_store (struct imap_server_conf * srvc )
9531053{
9541054 struct imap_store * ctx ;
@@ -1130,9 +1230,34 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
11301230 if (!imap -> buf .sock .ssl )
11311231 imap_warn ("*** IMAP Warning *** Password is being "
11321232 "sent in the clear\n" );
1133- if (imap_exec (ctx , NULL , "LOGIN \"%s\" \"%s\"" , srvc -> user , srvc -> pass ) != RESP_OK ) {
1134- fprintf (stderr , "IMAP error: LOGIN failed\n" );
1135- goto bail ;
1233+
1234+ if (srvc -> auth_method ) {
1235+ struct imap_cmd_cb cb ;
1236+
1237+ if (!strcmp (srvc -> auth_method , "CRAM-MD5" )) {
1238+ if (!CAP (AUTH_CRAM_MD5 )) {
1239+ fprintf (stderr , "You specified"
1240+ "CRAM-MD5 as authentication method, "
1241+ "but %s doesn't support it.\n" , srvc -> host );
1242+ goto bail ;
1243+ }
1244+ /* CRAM-MD5 */
1245+
1246+ memset (& cb , 0 , sizeof (cb ));
1247+ cb .cont = auth_cram_md5 ;
1248+ if (imap_exec (ctx , & cb , "AUTHENTICATE CRAM-MD5" ) != RESP_OK ) {
1249+ fprintf (stderr , "IMAP error: AUTHENTICATE CRAM-MD5 failed\n" );
1250+ goto bail ;
1251+ }
1252+ } else {
1253+ fprintf (stderr , "Unknown authentication method:%s\n" , srvc -> host );
1254+ goto bail ;
1255+ }
1256+ } else {
1257+ if (imap_exec (ctx , NULL , "LOGIN \"%s\" \"%s\"" , srvc -> user , srvc -> pass ) != RESP_OK ) {
1258+ fprintf (stderr , "IMAP error: LOGIN failed\n" );
1259+ goto bail ;
1260+ }
11361261 }
11371262 } /* !preauth */
11381263
@@ -1310,18 +1435,6 @@ static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
13101435 return 1 ;
13111436}
13121437
1313- static struct imap_server_conf server = {
1314- NULL , /* name */
1315- NULL , /* tunnel */
1316- NULL , /* host */
1317- 0 , /* port */
1318- NULL , /* user */
1319- NULL , /* pass */
1320- 0 , /* use_ssl */
1321- 1 , /* ssl_verify */
1322- 0 , /* use_html */
1323- };
1324-
13251438static char * imap_folder ;
13261439
13271440static int git_imap_config (const char * key , const char * val , void * cb )
@@ -1361,6 +1474,9 @@ static int git_imap_config(const char *key, const char *val, void *cb)
13611474 server .port = git_config_int (key , val );
13621475 else if (!strcmp ("tunnel" , key ))
13631476 server .tunnel = xstrdup (val );
1477+ else if (!strcmp ("authmethod" , key ))
1478+ server .auth_method = xstrdup (val );
1479+
13641480 return 0 ;
13651481}
13661482
0 commit comments