File user_deny_db-once.patch of Package cyrus-imapd.import5240

From 1d6f0eedfb3a31695053ef0babe26e92b5fbbb2a Mon Sep 17 00:00:00 2001
From: murch <murch>
Date: Thu, 22 Apr 2010 17:29:53 +0000
Subject: Modified user_deny.db code to open database once at service startup time.


 8 files changed, 325 insertions(+), 127 deletions(-)

diff --git a/imap/Makefile.in b/imap/Makefile.in
index 3dd823c..e9c5790 100644
--- a/imap/Makefile.in
+++ b/imap/Makefile.in
@@ -103,7 +103,7 @@ LOBJS= append.o mailbox.o mboxlist.o mupdate-client.o mboxname.o message.o \
 	annotate.o search_engines.o squat.o squat_internal.o mbdump.o \
 	imapparse.o telemetry.o user.o notify.o idle.o quota_db.o \
 	sync_log.o autosieve.o $(SEEN) mboxkey.o backend.o tls.o message_guid.o \
-	statuscache_db.o
+	statuscache_db.o userdeny_db.o
 
 IMAPDOBJS=pushstats.o imapd.o proxy.o imap_proxy.o index.o version.o
 
diff --git a/imap/global.c b/imap/global.c
index 30412b6..940f592 100644
--- a/imap/global.c
+++ b/imap/global.c
@@ -72,8 +72,8 @@
 #include "mupdate_err.h"
 #include "mutex.h"
 #include "prot.h" /* for PROT_BUFSIZE */
+#include "userdeny.h"
 #include "util.h"
-#include "wildmat.h"
 #include "xmalloc.h"
 #include "xstrlcpy.h"
 #include "xstrlcat.h"
@@ -531,124 +531,6 @@ static int acl_ok(const char *user, struct auth_state *authstate)
     return r;
 }
 
