aboutsummaryrefslogtreecommitdiffstats
path: root/fetchmail-SA-2005-03.txt
blob: 43468f5de7e4142dec3afa0a52e8894f2bb72336 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

fetchmail-SA-2005-03: security announcement

Topics:		#1 crash retrieving headerless message in multidrop mode
		#2 fetchmail 6.2.5.X end of life

Author:		Matthias Andree
Version:	1.00
Announced:	2005-12-19
Type:		null pointer dereference
Impact:		fetchmail crashes
Danger:		low
Credits:	Daniel Drake, Gentoo (bug report)
		Sunil Shetye (bug fix)
CVE Name:	CVE-2005-4348
URL:		http://fetchmail.berlios.de/fetchmail-SA-2005-03.txt
		http://article.gmane.org/gmane.mail.fetchmail.user/7573
		http://bugs.debian.org/343836
Project URL:	http://fetchmail.berlios.de/

Affects:	fetchmail version 6.2.5.4
		fetchmail version 6.3.0

Not affected:	fetchmail 6.3.1
		fetchmail 6.2.5.5
		other versions not mentioned here or in the previous
		sections have not been checked

Corrected:	2005-12-19 - released fetchmail 6.3.1
		2005-12-18 - released fetchmail 6.3.1-rc1
		2005-12-19 - released fetchmail 6.2.5.5


0. Release history
==================

2005-12-19	1.00 - initial version


1. Background
=============

fetchmail is a software package to retrieve mail from remote POP2, POP3,
IMAP, ETRN or ODMR servers and forward it to local SMTP, LMTP servers or
message delivery agents.

fetchmail ships with a graphical, Python/Tkinter based configuration
utility named "fetchmailconf" to help the user create configuration (run
control) files for fetchmail.


2. Problem description and Impact
=================================

Fetchmail contains a bug that causes an application crash when fetchmail
is configured for multidrop mode and the upstream mail server sends a
message without headers.  As fetchmail does not record this message as
"previously fetched", it will crash with the same message if it is
re-executed, so it cannot make progress. A malicious or broken-into
upstream server could thus cause a denial of service in fetchmail
clients.

Note that such messages are not RFC-822 conformant, so if the server has
not been tampered with, the server software is faulty.


3. Workaround
=============

Where possible, singledrop mode may be an alternative.

For sites, where multidrop mode is required, no workaround is known.


4. Solution
===========

Download and install fetchmail 6.3.1 or a newer stable release from
fetchmail's project site at
<http://developer.berlios.de/project/showfiles.php?group_id=1824>.

The fix has also been backported to the 6.2.5.5 legacy release which is
available from the same site.

Note however that 6.3.X has very few incompatible changes since 6.2.5.X
so 6.3.X should be viable for most sites.  It is therefore recommended
that every user and distributor upgrade to 6.3.1 or newer.


5. End of life announcement
===========================

The fetchmail 6.2.5.X branch will be discontinued early in 2006.

The new 6.3.X stable branch has been available since 2005-11-30
and will not change except for bugfixes, documentation and translations.


A. Copyright, License and Warranty
==================================

(C) Copyright 2005 by Matthias Andree, <matthias.andree@gmx.de>.
Some rights reserved.

This work is licensed under the Creative Commons
Attribution-NonCommercial-NoDerivs German License. To view a copy of
this license, visit http://creativecommons.org/licenses/by-nc-nd/2.0/de/
or send a letter to Creative Commons; 559 Nathan Abbott Way;
Stanford, California 94305; USA.

THIS WORK IS PROVIDED FREE OF CHARGE AND WITHOUT ANY WARRANTIES.
Use the information herein at your own risk.

END OF fetchmail-SA-2005-03.txt
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (GNU/Linux)

iD8DBQFIV7WXvmGDOQUufZURAjqeAJ90wOleuLWpPKGLdPyLHeDqjxXBrQCgktVz
5rKRtG/LwqXUiqNxjHALy7k=
=NBXT
-----END PGP SIGNATURE-----
href='#n405'>405 406 407 408 409 410 411 412 413 414 415 416 417
/*
 * netrc.c -- parse the .netrc file to get hosts, accounts, and passwords
 *
   Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
   Copyright assigned to Eric S. Raymond, October 2001.

   For license terms, see the file COPYING in this directory.

   Compile with -DSTANDALONE to test this module.
   (Makefile.am should have a rule so you can just type "make netrc")
*/

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>

#include "config.h"
#include "fetchmail.h"
#include "netrc.h"
#include "i18n.h"

