aboutsummaryrefslogtreecommitdiffstats
path: root/website/security.html
blob: d1edefa6db113eee1112f7a3627fb46a52d210e8 (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
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<link rel="stylesheet" href="sitestyle.css" type="text/css">
<meta name="description" content="The Fetchmail Project">
<meta name="keywords" content="fetchmail, pop3, imap, email, mail">
<meta name="MSSmartTagsPreventParsing" content="TRUE">
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Fetchmail</title>
</head>
<body>
<div id="Header">
<table width="100%" cellpadding="0" summary="Canned page header">
<tr>
<td>Fetchmail</td>
<td align="right"><!-- update date -->2010-05-06</td>
</tr>
</table>
</div>

<div id="Menu">
	<hr>
	<a href="index.html" title="Main">Main</a><br>
	<a href="fetchmail-features.html">Features</a><br>
	<a href="fetchmail-man.html">Manual</a><br>
	<a href="fetchmail-FAQ.html" title="Fetchmail FAQ">FAQ</a><br>
	<a href="fetchmail-FAQ.pdf" title="Fetchmail FAQ as PDF">FAQ (PDF)</a><br>
	<a href="design-notes.html">Design Notes</a><br>
	<a href="http://developer.berlios.de/project/showfiles.php?group_id=1824">Download</a><br>
	<a href="http://gitorious.org/fetchmail/fetchmail/">Development</a><br>
	<a href="http://developer.berlios.de/projects/fetchmail/">Project Page</a><br>
	<hr>
</div>

<div id="Content">

    <h1>Fetchmail Security Information</h1>
    <p>These security issues (listed immediately below) have become
    known to the fetchmail maintainer to the date mentioned above. Note
    that fetchmail 6.2.X and older are no longer supported and contain
    some of the problems mentioned below, even if they aren't mentioned
    in the security announcements:</p>
    <ul>
	<li><a name="cve-2010-1167"
	    href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2010-1167">CVE-2010-1167:</a>
	Fetchmail <a href="fetchmail-SA-2010-02.txt">could exhaust all
	    available memory and abort on certain computers (for
	    instance Linux) in multibyte locales (for instance UTF-8)
	    when dumping malformed headers in debug (-v -v) mode.</a>
	This bug was introduced long before 6.0.0 and has been fixed in
	release 6.3.17.</li>
	<li><a name="cve-2010-0562"
	    href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2010-0562">CVE-2010-0562:</a> Fetchmail <a href="fetchmail-SA-2010-01.txt">would overrun the heap when displaying X.509 TLS/SSL certificates with characters with high bit set in verbose mode on platforms where char is a signed type.</a> This bug was introduced in release 6.3.11 and has been fixed in release 6.3.14.</li>
	<li><a name="cve-2009-2666" href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-2666">CVE-2009-2666:</a> Fetchmail <a href="fetchmail-SA-2009-01.txt">was found to validate SSL/TLS X.509 certificates improperly and allow man-in-the-middle-attacks to go undetected.</a> This bug has been fixed in release 6.3.11. For previous versions, use the <a href="fetchmail-SA-2009-01.txt">patch contained in the security announcement.</a></li>
	<li><a name="cve-2008-2711" href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-2711">CVE-2008-2711:</a> Fetchmail can <a href="fetchmail-SA-2008-01.txt">crash in verbose mode when logging long message headers.</a> This bug has been fixed in release 6.3.9. For 6.3.8, use the <a href="fetchmail-SA-2008-01.txt">patch contained in the security announcement.</a></li>
	<li><a name="cve-2007-4565" href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-4565">CVE-2007-4565:</a> Fetchmail can <a href="fetchmail-SA-2007-02.txt">crash when the SMTP server refuses a warning message generated by fetchmail.</a> This bug was introduced in fetchmail 4.6.8 and has been fixed in release 6.3.9. For 6.3.8, use the <a href="fetchmail-SA-2007-02.txt">patch contained in this security announcement.</a></li>
	<li><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-1558">CVE-2007-1558:</a> Fetchmail's APOP client was found to <a href="fetchmail-SA-2007-01.txt">validate APOP challenges insufficiently, making man-in-the-middle attacks on APOP secrets unnecessarily easier than need be.</a> This bug was long-standing, fetchmail 6.3.8 and newer validate the APOP challenge more strictly.</li>
	<li><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-5974">CVE-2006-5974:</a> Fetchmail was found to <a href="fetchmail-SA-2006-03.txt">crash when refusing a message that was bound to be delivered by an MDA.</a> This bug was introduced into fetchmail 6.3.5 and fixed in 6.3.6.</li>
	<li><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-5867">CVE-2006-5867:</a> Fetchmail was found to <a href="fetchmail-SA-2006-02.txt">omit TLS or send the password in clear text despite the configuration stating otherwise.</a> This was a long-standing bug reported by Isaac Wilcox, fixed in fetchmail 6.3.6. There will be no 6.2.X releases to fix this bug in 6.2.X.</li>
	<li><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-0321">CVE-2006-0321:</a> Fetchmail was found to <a href="fetchmail-SA-2006-01.txt">crash after bouncing a message with bad addresses. This bug was introduced with fetchmail 6.3.0 and fixed in fetchmail 6.3.2.</a></li>
	<li><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2005-4348">CVE-2005-4348:</a> Fetchmail was found to contain <a href="fetchmail-SA-2005-03.txt">a bug (null pointer dereference) that can be exploited to a denial of service attack</a> when fetchmail runs in multidrop mode. 6.2.5.5 and 6.3.1 have this bug fixed.</li>
	<li><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2005-3088">CVE-2005-3088:</a> Fetchmailconf was found to <a href="fetchmail-SA-2005-02.txt">open the configuration files world-readable, writing data to them, and only then tightening up permissions</a>, which may cause password information to be visible to other users. This bug affected fetchmail 6.2.0, 6.2.5 and 6.2.5.2.  The bug is fixed in fetchmail 6.2.5.4 and 6.3.0.</li>
	<li><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2005-2335">CVE-2005-2335:</a> Fetchmail was found to contain a <a href="fetchmail-SA-2005-01.txt">remotely exploitable code injection vulnerability (potentially privileged code)</a> in the POP3 code, affecting both the 6.2.0 and 6.2.5 releases. 6.2.5.2, 6.2.5.4 and 6.3.0 have got this bug fixed. (Other versions have not been checked if they contain this bug.)</li>
</ul>

<p style="font-size:100%"><strong>Please <a
	href="http://developer.berlios.de/project/showfiles.php?group_id=1824">update
	to the newest fetchmail version</a>.</strong></p>
</div>
</body>
</html>
n class="p">; errno = 0; /* * Croak if the uidl directory does not exist. * This probably means an NFS mount failed and we can't * see a uidl file that ought to be there. * Question: is this a portable check? It's not clear * that all implementations of lstat() will return ENOTDIR * rather than plain ENOENT in this case... */ if (lstat(idfile, &statbuf) < 0) { if (errno == ENOTDIR) { report(stderr, GT_("lstat: %s: %s\n"), idfile, strerror(errno)); exit(PS_IOERR); } } /* let's get stored message UIDs from previous queries */ if ((tmpfp = fopen(idfile, "r")) != (FILE *)NULL) { char buf[POPBUFSIZE+1]; char *host = NULL; /* pacify -Wall */ char *user; char *id; char *atsign; /* temp pointer used in parsing user and host */ char *delimp1; char saveddelim1; char *delimp2; char saveddelim2 = '\0'; /* pacify -Wall */ while (fgets(buf, POPBUFSIZE, tmpfp) != (char *)NULL) { /* * At this point, we assume the bug has two fields -- a user@host * part, and an ID part. Either field may contain spurious @ signs. * The previous version of this code presumed one could split at * the rightmost '@'. This is not correct, as InterMail puts an * '@' in the UIDL. */ /* first, skip leading spaces */ user = buf + strspn(buf, " \t"); /* * First, we split the buf into a userhost part and an id * part ... but id doesn't necessarily start with a '<', * espescially if the POP server returns an X-UIDL header * instead of a Message-ID, as GMX's (www.gmx.net) POP3 * StreamProxy V1.0 does. */ if ((id = strchr(user, ' ')) != NULL ) { /* * this is one other trick. The userhost part * may contain ' ' in the user part, at least in * the lotus notes case. * So we start looking for the '@' after which the * host will follow with the ' ' seperator finaly id. */ delimp1 = strchr(user, '@'); id = strchr(delimp1,' '); for (delimp1 = id; delimp1 >= user; delimp1--) if ((*delimp1 != ' ') && (*delimp1 != '\t')) break; /* * It should be safe to assume that id starts after * the " " - after all, we're writing the " " * ourselves in write_saved_lists() :-) */ id = id + strspn(id, " "); delimp1++; /* but what if there is only white space ?!? */ saveddelim1 = *delimp1; /* save char after token */ *delimp1 = '\0'; /* delimit token with \0 */ if (id != NULL) { /* now remove trailing white space chars from id */ if ((delimp2 = strpbrk(id, " \t\n")) != NULL ) { saveddelim2 = *delimp2; *delimp2 = '\0'; } atsign = strrchr(user, '@'); if (atsign) { *atsign = '\0'; host = atsign + 1; } for (ctl = hostlist; ctl; ctl = ctl->next) { if (strcasecmp(host, ctl->server.queryname) == 0 && strcasecmp(user, ctl->remotename) == 0) { save_str(&ctl->oldsaved, id, UID_SEEN); break; } } /* * If it's not in a host we're querying, * save it anyway. Otherwise we'd lose UIDL * information any time we queried an explicit * subset of hosts. */ if (ctl == (struct query *)NULL) { /* restore string */ *delimp1 = saveddelim1; *atsign = '@'; if (delimp2 != NULL) { *delimp2 = saveddelim2; } save_str(&scratchlist, buf, UID_SEEN); } } } } fclose(tmpfp); /* not checking should be safe, mode was "r" */ } if (outlevel >= O_DEBUG) { struct idlist *idp; int uidlcount = 0; for (ctl = hostlist; ctl; ctl = ctl->next) if (ctl->server.uidl) { report_build(stdout, GT_("Old UID list from %s:"), ctl->server.pollname); for (idp = ctl->oldsaved; idp; idp = idp->next) report_build(stdout, " %s", idp->id); if (!idp) report_build(stdout, GT_(" <empty>")); report_complete(stdout, "\n"); uidlcount++; } if (uidlcount) { report_build(stdout, GT_("Scratch list of UIDs:")); for (idp = scratchlist; idp; idp = idp->next) report_build(stdout, " %s", idp->id); if (!idp) report_build(stdout, GT_(" <empty>")); report_complete(stdout, "\n"); } } } #endif /* POP3_ENABLE */ struct idlist *save_str(struct idlist **idl, const char *str, flag status) /* save a number/UID pair on the given UID list */ { struct idlist **end; /* do it nonrecursively so the list is in the right order */ for (end = idl; *end; end = &(*end)->next) continue; *end = (struct idlist *)xmalloc(sizeof(struct idlist)); (*end)->val.status.mark = status; (*end)->id = str ? xstrdup(str) : (char *)NULL; (*end)->next = NULL; return(*end); } void free_str_list(struct idlist **idl) /* free the given UID list */ { if (*idl == (struct idlist *)NULL) return; free_str_list(&(*idl)->next); free ((*idl)->id); free(*idl); *idl = (struct idlist *)NULL; } void save_str_pair(struct idlist **idl, const char *str1, const char *str2) /* save an ID pair on the given list */ { struct idlist **end; /* do it nonrecursively so the list is in the right order */ for (end = idl; *end; end = &(*end)->next) continue; *end = (struct idlist *)xmalloc(sizeof(struct idlist)); (*end)->id = str1 ? xstrdup(str1) : (char *)NULL; if (str2) (*end)->val.id2 = xstrdup(str2); else (*end)->val.id2 = (char *)NULL; (*end)->next = (struct idlist *)NULL; } #ifdef __UNUSED__ void free_str_pair_list(struct idlist **idl) /* free the given ID pair list */ { if (*idl == (struct idlist *)NULL) return; free_idpair_list(&(*idl)->next); free ((*idl)->id); free ((*idl)->val.id2); free(*idl); *idl = (struct idlist *)NULL; } #endif int str_in_list(struct idlist **idl, const char *str, const flag caseblind) /* is a given ID in the given list? (comparison may be caseblind) */ { if (*idl == (struct idlist *)NULL || str == (char *) NULL) return(0); else if (!caseblind && strcmp(str, (*idl)->id) == 0) return(1); else if (caseblind && strcasecmp(str, (*idl)->id) == 0) return(1); else return(str_in_list(&(*idl)->next, str, caseblind)); } int str_nr_in_list( struct idlist **idl, const char *str ) /* return the position of str in idl */ { int nr; struct idlist *walk; if ( !str ) return -1; for( walk = *idl, nr = 0; walk; nr ++, walk = walk->next ) if( strcmp( str, walk->id) == 0 ) return nr; return -1; } int str_nr_last_in_list( struct idlist **idl, const char *str) /* return the last position of str in idl */ { int nr, ret = -1; struct idlist *walk; if ( !str ) return -1; for( walk = *idl, nr = 0; walk; nr ++, walk = walk->next ) if( strcmp( str, walk->id) == 0 ) ret = nr; return ret; } void str_set_mark( struct idlist **idl, const char *str, const flag val) /* update the mark on an of an id to given value */ { int nr; struct idlist *walk; if (!str) return; for(walk = *idl, nr = 0; walk; nr ++, walk = walk->next) if (strcmp(str, walk->id) == 0) walk->val.status.mark = val; } int count_list( struct idlist **idl) /* count the number of elements in the list */ { if( !*idl ) return 0; return 1 + count_list( &(*idl)->next ); } char *str_from_nr_list(struct idlist **idl, int number) /* return the number'th string in idl */ { if( !*idl || number < 0) return 0; if( number == 0 ) return (*idl)->id; return str_from_nr_list(&(*idl)->next, number-1); } char *str_find(struct idlist **idl, int number) /* return the id of the given number in the given list. */ { if (*idl == (struct idlist *) 0) return((char *) 0); else if (number == (*idl)->val.status.num) return((*idl)->id); else return(str_find(&(*idl)->next, number)); } char *idpair_find(struct idlist **idl, const char *id) /* return the id of the given id in the given list (caseblind comparison) */ { if (*idl == (struct idlist *) 0) return((char *) 0); else if (strcasecmp(id, (*idl)->id) == 0) return((*idl)->val.id2 ? (*idl)->val.id2 : (*idl)->id); else return(idpair_find(&(*idl)->next, id)); } int delete_str(struct idlist **idl, int num) /* delete given message from given list */ { struct idlist *idp; for (idp = *idl; idp; idp = idp->next) if (idp->val.status.num == num) { idp->val.status.mark = UID_DELETED; return(1); } return(0); } struct idlist *copy_str_list(struct idlist *idl) /* copy the given UID list */ { struct idlist *newnode ; if (idl == (struct idlist *)NULL) return(NULL); else { newnode = (struct idlist *)xmalloc(sizeof(struct idlist)); memcpy(newnode, idl, sizeof(struct idlist)); newnode->next = copy_str_list(idl->next); return(newnode); } } void append_str_list(struct idlist **idl, struct idlist **nidl) /* append nidl to idl (does not copy *) */ { if ((*nidl) == (struct idlist *)NULL || *nidl == *idl) return; else if ((*idl) == (struct idlist *)NULL) *idl = *nidl; else if ((*idl)->next == (struct idlist *)NULL) (*idl)->next = *nidl; else if ((*idl)->next != *nidl) append_str_list(&(*idl)->next, nidl); } #ifdef POP3_ENABLE void expunge_uids(struct query *ctl) /* assert that all UIDs marked deleted have actually been expunged */ { struct idlist *idl; for (idl = ctl->newsaved; idl; idl = idl->next) if (idl->val.status.mark == UID_DELETED) idl->val.status.mark = UID_EXPUNGED; } void uid_swap_lists(struct query *ctl) /* finish a query */ { /* debugging code */ if (ctl->server.uidl && outlevel >= O_DEBUG) { struct idlist *idp; report_build(stdout, GT_("New UID list from %s:"), ctl->server.pollname); for (idp = ctl->newsaved; idp; idp = idp->next) report_build(stdout, " %s = %d", idp->id, idp->val.status.mark); if (!idp) report_build(stdout, GT_(" <empty>")); report_complete(stdout, "\n"); } /* * Don't swap UID lists unless we've actually seen UIDLs. * This is necessary in order to keep UIDL information * from being heedlessly deleted later on. * * Older versions of fetchmail did * * free_str_list(&scratchlist); * * after swap. This was wrong; we need to preserve the UIDL information * from unqueried hosts. Unfortunately, not doing this means that * under some circumstances UIDLs can end up being stored forever -- * specifically, if a user description is removed from .fetchmailrc * with UIDLs from that account in .fetchids, there is no way for * them to ever get garbage-collected. */ if (ctl->newsaved) { /* old state of mailbox may now be irrelevant */ if (outlevel >= O_DEBUG) report(stdout, GT_("swapping UID lists\n")); free_str_list(&ctl->oldsaved); ctl->oldsaved = ctl->newsaved; ctl->newsaved = (struct idlist *) NULL; } else if (outlevel >= O_DEBUG) report(stdout, GT_("not swapping UID lists, no UIDs seen this query\n")); } void write_saved_lists(struct query *hostlist, const char *idfile) /* perform end-of-run write of seen-messages list */ { int idcount; FILE *tmpfp; struct query *ctl; struct idlist *idp; /* if all lists are empty, nuke the file */ idcount = 0; for (ctl = hostlist; ctl; ctl = ctl->next) { for (idp = ctl->oldsaved; idp; idp = idp->next) if (idp->val.status.mark == UID_SEEN || idp->val.status.mark == UID_DELETED) idcount++; } /* either nuke the file or write updated last-seen IDs */ if (!idcount && !scratchlist) { if (outlevel >= O_DEBUG) report(stdout, GT_("Deleting fetchids file.\n")); unlink(idfile); } else { if (outlevel >= O_DEBUG) report(stdout, GT_("Writing fetchids file.\n")); if ((tmpfp = fopen(idfile, "w")) != (FILE *)NULL) { for (ctl = hostlist; ctl; ctl = ctl->next) { for (idp = ctl->oldsaved; idp; idp = idp->next) if (idp->val.status.mark == UID_SEEN || idp->val.status.mark == UID_DELETED) fprintf(tmpfp, "%s@%s %s\n", ctl->remotename, ctl->server.queryname, idp->id); } for (idp = scratchlist; idp; idp = idp->next) fputs(idp->id, tmpfp); fclose(tmpfp); } } } #endif /* POP3_ENABLE */ /* uid.c ends here */