sync with cvs 4.64cvs4
[claws.git] / src / esmtp.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999,2000 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include <glib.h>
21 #include <stdio.h>
22 #include <string.h>
23
24 #include "esmtp.h"
25 #include "smtp.h"
26 #include "socket.h"
27 #include "utils.h"
28 #include "md5.h"
29 #include "base64.h"
30
31 #define MSGBUFSIZE      8192
32
33 static gint verbose = 1;
34 static gchar esmtp_response[MSGBUFSIZE];
35
36 /*
37 ** Function: hmac_md5
38 ** taken from the file rfc2104.txt
39 */
40 void hmac_md5(guchar *text, gint text_len, guchar *key, gint key_len,
41               guchar *digest)
42 {
43         MD5_CTX context;
44         guchar k_ipad[65];      /* inner padding -
45                                  * key XORd with ipad
46                                  */
47         guchar k_opad[65];      /* outer padding -
48                                  * key XORd with opad
49                                  */
50         guchar tk[16];
51         gint i;
52
53         /* if key is longer than 64 bytes reset it to key=MD5(key) */
54         if (key_len > 64) {
55                 MD5_CTX tctx;
56
57                 MD5Init(&tctx);
58                 MD5Update(&tctx, key, key_len);
59                 MD5Final(tk, &tctx);
60
61                 key = tk;
62                 key_len = 16;
63         }
64
65         /*
66          * the HMAC_MD5 transform looks like:
67          *
68          * MD5(K XOR opad, MD5(K XOR ipad, text))
69          *
70          * where K is an n byte key
71          * ipad is the byte 0x36 repeated 64 times
72          * opad is the byte 0x5c repeated 64 times
73          * and text is the data being protected
74          */
75
76         /* start out by storing key in pads */
77         bzero(k_ipad, sizeof k_ipad);
78         bzero(k_opad, sizeof k_opad);
79         bcopy(key, k_ipad, key_len);
80         bcopy(key, k_opad, key_len);
81
82         /* XOR key with ipad and opad values */
83         for (i = 0; i < 64; i++) {
84                 k_ipad[i] ^= 0x36;
85                 k_opad[i] ^= 0x5c;
86         }
87
88         /*
89          * perform inner MD5
90          */
91         MD5Init(&context);                   /* init context for 1st
92                                               * pass */
93         MD5Update(&context, k_ipad, 64);     /* start with inner pad */
94         MD5Update(&context, text, text_len); /* then text of datagram */
95         MD5Final(digest, &context);          /* finish up 1st pass */
96
97         /*
98          * perform outer MD5
99          */
100         MD5Init(&context);                   /* init context for 2nd
101                                               * pass */
102         MD5Update(&context, k_opad, 64);     /* start with outer pad */
103         MD5Update(&context, digest, 16);     /* then results of 1st
104                                               * hash */
105         MD5Final(digest, &context);          /* finish up 2nd pass */
106 }
107
108 void md5_hex_hmac(gchar *hexdigest, guchar *text, gint text_len,
109                   guchar *key, gint key_len)
110 {
111         guchar digest[16];
112         gint i;
113
114         hmac_md5(text, text_len, key, key_len, digest);
115         for(i = 0; i < 16; i++)
116                 sprintf(hexdigest + 2 * i, "%02x", digest[i]);
117 }
118
119 gint esmtp_auth_cram_md5(gint sock)
120 {
121         gchar buf[MSGBUFSIZE];
122
123         g_snprintf(buf, sizeof(buf), "AUTH CRAM-MD5");
124         sock_printf(sock, "%s\r\n", buf);
125
126         if (verbose)
127                 log_print("ESMTP> %s\n", buf);
128
129         return esmtp_ok(sock);
130 }
131
132 gint esmtp_auth_login(gint sock)
133 {
134         gchar buf[MSGBUFSIZE];
135
136         g_snprintf(buf, sizeof(buf), "AUTH LOGIN");
137         sock_printf(sock, "%s\r\n", buf);
138
139         if (verbose)
140                 log_print("ESMTP> %s\n", buf);
141
142         return esmtp_ok(sock);
143 }
144
145 gint esmtp_auth(gint sock, SMTPAuthType authtype,
146                 const gchar *userid, const gchar *passwd,
147                 gboolean use_smtp_auth)
148 {
149         gchar buf[MSGBUFSIZE];
150         guchar hexdigest[33];
151         gchar *challenge, *response, *response64;
152         gint challengelen;
153         //const gchar delimiters[]=" ";
154         gchar *token;
155
156         switch (authtype) {
157         case SMTPAUTH_LOGIN:
158                 if (!strncmp(esmtp_response, "334 ", 4))
159                         to64frombits(buf, userid, strlen(userid));
160                 else
161                         /* Server rejects AUTH */
162                         g_snprintf(buf, sizeof(buf), "*");
163
164                 sock_printf(sock, "%s\r\n", buf);
165                 if (verbose) log_print("ESMTP> USERID\n");
166
167                 esmtp_ok(sock); /* to read the answer from the server */
168
169                 if (!strncmp(esmtp_response, "334 ", 4))
170                         to64frombits(buf, passwd, strlen(passwd));
171                 else
172                         /* Server rejects AUTH */
173                         g_snprintf(buf, sizeof(buf), "*");
174
175                 sock_printf(sock, "%s\r\n", buf);
176                 if (verbose) log_print("ESMTP> PASSWORD\n");
177                 break;
178         case SMTPAUTH_CRAM_MD5:
179                 if (!strncmp(esmtp_response, "334 ", 4)) {
180                         /* remove 334 from esmtp_response */
181                         g_snprintf(buf, sizeof(buf), "%s",esmtp_response);
182                         token = strtok(buf, " ");
183                         token = strtok(NULL, " ");
184                         challenge = g_malloc(strlen(token)+1);
185                         challengelen = from64tobits(challenge,token);
186                         if (verbose)
187                                 log_print("ESMTP< Decoded: %s\n", challenge);
188
189                         g_snprintf(buf, sizeof(buf), "%s", passwd);
190                         md5_hex_hmac(hexdigest, challenge, challengelen,
191                                      buf, strlen(passwd));
192
193                         response = g_strdup_printf("%s %s", userid, hexdigest);
194                         if (verbose)
195                                 log_print("ESMTP> Encoded %s\n",response);
196
197                         response64 = g_malloc((strlen(response) + 3) * 2 + 1);
198                         to64frombits(response64, response, strlen(response));
199                         g_free(response);
200
201                         sock_printf(sock, "%s\r\n", response64);
202                         if (verbose) log_print("ESMTP> %s\n", response64);
203                         g_free(response64);
204                 } else {
205                         /* Server rejects AUTH */
206                         g_snprintf(buf, sizeof(buf), "*");
207                         sock_printf(sock, "%s\r\n", buf);
208                         if (verbose)
209                                 log_print("ESMTP> %s\n",buf);
210                 }
211                 break;
212         case SMTPAUTH_DIGEST_MD5:
213         default:
214                 /* stop esmtp_auth when no correct authtype */
215                 g_snprintf(buf, sizeof(buf), "*");
216                 sock_printf(sock, "%s\r\n", buf);
217                 if (verbose) log_print("ESMTP> %s\n", buf);
218                 break;
219         }
220
221         return esmtp_ok(sock);
222 }
223
224 gint esmtp_ok(gint sock)
225 {
226         while (sock_read(sock, esmtp_response, sizeof(esmtp_response) - 1)
227                != -1) {
228                 if (strlen(esmtp_response) < 4)
229                         return SM_ERROR;
230                 strretchomp(esmtp_response);
231
232                 if (verbose)
233                         log_print("ESMTP< %s\n", esmtp_response);
234
235                 if ((esmtp_response[0] == '1' || esmtp_response[0] == '2' ||
236                      esmtp_response[0] == '3') && esmtp_response[3] == ' ')
237                         return SM_OK;
238                 else if (esmtp_response[3] != '-')
239                         return SM_ERROR;
240                 else if (esmtp_response[0] == '5' &&
241                          esmtp_response[1] == '0' &&
242                          (esmtp_response[3] == '4' ||
243                           esmtp_response[3] == '3' ||
244                           esmtp_response[3] == '1'))
245                         return SM_ERROR;
246         }
247
248         return SM_UNRECOVERABLE;
249 }