#ifdef STANDALONE
/* Normally defined in xstrdup.c. */
# define xstrdup strdup

/* Normally defined in xmalloc.c */
# define xmalloc malloc
# define xrealloc realloc

char *program_name = "netrc";
#endif

/* Maybe add NEWENTRY to the account information list, LIST.  NEWENTRY is
   set to a ready-to-use netrc_entry, in any event. */
static void
maybe_add_to_list (netrc_entry **newentry, netrc_entry **list)
{
    netrc_entry *a, *l;
    a = *newentry;
    l = *list;

    /* We need a login name in order to add the entry to the list. */
    if (a && ! a->login)
    {
	/* Free any allocated space. */
	if (a->host)
	    free (a->host);
	if (a->password)
	    free (a->password);
    }
    else
    {
	if (a)
	{
	    /* Add the current machine into our list. */
	    a->next = l;
	    l = a;
	}

	/* Allocate a new netrc_entry structure. */
	a = (netrc_entry *) xmalloc (sizeof (netrc_entry));
    }

    /* Zero the structure, so that it is ready to use. */
    memset (a, 0, sizeof(*a));

    /* Return the new pointers. */
    *newentry = a;
    *list = l;
    return;
}


/* Parse FILE as a .netrc file (as described in ftp(1)), and return a
   list of entries.  NULL is returned if the file could not be
   parsed. */
netrc_entry *
parse_netrc (char *file)
{
    FILE *fp;
    char buf[POPBUFSIZE+1], *p, *tok;
    const char *premature_token;
    netrc_entry *current, *retval;
    int ln;

    /* The latest token we've seen in the file. */
    enum
    {
	tok_nothing, tok_account, tok_login, tok_macdef, tok_machine, tok_password
    } last_token = tok_nothing;

    current = retval = NULL;

    fp = fopen (file, "r");
    if (!fp)
    {
	/* Just return NULL if we can't open the file. */
	return NULL;
    }

    /* Initialize the file data. */
    ln = 0;
    premature_token = NULL;

    /* While there are lines in the file... */
    while (fgets(buf, sizeof(buf) - 1, fp))
    {
	ln++;

	/* Strip trailing CRLF */
	for (p = buf + strlen(buf) - 1; (p >= buf) && isspace((unsigned char)*p); p--)
	    *p = '\0';

	/* Parse the line. */
	p = buf;

	/* If the line is empty... */
	if (!*p)
	{
	    if (last_token == tok_macdef)	/* end of macro */
		last_token = tok_nothing;
	    else
		continue;			/* otherwise ignore it */
	}

	/* If we are defining macros, then skip parsing the line. */
	while (*p && last_token != tok_macdef)
	{
	    char quote_char = 0;
	    char *pp;

	    /* Skip any whitespace. */
	    while (*p && isspace ((unsigned char)*p))
		p++;

	    /* Discard end-of-line comments. */
	    if (*p == '#')
		break;

	    tok = pp = p;

	    /* Find the end of the token. */
	    while (*p && (quote_char || !isspace ((unsigned char)*p)))
	    {
		if (quote_char)
		{
		    if (quote_char == *p)
		    {
			quote_char = 0;
			p ++;
		    }
		    else
		    {
			*pp = *p;
			p ++;
			pp ++;
		    }
		}
		else
		{
		    if (*p == '"' || *p == '\'')
			quote_char = *p;
		    else
		    {
			*pp = *p;
			pp ++;
		    }
		    p ++;
		}
	    }
	    /* Null-terminate the token, if it isn't already. */
	    if (*p)
		*p ++ = '\0';
	    *pp = 0;

	    switch (last_token)
	    {
	    case tok_login:
		if (current)
		    current->login = (char *) xstrdup (tok);
		else
		    premature_token = "login";
		break;

	    case tok_machine:
		/* Start a new machine entry. */
		maybe_add_to_list (&current, &retval);
		current->host = (char *) xstrdup (tok);
		break;

	    case tok_password:
		if (current)
		    current->password = (char *) xstrdup (tok);
		else
		    premature_token = "password";
		break;

		/* We handle most of tok_macdef above. */
	    case tok_macdef:
		if (!current)
		    premature_token = "macdef";
		break;

		/* We don't handle the account keyword at all. */
	    case tok_account:
		if (!current)
		    premature_token = "account";
		break;

		/* We handle tok_nothing below this switch. */
	    case tok_nothing:
		break;
	    }

	    if (premature_token)
	    {
		fprintf (stderr,
			 GT_("%s:%d: warning: found \"%s\" before any host names\n"),
			 file, ln, premature_token);
		premature_token = NULL;
	    }

	    if (last_token != tok_nothing)
		/* We got a value, so reset the token state. */
		last_token = tok_nothing;
	    else
	    {
		/* Fetch the next token. */
		if (!strcmp (tok, "default"))
		{
		    maybe_add_to_list (&current, &retval);
		}
		else if (!strcmp (tok, "login"))
		    last_token = tok_login;

		else if (!strcmp (tok, "user"))
		    last_token = tok_login;

		else if (!strcmp (tok, "macdef"))
		    last_token = tok_macdef;

		else if (!strcmp (tok, "machine"))
		    last_token = tok_machine;

		else if (!strcmp (tok, "password"))
		    last_token = tok_password;

		else if (!strcmp (tok, "passwd"))
		    last_token = tok_password;

		else if (!strcmp (tok, "account"))
		    last_token = tok_account;

		else
		{
		    fprintf (stderr, GT_("%s:%d: warning: unknown token \"%s\"\n"),
			     file, ln, tok);
		}
	    }
	}
    }

    fclose (fp);

    /* Finalize the last machine entry we found. */
    maybe_add_to_list (&current, &retval);
    free (current);

    /* Reverse the order of the list so that it appears in file order. */
    current = retval;
    retval = NULL;
    while (current)
    {
	netrc_entry *saved_reference;

	/* Change the direction of the pointers. */
	saved_reference = current->next;
	current->next = retval;

	/* Advance to the next node. */
	retval = current;
	current = saved_reference;
    }

    return retval;
}