-#define DENYDB config_userdeny_db
-#define FNAME_USERDENYDB "/user_deny.db"
-#define USERDENY_VERSION 2
-
-/*
- * access_ok() checks to see if 'user' is allowed access to 'service'
- * Returns 1 if so, 0 if not.
- */
-int access_ok(const char *user, const char *service, char *msgbuf, int size)
-{
-    static char *fname = NULL;
-    struct db *db = NULL;
-    int r, ret = 1;  /* access always granted by default */
-
-    if (!fname) {
-	/* create path to database */
-	fname = xmalloc(strlen(config_dir) + sizeof(FNAME_USERDENYDB) + 1);
-	strcpy(fname, config_dir);
-	strcat(fname, FNAME_USERDENYDB);
-    }
-
-    /* try to open database */
-    r = DENYDB->open(fname, 0, &db);
-    if (r) {
-	/* ignore non-existent DB, report all other errors */
-	if (errno != ENOENT) {
-	    syslog(LOG_WARNING, "DENYDB_ERROR: error opening '%s': %s",
-		   fname, cyrusdb_strerror(r));
-	}
-
-    } else {
-	/* fetch entry for user */
-	const char *data = NULL;
-	int datalen;
-
-	syslog(LOG_DEBUG, "fetching user_deny.db entry for '%s'", user);
-	do {
-	    r = DENYDB->fetch(db, user, strlen(user), &data, &datalen, NULL);
-	} while (r == CYRUSDB_AGAIN);
-
-	if (r || !data || !datalen) {
-	    /* ignore non-existent/empty entry, report all other errors */
-	    if (r != CYRUSDB_NOTFOUND) {
-		syslog(LOG_WARNING,
-		       "DENYDB_ERROR: error reading entry '%s': %s",
-		       user, cyrusdb_strerror(r));
-	    }
-	} else {
-	    /* parse the data */
-	    char *buf, *wild;
-	    unsigned long version;
-
-	    buf = xstrndup(data, datalen);  /* use a working copy */
-
-	    /* check version */
-	    if (((version = strtoul(buf, &wild, 10)) < 1) ||
-		(version > USERDENY_VERSION)) {
-		syslog(LOG_WARNING,
-		       "DENYDB_ERROR: invalid version for entry '%s': %lu",
-		       user, version);
-	    } else if (*wild++ != '\t') {
-		syslog(LOG_WARNING,
-		       "DENYDB_ERROR: missing wildmat for entry '%s'", user);
-	    } else {
-		char *pat, *msg = "Access to this service has been blocked";
-		int not;
-
-		/* check if we have a deny message */
-		switch (version) {
-		case USERDENY_VERSION:
-		    if ((msg = strchr(wild, '\t'))) *msg++ = '\0';
-		    break;
-		}
-
-		/* scan wildmat right to left for a match against our service */
-		syslog(LOG_DEBUG, "wild: '%s'   service: '%s'", wild, service);
-		do {
-		    /* isolate next pattern */
-		    if ((pat = strrchr(wild, ','))) {
-			*pat++ = '\0';
-		    } else {
-			pat = wild;
-		    }
-
-		    /* XXX  trim leading & trailing whitespace? */
-
-		    /* is it a negated pattern? */
-		    not = (*pat == '!');
-		    if (not) ++pat;
-
-		    syslog(LOG_DEBUG, "pat %d:'%s'", not, pat);
-
-		    /* see if pattern matches our service */
-		    if (wildmat(service, pat)) {
-			/* match ==> we're done */
-			ret = not;
-			if (msgbuf) strlcpy(msgbuf, msg, size);
-			break;
-		    }
-
-		    /* continue until we reach head of wildmat */
-		} while (pat != wild);
-	    }
-
-	    free(buf);
-	}
-
-
-	r = DENYDB->close(db);
-	if (r) {
-	    syslog(LOG_WARNING, "DENYDB_ERROR: error closing: %s",
-		   cyrusdb_strerror(r));
-	}
-    }
-
-    return ret;
-}
-
 /* should we allow users to proxy?  return SASL_OK if yes,
    SASL_BADAUTH otherwise */
 int mysasl_proxy_policy(sasl_conn_t *conn,
@@ -704,7 +586,7 @@ int mysasl_proxy_policy(sasl_conn_t *conn,
     }
 
     /* is requested_user denied access?  authenticated admins are exempt */
-    if (!userisadmin && !access_ok(requested_user, config_ident, NULL, 0)) {
+    if (!userisadmin && userdeny(requested_user, config_ident, NULL, 0)) {
 	syslog(LOG_ERR, "user '%s' denied access to service '%s'",
 	       requested_user, config_ident);
 	sasl_seterror(conn, SASL_NOLOG,
diff --git a/imap/global.h b/imap/global.h
index ec3d54e..791c5c7 100644
--- a/imap/global.h
+++ b/imap/global.h
@@ -160,5 +160,6 @@ extern struct cyrusdb_backend *config_duplicate_db;
 extern struct cyrusdb_backend *config_tlscache_db;
 extern struct cyrusdb_backend *config_ptscache_db;
 extern struct cyrusdb_backend *config_statuscache_db;
+extern struct cyrusdb_backend *config_userdeny_db;
 
 #endif /* INCLUDED_GLOBAL_H */
diff --git a/imap/imapd.c b/imap/imapd.c
index ca837b9..3ebeb16 100644
--- a/imap/imapd.c
+++ b/imap/imapd.c
@@ -101,6 +101,7 @@
 #include "telemetry.h"
 #include "tls.h"
 #include "user.h"
+#include "userdeny.h"
 #include "util.h"
 #include "version.h"
 #include "xmalloc.h"
@@ -679,6 +680,10 @@ int service_init(int argc, char **argv, char **envp)
     quotadb_init(0);
     quotadb_open(NULL);
 
+    /* open the user deny db */
+    denydb_init(0);
+    denydb_open(NULL);
+
     /* setup for sending IMAP IDLE notifications */
     idle_enabled();
 
@@ -932,6 +937,9 @@ void shut_down(int code)
     quotadb_close();
     quotadb_done();
 
+    denydb_close();
+    denydb_done();
+
     annotatemore_close();
     annotatemore_done();
 
@@ -1104,7 +1112,7 @@ void cmdloop()
 	/* Check for shutdown file */
 	if ( !imapd_userisadmin && imapd_userid &&
 	     (shutdown_file(shut, sizeof(shut)) ||
-	      !access_ok(imapd_userid, config_ident, shut, sizeof(shut)))) {
+	      userdeny(imapd_userid, config_ident, shut, sizeof(shut)))) {
 	    for (p = shut; *p == '['; p++); /* can't have [ be first char */
 	    prot_printf(imapd_out, "* BYE [ALERT] %s\r\n", p);
 	    shut_down(0);
@@ -2738,7 +2746,7 @@ void cmd_idle(char *tag)
 	    if (!imapd_userisadmin &&
 		(shutdown_file(buf, sizeof(buf)) ||
 		 (imapd_userid && 
-		  !access_ok(imapd_userid, config_ident, buf, sizeof(buf))))) {
+		  userdeny(imapd_userid, config_ident, buf, sizeof(buf))))) {
 		shutdown = done = 1;
 		goto done;
 	    }
@@ -2799,7 +2807,7 @@ void idle_update(idle_flags_t flags)
 	if (! imapd_userisadmin &&
 	    (shutdown_file(shut, sizeof(shut)) ||
 	     (imapd_userid && 
-	      !access_ok(imapd_userid, config_ident, shut, sizeof(shut))))) {
+	      userdeny(imapd_userid, config_ident, shut, sizeof(shut))))) {
 	    char *p;
 	    for (p = shut; *p == '['; p++); /* can't have [ be first char */
 	    prot_printf(imapd_out, "* BYE [ALERT] %s\r\n", p);
diff --git a/imap/nntpd.c b/imap/nntpd.c
index 4265abe..a495285 100644
--- a/imap/nntpd.c
+++ b/imap/nntpd.c
@@ -103,6 +103,7 @@
 #include "sync_log.h"
 #include "telemetry.h"
 #include "tls.h"
+#include "userdeny.h"
 #include "util.h"
 #include "version.h"
 #include "wildmat.h"
@@ -494,6 +495,10 @@ int service_init(int argc __attribute__((unused)),
     quotadb_init(0);
     quotadb_open(NULL);
 
+    /* open the user deny db */
+    denydb_init(0);
+    denydb_open(NULL);
+
     /* setup for sending IMAP IDLE notifications */
     idle_enabled();
 
@@ -704,6 +709,9 @@ void shut_down(int code)
     quotadb_close();
     quotadb_done();
 
+    denydb_close();
+    denydb_done();
+
     annotatemore_close();
     annotatemore_done();
 
@@ -815,7 +823,7 @@ static void cmdloop(void)
 	/* Check for shutdown file */
 	if (shutdown_file(buf, sizeof(buf)) ||
 	    (nntp_userid &&
-	     !access_ok(nntp_userid, config_ident, buf, sizeof(buf)))) {
+	     userdeny(nntp_userid, config_ident, buf, sizeof(buf)))) {
 	    prot_printf(nntp_out, "400 %s\r\n", buf);
 	    shut_down(0);
 	}
diff --git a/imap/pop3d.c b/imap/pop3d.c
index 1302e3c..84b928b 100644
--- a/imap/pop3d.c
+++ b/imap/pop3d.c
@@ -88,6 +88,7 @@
 #include "backend.h"
 #include "proxy.h"
 #include "seen.h"
+#include "userdeny.h"
 
 #include "sync_log.h"
 #include "statuscache.h"
@@ -434,6 +435,10 @@ int service_init(int argc __attribute__((unused)),
     quotadb_init(0);
     quotadb_open(NULL);
 
+    /* open the user deny db */
+    denydb_init(0);
+    denydb_open(NULL);
+
     if (config_getswitch(IMAPOPT_STATUSCACHE)) {
 	/* open statuscache db to optimize handling an empty maildrop */
 	statuscache_open(NULL);
@@ -643,6 +648,9 @@ void shut_down(int code)
     quotadb_close();
     quotadb_done();
 
+    denydb_close();
+    denydb_done();
+
     if (popd_in) {
 	prot_NONBLOCK(popd_in);
 	prot_fill(popd_in);
@@ -804,7 +812,7 @@ static void cmdloop(void)
 	/* check for shutdown file */
 	if (shutdown_file(inputbuf, sizeof(inputbuf)) ||
 	    (popd_userid &&
-	     !access_ok(popd_userid, config_ident, inputbuf, sizeof(inputbuf)))) {
+	     userdeny(popd_userid, config_ident, inputbuf, sizeof(inputbuf)))) {
 	    for (p = inputbuf; *p == '['; p++); /* can't have [ be first char */
 	    prot_printf(popd_out, "-ERR [SYS/TEMP] %s\r\n", p);
 	    shut_down(0);
@@ -2024,7 +2032,7 @@ static void bitpipe(void)
 
 	/* check for shutdown file */
 	if (shutdown_file(buf, sizeof(buf)) ||
-	    !access_ok(popd_userid, config_ident, buf, sizeof(buf))) {
+	    userdeny(popd_userid, config_ident, buf, sizeof(buf))) {
 	    shutdown = 1;
 	    goto done;
 	}
diff --git a/imap/userdeny.h b/imap/userdeny.h
new file mode 100644
index 0000000..a4428c3
--- /dev/null
+++ b/imap/userdeny.h
@@ -0,0 +1,72 @@
+/* userdeny.h -- User deny definitions
+ *
+ * Copyright (c) 1994-2010 Carnegie Mellon University.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Carnegie Mellon University
+ *      Center for Technology Transfer and Enterprise Creation
+ *      4615 Forbes Avenue
+ *      Suite 302
+ *      Pittsburgh, PA  15213
+ *      (412) 268-7393, fax: (412) 268-7395
+ *      innovation@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id$
+ */
+
+#ifndef INCLUDED_USERDENY_H
+#define INCLUDED_USERDENY_H
+
+#include "cyrusdb.h"
+#include <config.h>
+
+#define FNAME_USERDENYDB "/user_deny.db"
+#define USERDENY_VERSION 2
+
+extern struct db *denydb;
+
+extern int userdeny(const char *user, const char *service,
+		    char *msgbuf, size_t bufsiz);
+
+/* open the user deny db */
+void denydb_open(char *name);
+
+/* close the database */
+void denydb_close(void);
+
+/* initialize database structures */
+#define DENYDB_SYNC 0x02
+void denydb_init(int flags);
+
+/* done with database stuff */
+void denydb_done(void);
+
+#endif /* INCLUDED_USERDENY_H */
diff --git a/imap/userdeny_db.c b/imap/userdeny_db.c
new file mode 100644
index 0000000..8d8791d
--- /dev/null
+++ b/imap/userdeny_db.c
@@ -0,0 +1,219 @@
+/* userdeny_db.c -- User deny manipulation routines
+ *
+ * Copyright (c) 1994-2010 Carnegie Mellon University.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Carnegie Mellon University
+ *      Center for Technology Transfer and Enterprise Creation
+ *      4615 Forbes Avenue
+ *      Suite 302
+ *      Pittsburgh, PA  15213
+ *      (412) 268-7393, fax: (412) 268-7395
+ *      innovation@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id$
+ */
+
+#include <config.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include "cyrusdb.h"
+#include "global.h"
+#include "userdeny.h"
+#include "wildmat.h"
+#include "xmalloc.h"
+#include "xstrlcpy.h"
+#include "xstrlcat.h"
+
+#define DENYDB config_userdeny_db
+
+struct db *denydb;
+
+static int deny_dbopen = 0;
+
+
+/*
+ * userdeny() checks to see if 'user' is denied access to 'service'
+ * Returns 1 if a matching deny entry exists in DB, otherwise returns 0.
+ */
+int userdeny(const char *user, const char *service, char *msgbuf, size_t bufsiz)
+{
+    int r, ret = 0; /* allow access by default */
+    const char *data = NULL;
+    int datalen;
+
+    if (!deny_dbopen) return 0;
+
+    /* fetch entry for user */
+    syslog(LOG_DEBUG, "fetching user_deny.db entry for '%s'", user);
+    do {
+	r = DENYDB->fetch(denydb, user, strlen(user), &data, &datalen, NULL);
+    } while (r == CYRUSDB_AGAIN);
+
+    /* XXX  Should we try to reopen the DB if we get IOERROR?
+            This might be necessary when using SQL backend
+	    and we lose the connection.
+    */
+
+    if (r || !data || !datalen) {
+	/* ignore non-existent/empty entry, report all other errors */
+	if (r != CYRUSDB_NOTFOUND) {
+	    syslog(LOG_WARNING,
+		   "DENYDB_ERROR: error reading entry '%s': %s",
+		   user, cyrusdb_strerror(r));
+	}
+    } else {
+	/* parse the data */
+	char *buf, *wild;
+	unsigned long version;
+
+	buf = xstrndup(data, datalen);  /* use a working copy */
+
+	/* check version */
+	if (((version = strtoul(buf, &wild, 10)) < 1) ||
+	    (version > USERDENY_VERSION)) {
+	    syslog(LOG_WARNING,
+		   "DENYDB_ERROR: invalid version for entry '%s': %lu",
+		   user, version);
+	} else if (*wild++ != '\t') {
+	    syslog(LOG_WARNING,
+		   "DENYDB_ERROR: missing wildmat for entry '%s'", user);
+	} else {
+	    char *pat, *msg = "Access to this service has been blocked";
+	    int not;
+
+	    /* check if we have a deny message */
+	    switch (version) {
+	    case USERDENY_VERSION:
+		if ((msg = strchr(wild, '\t'))) *msg++ = '\0';
+		break;
+	    }
+
+	    /* scan wildmat right to left for a match against our service */
+	    syslog(LOG_DEBUG, "wild: '%s'   service: '%s'", wild, service);
+	    do {
+		/* isolate next pattern */
+		if ((pat = strrchr(wild, ','))) {
+		    *pat++ = '\0';
+		} else {
+		    pat = wild;
+		}
+
+		/* XXX  trim leading & trailing whitespace? */
+
+		/* is it a negated pattern? */
+		not = (*pat == '!');
+		if (not) ++pat;
+
+		syslog(LOG_DEBUG, "pat %d:'%s'", not, pat);
+
+		/* see if pattern matches our service */
+		if (wildmat(service, pat)) {
+		    /* match ==> we're done */
+		    ret = !not;
+		    if (msgbuf) strlcpy(msgbuf, msg, bufsiz);
+		    break;
+		}
+
+		/* continue until we reach head of wildmat */
+	    } while (pat != wild);
+	}
+
+	free(buf);
+    }
+
+    return ret;
+}
+
+/* must be called after cyrus_init */
+void denydb_init(int myflags)
+{
+    int r;
+
+    if (myflags & DENYDB_SYNC) {
+	r = DENYDB->sync();
+    }
+}
+
+void denydb_open(char *fname)
+{
+    int ret;
+    char *tofree = NULL;
+
+    /* create db file name */
+    if (!fname) {
+	size_t fname_len = strlen(config_dir)+strlen(FNAME_USERDENYDB)+1;
+	
+	fname = xmalloc(fname_len);
+	tofree = fname;
+
+	strlcpy(fname, config_dir, fname_len);
+	strlcat(fname, FNAME_USERDENYDB, fname_len);
+    }
+
+    ret = (DENYDB->open)(fname, 0, &denydb);
+    if (ret == CYRUSDB_OK) {
+	deny_dbopen = 1;
+    } else if (errno != ENOENT) {
+	/* ignore non-existent DB, report all other errors */
+	syslog(LOG_WARNING, "DENYDB_ERROR: opening %s: %s", fname,
+	       cyrusdb_strerror(ret));
+    }
+
+    if (tofree) free(tofree);
+}
+
+void denydb_close(void)
+{
+    int r;
+
+    if (deny_dbopen) {
+	r = (DENYDB->close)(denydb);
+	if (r) {
+	    syslog(LOG_ERR, "DENYDB_ERROR: error closing: %s",
+		   cyrusdb_strerror(r));
+	}
+	deny_dbopen = 0;
+    }
+}
+
+void denydb_done(void)
+{
+    /* DB->done() handled by cyrus_done() */
+}
-- 
1.7.0.3