@matheusbsbs / matheus-bagliano / commits / ae9c606ed2
Fork 0

imap-send: support CRAM-MD5 authentication

CRAM-MD5 authentication ought to be independent from SSL, but NO_OPENSSL build will not support this because the base64 and md5 code are used from the OpenSSL library in this implementation. Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Hitoshi Mitake committed Feb 15, 2010 at 22:34 UTC ae9c606ed228ea6a17ddde3a1e26451d02f51df7
2 files changed +135 -15
Documentation/git-imap-send.txt
+4
@@ -71,6 +71,10 @@ imap.preformattedHTML::
71 option causes Thunderbird to send the patch as a plain/text,
72 format=fixed email. Default is `false`.
73
74 +imap.authMethod::
75 + Specify authenticate method for authentication with IMAP server.
76 + Current supported method is 'CRAM-MD5' only.
77 +
78 Examples
79 ~~~~~~~~
80
imap-send.c
+131 -15
@@ -27,6 +27,9 @@
27 #include "run-command.h"
28 #ifdef NO_OPENSSL
29 typedef void *SSL;
30 +#else
31 +#include <openssl/evp.h>
32 +#include <openssl/hmac.h>
33 #endif
34
35 struct store_conf {
@@ -140,6 +143,20 @@ struct imap_server_conf {
143 int use_ssl;
144 int ssl_verify;
145 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 */
160 };
161
162 struct imap_store_conf {
@@ -214,6 +231,7 @@ enum CAPABILITY {
231 LITERALPLUS,
232 NAMESPACE,
233 STARTTLS,
234 + AUTH_CRAM_MD5,
235 };
236
237 static const char *cap_list[] = {
@@ -222,6 +240,7 @@ static const char *cap_list[] = {
240 "LITERAL+",
241 "NAMESPACE",
242 "STARTTLS",
243 + "AUTH=CRAM-MD5",
244 };
245
246 #define RESP_OK 0
@@ -949,6 +968,87 @@ static void imap_close_store(struct store *ctx)
968 free(ctx);
969 }
970
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 +
1052 static struct store *imap_open_store(struct imap_server_conf *srvc)
1053 {
1054 struct imap_store *ctx;
@@ -1130,9 +1230,34 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
1230 if (!imap->buf.sock.ssl)
1231 imap_warn("*** IMAP Warning *** Password is being "
1232 "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 + }
1261 }
1262 } /* !preauth */
1263
@@ -1310,18 +1435,6 @@ static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
1435 return 1;
1436 }
1437
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 -
1438 static char *imap_folder;
1439
1440 static 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)
1474 server.port = git_config_int(key, val);
1475 else if (!strcmp("tunnel", key))
1476 server.tunnel = xstrdup(val);
1477 + else if (!strcmp("authmethod", key))
1478 + server.auth_method = xstrdup(val);
1479 +
1480 return 0;
1481 }
1482