/* Return the netrc entry from LIST corresponding to HOST.  NULL is
   returned if no such entry exists. */
netrc_entry *
search_netrc (netrc_entry *list, char *host, char *login)
{
    /* Look for the HOST in LIST. */
    while (list)
    {
	if (list->host && !strcmp(list->host, host))
	    if (!list->login || !strcmp(list->login, login))
		/* We found a matching entry. */
		break;

	list = list->next;
    }

    /* Return the matching entry, or NULL. */
    return list;
}

void
free_netrc(netrc_entry *a) {
    while(a) {
	netrc_entry *n = a->next;
	if (a->password != NULL) {
		memset(a->password, 0x55, strlen(a->password));
		free(a->password);
	}
	xfree(a->login);
	xfree(a->host);
	xfree(a);
	a = n;
    }
}

#ifdef STANDALONE
#include <sys/types.h>
#include <sys/stat.h>

#include <errno.h>

int main (int argc, char **argv)
{
    struct stat sb;
    char *program_name, *file, *host, *login;
    netrc_entry *head, *a;

    program_name = argv[0];
    file = argv[1];
    host = argv[2];
    login = argv[3];

    switch (argc) {
	case 2:
	case 4:
	    break;
	default:
	    fprintf (stderr, "Usage: %s <file> [<host> <login>]\n", argv[0]);
	    exit(EXIT_FAILURE);
    }

    if (stat (file, &sb))
    {
	fprintf (stderr, "%s: cannot stat %s: %s\n", argv[0], file,
		 strerror (errno));
	exit (1);
    }

    head = parse_netrc (file);
    if (!head)
    {
	fprintf (stderr, "%s: no entries found in %s\n", argv[0], file);
	exit (1);
    }

    if (host && login)
    {
	int status;
	status = EXIT_SUCCESS;

	printf("Host: %s, Login: %s\n", host, login);
	    
	a = search_netrc (head, host, login);
	if (a)
	{
	    /* Print out the password (if any). */
	    if (a->password)
	    {
		printf("Password: %s\n", a->password);
	    }
	} else
	    status = EXIT_FAILURE;
	fputc ('\n', stdout);

	exit (status);
    }

    /* Print out the entire contents of the netrc. */
    a = head;
    while (a)
    {
	/* Print the host name. */
	if (a->host)
	    fputs (a->host, stdout);
	else
	    fputs ("DEFAULT", stdout);

	fputc (' ', stdout);

	/* Print the login name. */
	fputs (a->login, stdout);

	if (a->password)
	{
	    /* Print the password, if there is any. */
	    fputc (' ', stdout);
	    fputs (a->password, stdout);
	}

	fputc ('\n', stdout);
	a = a->next;
    }

    free_netrc(head);

    exit (0);
}
#endif /* STANDALONE */