Subject: pkg/13529: mail/cyrus quota program -f option buggy, scrambles mail quotas
To: None <gnats-bugs@gnats.netbsd.org>
From: None <John.P.Darrow@wheaton.edu>
List: netbsd-bugs
Date: 07/21/2001 19:04:22
>Number:         13529
>Category:       pkg
>Synopsis:       mail/cyrus quota program -f option buggy, scrambles mail quotas
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    pkg-manager
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat Jul 21 17:01:00 PDT 2001
>Closed-Date:
>Last-Modified:
>Originator:     John Darrow
>Release:        NetBSD 1.5.1
>Organization:
Computing Services, Wheaton College, Wheaton, IL
>Environment:
System: NetBSD jdarrowpiii.wheaton.edu 1.5.1 NetBSD 1.5.1 (JDARROW) #0: Fri Jul 6 16:31:07 CDT 2001 jdarrow@michal.wheaton.edu:/var/src/sys/arch/i386/compile/JDARROW i386


>Description:
(This was discovered by me a year ago on a hand-installed cyrus-imapd
1.5.19, but while working on an upgrade, I've found that it's still
present in the 1.6.24 codebase, and appears (by inspection) to still
be there in 2.0.15, also...)

The cyrus quota program (which checks IMAP mail quotas on a cyrus-imapd
server) has a case-sensitivity bug.  It reads from the filesystem in
fs-default order (which on unix file-systems like ffs is case-sensitive)
but then does comparisons case-insensitively (using str{,n}casecmp).

As a result, if a cyrus system has any top level mailboxes starting with
capital letters which sort alphabetically after "User", running 'quota -f'
to rebuild the mailbox quotas (after e.g. a restore from backup of some
of a user's mail) will _remove_ all the user mailbox quota roots instead,
thinking that, since it's gotten past "U" without hitting the user
mailbox area, those quota roots aren't relevant...

>How-To-Repeat:
Restore some of a user's mail from backups.  Run reconstruct to
rebuild the user's mailbox indexes.  Run quota -f to fix their quota
usage, only to find it wiping out the quotas for half the system (before
you manage to catch and stop the process...)

>Fix:
Add the following patch (and rebuild distinfo).  The patch simply changes
all the str{,n}casecmp's in question into str{,n}cmp's.  And, if you've
accidentally run the old (bad) quota -f command and it destroyed the
quota roots, running the patched version will actually resurrect them!

(I intend to submit this to the Cyrus folks as soon as I have a chance...)

--- imap/quota.c.orig	Fri Aug 20 15:08:30 1999
+++ imap/quota.c	Sat Jul 21 17:13:16 2001
@@ -132,7 +132,7 @@
 int compare_quota(a, b)
 char *a, *b;
 {
-    return strcasecmp(((struct quotaentry *)a)->quota.root,
+    return strcmp(((struct quotaentry *)a)->quota.root,
 		      ((struct quotaentry *)b)->quota.root);
 }
 
@@ -172,8 +172,8 @@
 	    /* If restricting our list, see if this quota file matches */
 	    if (nroots) {
 		for (i = 0; i < nroots; i++) {
-		    if (!strcasecmp(dirent->d_name, roots[i]) ||
-			(!strncasecmp(dirent->d_name, roots[i], strlen(roots[i])) &&
+		    if (!strcmp(dirent->d_name, roots[i]) ||
+			(!strncmp(dirent->d_name, roots[i], strlen(roots[i])) &&
 			 dirent->d_name[strlen(roots[i])] == '.')) break;
 		}
 		if (i == nroots) continue;
@@ -231,7 +231,7 @@
     int i, len, thisquota, thisquotalen;
 
     while (firstquota < quota_num &&
-	   strncasecmp(name, quota[firstquota].quota.root,
+	   strncmp(name, quota[firstquota].quota.root,
 		       strlen(quota[firstquota].quota.root)) > 0) {
 	r = fixquota_finish(firstquota++);
 	if (r) return r;
@@ -240,9 +240,9 @@
     thisquota = -1;
     thisquotalen = 0;
     for (i = firstquota;
-	 i < quota_num && strcasecmp(name, quota[i].quota.root) >= 0; i++) {
+	 i < quota_num && strcmp(name, quota[i].quota.root) >= 0; i++) {
 	len = strlen(quota[i].quota.root);
-	if (!strncasecmp(name, quota[i].quota.root, len) &&
+	if (!strncmp(name, quota[i].quota.root, len) &&
 	    (!name[len] || name[len] == '.')) {
 	    quota[i].refcount++;
 	    if (len > thisquotalen) {
>Release-Note:
>Audit-Trail:
>Unformatted: