1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4 *
5 * This library is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Jeffrey Stedfast <fejj@ximian.com>
18 */
19
20 #include "evolution-data-server-config.h"
21
22 #include <ctype.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include <glib/gi18n-lib.h>
28
29 #include "camel-charset-map.h"
30 #include "camel-iconv.h"
31 #include "camel-mime-utils.h"
32 #include "camel-net-utils.h"
33 #include "camel-network-settings.h"
34 #ifdef G_OS_WIN32
35 #include <winsock2.h>
36 #include <ws2tcpip.h>
37 #ifdef HAVE_WSPIAPI_H
38 #include <wspiapi.h>
39 #endif
40 #endif
41 #include "camel-sasl-digest-md5.h"
42
43 #define d(x)
44
45 #define PARANOID(x) x
46
47 /* Implements rfc2831 */
48
49 static CamelServiceAuthType sasl_digest_md5_auth_type = {
50 N_("DIGEST-MD5"),
51
52 N_("This option will connect to the server using a "
53 "secure DIGEST-MD5 password, if the server supports it."),
54
55 "DIGEST-MD5",
56 TRUE
57 };
58
59 enum {
60 STATE_AUTH,
61 STATE_FINAL
62 };
63
64 typedef struct {
65 const gchar *name;
66 guint type;
67 } DataType;
68
69 enum {
70 DIGEST_REALM,
71 DIGEST_NONCE,
72 DIGEST_QOP,
73 DIGEST_STALE,
74 DIGEST_MAXBUF,
75 DIGEST_CHARSET,
76 DIGEST_ALGORITHM,
77 DIGEST_CIPHER,
78 DIGEST_UNKNOWN
79 };
80
81 static DataType digest_args[] = {
82 { "realm", DIGEST_REALM },
83 { "nonce", DIGEST_NONCE },
84 { "qop", DIGEST_QOP },
85 { "stale", DIGEST_STALE },
86 { "maxbuf", DIGEST_MAXBUF },
87 { "charset", DIGEST_CHARSET },
88 { "algorithm", DIGEST_ALGORITHM },
89 { "cipher", DIGEST_CIPHER },
90 { NULL, DIGEST_UNKNOWN }
91 };
92
93 #define QOP_AUTH (1 << 0)
94 #define QOP_AUTH_INT (1 << 1)
95 #define QOP_AUTH_CONF (1 << 2)
96 #define QOP_INVALID (1 << 3)
97
98 static DataType qop_types[] = {
99 { "auth", QOP_AUTH },
100 { "auth-int", QOP_AUTH_INT },
101 { "auth-conf", QOP_AUTH_CONF },
102 { NULL, QOP_INVALID }
103 };
104
105 #define CIPHER_DES (1 << 0)
106 #define CIPHER_3DES (1 << 1)
107 #define CIPHER_RC4 (1 << 2)
108 #define CIPHER_RC4_40 (1 << 3)
109 #define CIPHER_RC4_56 (1 << 4)
110 #define CIPHER_INVALID (1 << 5)
111
112 static DataType cipher_types[] = {
113 { "des", CIPHER_DES },
114 { "3des", CIPHER_3DES },
115 { "rc4", CIPHER_RC4 },
116 { "rc4-40", CIPHER_RC4_40 },
117 { "rc4-56", CIPHER_RC4_56 },
118 { NULL, CIPHER_INVALID }
119 };
120
121 struct _param {
122 gchar *name;
123 gchar *value;
124 };
125
126 struct _DigestChallenge {
127 GPtrArray *realms;
128 gchar *nonce;
129 guint qop;
130 gboolean stale;
131 gint32 maxbuf;
132 gchar *charset;
133 gchar *algorithm;
134 guint cipher;
135 GList *params;
136 };
137
138 struct _DigestURI {
139 gchar *type;
140 gchar *host;
141 gchar *name;
142 };
143
144 struct _DigestResponse {
145 gchar *username;
146 gchar *realm;
147 gchar *nonce;
148 gchar *cnonce;
149 gchar nc[9];
150 guint qop;
151 struct _DigestURI *uri;
152 gchar resp[33];
153 guint32 maxbuf;
154 gchar *charset;
155 guint cipher;
156 gchar *authzid;
157 gchar *param;
158 };
159
160 struct _CamelSaslDigestMd5Private {
161 struct _DigestChallenge *challenge;
162 struct _DigestResponse *response;
163 gint state;
164 };
165
G_DEFINE_TYPE_WITH_PRIVATE(CamelSaslDigestMd5,camel_sasl_digest_md5,CAMEL_TYPE_SASL)166 G_DEFINE_TYPE_WITH_PRIVATE (CamelSaslDigestMd5, camel_sasl_digest_md5, CAMEL_TYPE_SASL)
167
168 static void
169 decode_lwsp (const gchar **in)
170 {
171 const gchar *inptr = *in;
172
173 while (isspace (*inptr))
174 inptr++;
175
176 *in = inptr;
177 }
178
179 static gchar *
decode_quoted_string(const gchar ** in)180 decode_quoted_string (const gchar **in)
181 {
182 const gchar *inptr = *in;
183 gchar *out = NULL, *outptr;
184 gint outlen;
185 gint c;
186
187 decode_lwsp (&inptr);
188 if (*inptr == '"') {
189 const gchar *intmp;
190 gint skip = 0;
191
192 /* first, calc length */
193 inptr++;
194 intmp = inptr;
195 while ((c = *intmp++) && c != '"') {
196 if (c == '\\' && *intmp) {
197 intmp++;
198 skip++;
199 }
200 }
201
202 outlen = intmp - inptr - skip;
203 out = outptr = g_malloc (outlen + 1);
204
205 while ((c = *inptr++) && c != '"') {
206 if (c == '\\' && *inptr) {
207 c = *inptr++;
208 }
209 *outptr++ = c;
210 }
211 *outptr = '\0';
212 }
213
214 *in = inptr;
215
216 return out;
217 }
218
219 static gchar *
decode_token(const gchar ** in)220 decode_token (const gchar **in)
221 {
222 const gchar *inptr = *in;
223 const gchar *start;
224
225 decode_lwsp (&inptr);
226 start = inptr;
227
228 while (*inptr && *inptr != '=' && *inptr != ',')
229 inptr++;
230
231 if (inptr > start) {
232 *in = inptr;
233 return g_strndup (start, inptr - start);
234 } else {
235 return NULL;
236 }
237 }
238
239 static gchar *
decode_value(const gchar ** in)240 decode_value (const gchar **in)
241 {
242 const gchar *inptr = *in;
243
244 decode_lwsp (&inptr);
245 if (*inptr == '"') {
246 d (printf ("decoding quoted string token\n"));
247 return decode_quoted_string (in);
248 } else {
249 d (printf ("decoding string token\n"));
250 return decode_token (in);
251 }
252 }
253
254 static GList *
parse_param_list(const gchar * tokens)255 parse_param_list (const gchar *tokens)
256 {
257 GList *params = NULL;
258 struct _param *param;
259 const gchar *ptr;
260
261 for (ptr = tokens; ptr && *ptr; ) {
262 param = g_new0 (struct _param, 1);
263 param->name = decode_token (&ptr);
264 if (*ptr == '=') {
265 ptr++;
266 param->value = decode_value (&ptr);
267 }
268
269 params = g_list_prepend (params, param);
270
271 if (*ptr == ',')
272 ptr++;
273 }
274
275 return params;
276 }
277
278 static guint
decode_data_type(DataType * dtype,const gchar * name)279 decode_data_type (DataType *dtype,
280 const gchar *name)
281 {
282 gint i;
283
284 for (i = 0; dtype[i].name; i++) {
285 if (!g_ascii_strcasecmp (dtype[i].name, name))
286 break;
287 }
288
289 return dtype[i].type;
290 }
291
292 #define get_digest_arg(name) decode_data_type (digest_args, name)
293 #define decode_qop(name) decode_data_type (qop_types, name)
294 #define decode_cipher(name) decode_data_type (cipher_types, name)
295
296 static const gchar *
type_to_string(DataType * dtype,guint type)297 type_to_string (DataType *dtype,
298 guint type)
299 {
300 gint i;
301
302 for (i = 0; dtype[i].name; i++) {
303 if (dtype[i].type == type)
304 break;
305 }
306
307 return dtype[i].name;
308 }
309
310 #define qop_to_string(type) type_to_string (qop_types, type)
311 #define cipher_to_string(type) type_to_string (cipher_types, type)
312
313 static void
digest_abort(gboolean * have_type,gboolean * abort)314 digest_abort (gboolean *have_type,
315 gboolean *abort)
316 {
317 if (*have_type)
318 *abort = TRUE;
319 *have_type = TRUE;
320 }
321
322 static struct _DigestChallenge *
parse_server_challenge(const gchar * tokens,gboolean * abort)323 parse_server_challenge (const gchar *tokens,
324 gboolean *abort)
325 {
326 struct _DigestChallenge *challenge = NULL;
327 GList *params, *p;
328 const gchar *ptr;
329 #ifdef PARANOID
330 gboolean got_algorithm = FALSE;
331 gboolean got_stale = FALSE;
332 gboolean got_maxbuf = FALSE;
333 gboolean got_charset = FALSE;
334 #endif /* PARANOID */
335
336 params = parse_param_list (tokens);
337 if (!params) {
338 *abort = TRUE;
339 return NULL;
340 }
341
342 *abort = FALSE;
343
344 challenge = g_new0 (struct _DigestChallenge, 1);
345 challenge->realms = g_ptr_array_new ();
346 challenge->maxbuf = 65536;
347
348 for (p = params; p; p = p->next) {
349 struct _param *param = p->data;
350 gint type;
351
352 type = get_digest_arg (param->name);
353 switch (type) {
354 case DIGEST_REALM:
355 for (ptr = param->value; ptr && *ptr; ) {
356 gchar *token;
357
358 token = decode_token (&ptr);
359 if (token)
360 g_ptr_array_add (challenge->realms, token);
361
362 if (*ptr == ',')
363 ptr++;
364 }
365 g_free (param->value);
366 g_free (param->name);
367 g_free (param);
368 break;
369 case DIGEST_NONCE:
370 g_free (challenge->nonce);
371 challenge->nonce = param->value;
372 g_free (param->name);
373 g_free (param);
374 break;
375 case DIGEST_QOP:
376 for (ptr = param->value; ptr && *ptr; ) {
377 gchar *token;
378
379 token = decode_token (&ptr);
380 if (token)
381 challenge->qop |= decode_qop (token);
382
383 if (*ptr == ',')
384 ptr++;
385 }
386
387 if (challenge->qop & QOP_INVALID)
388 challenge->qop = QOP_INVALID;
389 g_free (param->value);
390 g_free (param->name);
391 g_free (param);
392 break;
393 case DIGEST_STALE:
394 PARANOID (digest_abort (&got_stale, abort));
395 if (!g_ascii_strcasecmp (param->value, "true"))
396 challenge->stale = TRUE;
397 else
398 challenge->stale = FALSE;
399 g_free (param->value);
400 g_free (param->name);
401 g_free (param);
402 break;
403 case DIGEST_MAXBUF:
404 PARANOID (digest_abort (&got_maxbuf, abort));
405 challenge->maxbuf = atoi (param->value);
406 g_free (param->value);
407 g_free (param->name);
408 g_free (param);
409 break;
410 case DIGEST_CHARSET:
411 PARANOID (digest_abort (&got_charset, abort));
412 g_free (challenge->charset);
413 if (param->value && *param->value)
414 challenge->charset = param->value;
415 else
416 challenge->charset = NULL;
417 g_free (param->name);
418 g_free (param);
419 break;
420 case DIGEST_ALGORITHM:
421 PARANOID (digest_abort (&got_algorithm, abort));
422 g_free (challenge->algorithm);
423 challenge->algorithm = param->value;
424 g_free (param->name);
425 g_free (param);
426 break;
427 case DIGEST_CIPHER:
428 for (ptr = param->value; ptr && *ptr; ) {
429 gchar *token;
430
431 token = decode_token (&ptr);
432 if (token)
433 challenge->cipher |= decode_cipher (token);
434
435 if (*ptr == ',')
436 ptr++;
437 }
438 if (challenge->cipher & CIPHER_INVALID)
439 challenge->cipher = CIPHER_INVALID;
440 g_free (param->value);
441 g_free (param->name);
442 g_free (param);
443 break;
444 default:
445 challenge->params = g_list_prepend (challenge->params, param);
446 break;
447 }
448 }
449
450 g_list_free (params);
451
452 return challenge;
453 }
454
455 static gchar *
digest_uri_to_string(struct _DigestURI * uri)456 digest_uri_to_string (struct _DigestURI *uri)
457 {
458 if (uri->name)
459 return g_strdup_printf ("%s/%s/%s", uri->type, uri->host, uri->name);
460 else
461 return g_strdup_printf ("%s/%s", uri->type, uri->host);
462 }
463
464 static void
compute_response(struct _DigestResponse * resp,const gchar * passwd,gboolean client,guchar out[33])465 compute_response (struct _DigestResponse *resp,
466 const gchar *passwd,
467 gboolean client,
468 guchar out[33])
469 {
470 GString *buffer;
471 GChecksum *checksum;
472 guint8 *digest;
473 gsize length;
474 gchar *hex_a1;
475 gchar *hex_a2;
476 gchar *hex_kd;
477 gchar *uri;
478
479 buffer = g_string_sized_new (256);
480 length = g_checksum_type_get_length (G_CHECKSUM_MD5);
481 digest = g_alloca (length);
482
483 /* Compute A1. */
484
485 g_string_append (buffer, resp->username);
486 g_string_append_c (buffer, ':');
487 g_string_append (buffer, resp->realm);
488 g_string_append_c (buffer, ':');
489 g_string_append (buffer, passwd);
490
491 checksum = g_checksum_new (G_CHECKSUM_MD5);
492 g_checksum_update (
493 checksum, (const guchar *) buffer->str, buffer->len);
494 g_checksum_get_digest (checksum, digest, &length);
495 g_checksum_free (checksum);
496
497 /* Clear the buffer. */
498 g_string_truncate (buffer, 0);
499
500 g_string_append_len (buffer, (gchar *) digest, length);
501 g_string_append_c (buffer, ':');
502 g_string_append (buffer, resp->nonce);
503 g_string_append_c (buffer, ':');
504 g_string_append (buffer, resp->cnonce);
505 if (resp->authzid != NULL) {
506 g_string_append_c (buffer, ':');
507 g_string_append (buffer, resp->authzid);
508 }
509
510 hex_a1 = g_compute_checksum_for_string (
511 G_CHECKSUM_MD5, buffer->str, buffer->len);
512
513 /* Clear the buffer. */
514 g_string_truncate (buffer, 0);
515
516 /* Compute A2. */
517
518 if (client) {
519 /* We are calculating the client response. */
520 g_string_append (buffer, "AUTHENTICATE:");
521 } else {
522 /* We are calculating the server rspauth. */
523 g_string_append_c (buffer, ':');
524 }
525
526 uri = digest_uri_to_string (resp->uri);
527 g_string_append (buffer, uri);
528 g_free (uri);
529
530 if (resp->qop == QOP_AUTH_INT || resp->qop == QOP_AUTH_CONF)
531 g_string_append (buffer, ":00000000000000000000000000000000");
532
533 hex_a2 = g_compute_checksum_for_string (
534 G_CHECKSUM_MD5, buffer->str, buffer->len);
535
536 /* Clear the buffer. */
537 g_string_truncate (buffer, 0);
538
539 /* Compute KD. */
540
541 g_string_append (buffer, hex_a1);
542 g_string_append_c (buffer, ':');
543 g_string_append (buffer, resp->nonce);
544 g_string_append_c (buffer, ':');
545 g_string_append_len (buffer, resp->nc, 8);
546 g_string_append_c (buffer, ':');
547 g_string_append (buffer, resp->cnonce);
548 g_string_append_c (buffer, ':');
549 g_string_append (buffer, qop_to_string (resp->qop));
550 g_string_append_c (buffer, ':');
551 g_string_append (buffer, hex_a2);
552
553 hex_kd = g_compute_checksum_for_string (
554 G_CHECKSUM_MD5, buffer->str, buffer->len);
555
556 g_strlcpy ((gchar *) out, hex_kd, 33);
557
558 g_free (hex_a1);
559 g_free (hex_a2);
560 g_free (hex_kd);
561
562 g_string_free (buffer, TRUE);
563 }
564
565 static struct _DigestResponse *
generate_response(struct _DigestChallenge * challenge,const gchar * host,const gchar * protocol,const gchar * user,const gchar * passwd)566 generate_response (struct _DigestChallenge *challenge,
567 const gchar *host,
568 const gchar *protocol,
569 const gchar *user,
570 const gchar *passwd)
571 {
572 struct _DigestResponse *resp;
573 struct _DigestURI *uri;
574 GChecksum *checksum;
575 guint8 *digest;
576 gsize length;
577 gchar *bgen;
578
579 length = g_checksum_type_get_length (G_CHECKSUM_MD5);
580 digest = g_alloca (length);
581
582 resp = g_new0 (struct _DigestResponse, 1);
583 resp->username = g_strdup (user);
584 /* FIXME: we should use the preferred realm */
585 if (challenge->realms && challenge->realms->len > 0)
586 resp->realm = g_strdup (challenge->realms->pdata[0]);
587 else
588 resp->realm = g_strdup ("");
589
590 resp->nonce = g_strdup (challenge->nonce);
591
592 /* generate the cnonce */
593 bgen = g_strdup_printf (
594 "%p:%lu:%lu",
595 (gpointer) resp,
596 (gulong) getpid (),
597 (gulong) time (NULL));
598 checksum = g_checksum_new (G_CHECKSUM_MD5);
599 g_checksum_update (checksum, (guchar *) bgen, -1);
600 g_checksum_get_digest (checksum, digest, &length);
601 g_checksum_free (checksum);
602 g_free (bgen);
603
604 /* take our recommended 64 bits of entropy */
605 resp->cnonce = g_base64_encode ((guchar *) digest, 8);
606
607 /* we don't support re-auth so the nonce count is always 1 */
608 g_strlcpy (resp->nc, "00000001", sizeof (resp->nc));
609
610 /* choose the QOP */
611 /* FIXME: choose - probably choose "auth" ??? */
612 resp->qop = QOP_AUTH;
613
614 /* create the URI */
615 uri = g_new0 (struct _DigestURI, 1);
616 uri->type = g_strdup (protocol);
617 uri->host = g_strdup (host);
618 uri->name = NULL;
619 resp->uri = uri;
620
621 /* charsets... yay */
622 if (challenge->charset) {
623 /* I believe that this is only ever allowed to be
624 * UTF-8. We strdup the charset specified by the
625 * challenge anyway, just in case it's not UTF-8.
626 */
627 resp->charset = g_strdup (challenge->charset);
628 }
629
630 resp->cipher = CIPHER_INVALID;
631 if (resp->qop == QOP_AUTH_CONF) {
632 /* FIXME: choose a cipher? */
633 resp->cipher = CIPHER_INVALID;
634 }
635
636 /* we don't really care about this... */
637 resp->authzid = NULL;
638
639 compute_response (resp, passwd, TRUE, (guchar *) resp->resp);
640
641 return resp;
642 }
643
644 static GByteArray *
digest_response(struct _DigestResponse * resp)645 digest_response (struct _DigestResponse *resp)
646 {
647 GByteArray *buffer;
648 const gchar *str;
649 gchar *buf;
650
651 buffer = g_byte_array_new ();
652 g_byte_array_append (buffer, (guint8 *) "username=\"", 10);
653 if (resp->charset) {
654 /* Encode the username using the requested charset */
655 gchar *username, *outbuf;
656 const gchar *charset;
657 gsize len, outlen;
658 const gchar *inbuf;
659 GIConv cd;
660
661 charset = camel_iconv_locale_charset ();
662 if (!charset)
663 charset = "iso-8859-1";
664
665 cd = camel_iconv_open (resp->charset, charset);
666
667 len = strlen (resp->username);
668 outlen = 2 * len; /* plenty of space */
669
670 outbuf = username = g_malloc0 (outlen + 1);
671 inbuf = resp->username;
672 if (cd == (GIConv) -1 || camel_iconv (cd, &inbuf, &len, &outbuf, &outlen) == (gsize) -1) {
673 /* We can't convert to UTF-8 - pretend we never got a charset param? */
674 g_free (resp->charset);
675 resp->charset = NULL;
676
677 /* Set the username to the non-UTF-8 version */
678 g_free (username);
679 username = g_strdup (resp->username);
680 }
681
682 if (cd != (GIConv) -1)
683 camel_iconv_close (cd);
684
685 g_byte_array_append (buffer, (guint8 *) username, strlen (username));
686 g_free (username);
687 } else {
688 g_byte_array_append (buffer, (guint8 *) resp->username, strlen (resp->username));
689 }
690
691 g_byte_array_append (buffer, (guint8 *) "\",realm=\"", 9);
692 g_byte_array_append (buffer, (guint8 *) resp->realm, strlen (resp->realm));
693
694 g_byte_array_append (buffer, (guint8 *) "\",nonce=\"", 9);
695 g_byte_array_append (buffer, (guint8 *) resp->nonce, strlen (resp->nonce));
696
697 g_byte_array_append (buffer, (guint8 *) "\",cnonce=\"", 10);
698 g_byte_array_append (buffer, (guint8 *) resp->cnonce, strlen (resp->cnonce));
699
700 g_byte_array_append (buffer, (guint8 *) "\",nc=", 5);
701 g_byte_array_append (buffer, (guint8 *) resp->nc, 8);
702
703 g_byte_array_append (buffer, (guint8 *) ",qop=", 5);
704 str = qop_to_string (resp->qop);
705 g_byte_array_append (buffer, (guint8 *) str, strlen (str));
706
707 g_byte_array_append (buffer, (guint8 *) ",digest-uri=\"", 13);
708 buf = digest_uri_to_string (resp->uri);
709 g_byte_array_append (buffer, (guint8 *) buf, strlen (buf));
710 g_free (buf);
711
712 g_byte_array_append (buffer, (guint8 *) "\",response=", 11);
713 g_byte_array_append (buffer, (guint8 *) resp->resp, 32);
714
715 if (resp->maxbuf > 0) {
716 g_byte_array_append (buffer, (guint8 *) ",maxbuf=", 8);
717 buf = g_strdup_printf ("%u", resp->maxbuf);
718 g_byte_array_append (buffer, (guint8 *) buf, strlen (buf));
719 g_free (buf);
720 }
721
722 if (resp->charset) {
723 g_byte_array_append (buffer, (guint8 *) ",charset=", 9);
724 g_byte_array_append (buffer, (guint8 *) resp->charset, strlen ((gchar *) resp->charset));
725 }
726
727 if (resp->cipher != CIPHER_INVALID) {
728 str = cipher_to_string (resp->cipher);
729 if (str) {
730 g_byte_array_append (buffer, (guint8 *) ",cipher=\"", 9);
731 g_byte_array_append (buffer, (guint8 *) str, strlen (str));
732 g_byte_array_append (buffer, (guint8 *) "\"", 1);
733 }
734 }
735
736 if (resp->authzid) {
737 g_byte_array_append (buffer, (guint8 *) ",authzid=\"", 10);
738 g_byte_array_append (buffer, (guint8 *) resp->authzid, strlen (resp->authzid));
739 g_byte_array_append (buffer, (guint8 *) "\"", 1);
740 }
741
742 return buffer;
743 }
744
745 static void
sasl_digest_md5_finalize(GObject * object)746 sasl_digest_md5_finalize (GObject *object)
747 {
748 CamelSaslDigestMd5 *sasl = CAMEL_SASL_DIGEST_MD5 (object);
749 struct _DigestChallenge *c = sasl->priv->challenge;
750 struct _DigestResponse *r = sasl->priv->response;
751 GList *p;
752 gint i;
753
754 if (c != NULL) {
755 for (i = 0; i < c->realms->len; i++)
756 g_free (c->realms->pdata[i]);
757 g_ptr_array_free (c->realms, TRUE);
758
759 g_free (c->nonce);
760 g_free (c->charset);
761 g_free (c->algorithm);
762 for (p = c->params; p; p = p->next) {
763 struct _param *param = p->data;
764
765 g_free (param->name);
766 g_free (param->value);
767 g_free (param);
768 }
769 g_list_free (c->params);
770 g_free (c);
771 }
772
773 if (r != NULL) {
774 g_free (r->username);
775 g_free (r->realm);
776 g_free (r->nonce);
777 g_free (r->cnonce);
778 if (r->uri) {
779 g_free (r->uri->type);
780 g_free (r->uri->host);
781 g_free (r->uri->name);
782 }
783 g_free (r->charset);
784 g_free (r->authzid);
785 g_free (r->param);
786 g_free (r);
787 }
788
789 /* Chain up to parent's finalize() method. */
790 G_OBJECT_CLASS (camel_sasl_digest_md5_parent_class)->finalize (object);
791 }
792
793 static GByteArray *
sasl_digest_md5_challenge_sync(CamelSasl * sasl,GByteArray * token,GCancellable * cancellable,GError ** error)794 sasl_digest_md5_challenge_sync (CamelSasl *sasl,
795 GByteArray *token,
796 GCancellable *cancellable,
797 GError **error)
798 {
799 CamelSaslDigestMd5 *sasl_digest = CAMEL_SASL_DIGEST_MD5 (sasl);
800 struct _CamelSaslDigestMd5Private *priv = sasl_digest->priv;
801 CamelNetworkSettings *network_settings;
802 CamelSettings *settings;
803 CamelService *service;
804 struct _param *rspauth;
805 GByteArray *ret = NULL;
806 gboolean abort = FALSE;
807 const gchar *ptr;
808 guchar out[33];
809 gchar *tokens;
810 struct addrinfo *ai, hints;
811 const gchar *service_name;
812 const gchar *password;
813 gchar *host;
814 gchar *user;
815
816 /* Need to wait for the server */
817 if (!token)
818 return NULL;
819
820 service = camel_sasl_get_service (sasl);
821 service_name = camel_sasl_get_service_name (sasl);
822
823 settings = camel_service_ref_settings (service);
824 g_return_val_if_fail (CAMEL_IS_NETWORK_SETTINGS (settings), NULL);
825
826 network_settings = CAMEL_NETWORK_SETTINGS (settings);
827 host = camel_network_settings_dup_host (network_settings);
828 user = camel_network_settings_dup_user (network_settings);
829
830 g_object_unref (settings);
831
832 g_return_val_if_fail (user != NULL, NULL);
833
834 if (!host || !*host) {
835 g_free (host);
836 host = g_strdup ("localhost");
837 }
838
839 password = camel_service_get_password (service);
840 g_return_val_if_fail (password != NULL, NULL);
841
842 switch (priv->state) {
843 case STATE_AUTH:
844 if (token->len > 2048) {
845 g_set_error (
846 error, CAMEL_SERVICE_ERROR,
847 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
848 _("Server challenge too long (>2048 octets)"));
849 goto exit;
850 }
851
852 tokens = g_strndup ((gchar *) token->data, token->len);
853 priv->challenge = parse_server_challenge (tokens, &abort);
854 g_free (tokens);
855 if (!priv->challenge || abort) {
856 g_set_error (
857 error, CAMEL_SERVICE_ERROR,
858 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
859 _("Server challenge invalid\n"));
860 goto exit;
861 }
862
863 if (priv->challenge->qop == QOP_INVALID) {
864 g_set_error (
865 error, CAMEL_SERVICE_ERROR,
866 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
867 _("Server challenge contained invalid "
868 "“Quality of Protection” token"));
869 goto exit;
870 }
871
872 memset (&hints, 0, sizeof (hints));
873 hints.ai_flags = AI_CANONNAME;
874 ai = camel_getaddrinfo (
875 host, NULL, &hints, cancellable, NULL);
876 if (ai && ai->ai_canonname)
877 ptr = ai->ai_canonname;
878 else
879 ptr = "localhost.localdomain";
880
881 priv->response = generate_response (
882 priv->challenge, ptr, service_name,
883 user, password);
884 if (ai)
885 camel_freeaddrinfo (ai);
886 ret = digest_response (priv->response);
887
888 break;
889 case STATE_FINAL:
890 if (token->len)
891 tokens = g_strndup ((gchar *) token->data, token->len);
892 else
893 tokens = NULL;
894
895 if (!tokens || !*tokens) {
896 g_free (tokens);
897 g_set_error (
898 error, CAMEL_SERVICE_ERROR,
899 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
900 _("Server response did not contain "
901 "authorization data"));
902 goto exit;
903 }
904
905 rspauth = g_new0 (struct _param, 1);
906
907 ptr = tokens;
908 rspauth->name = decode_token (&ptr);
909 if (*ptr == '=') {
910 ptr++;
911 rspauth->value = decode_value (&ptr);
912 }
913 g_free (tokens);
914
915 if (!rspauth->value) {
916 g_free (rspauth->name);
917 g_free (rspauth);
918 g_set_error (
919 error, CAMEL_SERVICE_ERROR,
920 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
921 _("Server response contained incomplete "
922 "authorization data"));
923 goto exit;
924 }
925
926 compute_response (priv->response, password, FALSE, out);
927 if (memcmp (out, rspauth->value, 32) != 0) {
928 g_free (rspauth->name);
929 g_free (rspauth->value);
930 g_free (rspauth);
931 g_set_error (
932 error, CAMEL_SERVICE_ERROR,
933 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
934 _("Server response does not match"));
935 camel_sasl_set_authenticated (sasl, TRUE);
936 goto exit;
937 }
938
939 g_free (rspauth->name);
940 g_free (rspauth->value);
941 g_free (rspauth);
942
943 ret = g_byte_array_new ();
944
945 camel_sasl_set_authenticated (sasl, TRUE);
946 default:
947 break;
948 }
949
950 priv->state++;
951
952 exit:
953 g_free (host);
954 g_free (user);
955
956 return ret;
957 }
958
959 static void
camel_sasl_digest_md5_class_init(CamelSaslDigestMd5Class * class)960 camel_sasl_digest_md5_class_init (CamelSaslDigestMd5Class *class)
961 {
962 GObjectClass *object_class;
963 CamelSaslClass *sasl_class;
964
965 object_class = G_OBJECT_CLASS (class);
966 object_class->finalize = sasl_digest_md5_finalize;
967
968 sasl_class = CAMEL_SASL_CLASS (class);
969 sasl_class->auth_type = &sasl_digest_md5_auth_type;
970 sasl_class->challenge_sync = sasl_digest_md5_challenge_sync;
971 }
972
973 static void
camel_sasl_digest_md5_init(CamelSaslDigestMd5 * sasl)974 camel_sasl_digest_md5_init (CamelSaslDigestMd5 *sasl)
975 {
976 sasl->priv = camel_sasl_digest_md5_get_instance_private (sasl);
977 }
978