[prev in list] [next in list] [prev in thread] [next in thread] 

List:       dovecot
Subject:    [Dovecot] dovecot checkpassword passdb and vmailmgr
From:       Mij <mij () bitchx ! it>
Date:       2006-09-27 23:44:11
Message-ID: B1B1553A-C9E4-4F5B-BDE4-0C9A45B9ABF8 () bitchx ! it
[Download RAW message or body]

hello

some times ago I posted some patches to workaround vmailmgr
not fitting into dovecot's passdb-checkpassword authentication
module.

Yesterday I spent some spare time for carrying out a cleaner
solution for this problem.
Check out these patches. If you're not interested in merging them,
I will make them available at the same page I used for the former
solution [ http://mij.oltrelinux.com/net/dovecot-qmail-vmailmgr ];
there were some 20 people/month that checked it out.


This is a brief explanation of the patches:

The root of all evil is that the checkpassword interface
[ http://cr.yp.to/checkpwd/interface.html ] leaves room to  
interpretation
when the scope is not system login. Vmailmgr and vpopmail, for
example, collapse many mail users on the same system account.
So, what "home" is here is not specified.
In the case of vmailmgr, it's "checkvpw" sets HOME to the system user's
home, and returns the path of the actual mail user differently.

The solution I applied for this problem is to generalize the process
that fetches authentication data (what currently is "checkpassword- 
reply").
I was set up for a specific passdb-vmailmgr module, but this would
have seen a lot of code replication. Similarly (but I did not inspect  
further),
I guess vpopmail can fit in this solution as well.

So
1) provide a module "vmailmgr-reply", with the same behavior as
checkpassword-reply, that's aware of the way checkvpw returns accounting
information and pass this back to dovecot transparently

so, this patch ships vmailmgr-reply along with checkpassword-reply (name
them "fetcher"s for brevity)

The idea is that every checkpassword-compatible module may have its own
fetcher for passing correct info back to dovecot.
Now with this generalization, the user must be able to specify if its  
checkpassword
module needs a custom fetcher, and if it does, what's its path.
So
2) modify auth/passdb-checkpassword.c and pump "args =". By default  
it takes the
path of the checkpassword module (e.g. "checkvpw"), now it can be  
appended
another field, that is the path of the custom fetcher. If this  
optional is omitted, the
default checkpassword-reply will be used, and the cp module is  
supposed to
behave like dovecot expects from it. Otherwise, a comma appear after  
the first path.
e.g.

passdb checkpassword {
	args = /xyz/checkvpw,/libexec/vmailmgr-reply
}

if one needs to specify paths with commas in the first one, commas  
can be escaped with \
(eg args=first\,path,second_path)


Last but not least, the patch addresses again the problem with colons  
in paths.
Currently, dovecot dictates no colons can exist in mailbox path  
names. In UNIX,
no such constraint exist. Moreover, paths with colons are common when  
working
specifically with qmail (see dot-qmail).
When dovecot encounters ':', it truncates the path.
This way, for example, if the authentication module returned home "/ 
path/name:surname",
dovecot will truncante it to "/path/name". If name exist,  
name:surname will browse
his mail without effort.
All this comes from dovecot parsing params on MAIL environment  
(INDEX, INBOX,
CONTROL), but it also happens if none of them was actually specified.

So
3) modify lib-storage/index/maildir/maildir-storage.c for fixing the  
parsing problem
with the mail environment. [ I did not provide the same fix for the  
remaining storages,
but this can easily be done by copy/paste if convenient ]. For  
simplification and without
loss of generality, this fix mandates ":INBOX=", ":INDEX=" and  
":CONTROL=" params
to follow in this order, if they appear.

[ a minor patch agains auth/Makefile.am is provided for including  
vmailmgr-reply when
building the rest of the auth stuff ]

Any comment on this is welcome for discussion.

bye






["vmailmgr-reply.c" (vmailmgr-reply.c)]

/* simple checkpassword wrapper to send userdb data back to dovecot-auth */

#include "lib.h"
#include "str.h"
#include "write-full.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#ifndef PATH_MAX
/* _POSIX_PATH_MAX is 256 */
#warning "PATH_MAX not available on your system. Defaulting to 256 chars"
#define PATH_MAX        256
#endif

int main(int argc, char *argv[])
{
	string_t *str;
	const char *extra_env, *value, *const *tmp;
    char mdirpath[PATH_MAX];


    if (argc < 2) {
        i_error("Unable to get maildir path. You need to put a \"maildir\" argument \
after me for me to get this information.");  return 1;
    }

    if (argv[1][0] == '/') {    /* absolute path given */
        size_t wsz;

        wsz = snprintf(mdirpath, PATH_MAX, "%s", argv[1]);
        if (wsz >= PATH_MAX) { /* overflow */
            i_error("PATH_MAX (%d) is not enough for holding the whole maildir path \
(%d).", PATH_MAX, wsz);  return 1;
        }
    } else if (argv[1][0] == '.' && argv[1][1] == '/') {    /* relative path */
        size_t wsz;

        if (getcwd(mdirpath, PATH_MAX) == NULL) {
            i_error("Could not get working directory: %m");
            return 1;
        }
        wsz = snprintf(mdirpath, PATH_MAX, "%s/%s", mdirpath, argv[1]+2);
        if (wsz >= PATH_MAX) { /* overflow */
            i_error("PATH_MAX (%d) is not enough for holding the whole maildir path \
(%d).", PATH_MAX, wsz);  return 1;
        }
    } else {            /* ?? */
        i_error("The argument I got in place of the maildir path is unusable: %s", \
argv[1]);  return 1;
    }

	lib_init();
	str = t_str_new(1024);

	if (strchr(getenv("USER"), '\t') != NULL) {
		i_error("USER contains TAB");
		return 1;
	}

	if (strchr(mdirpath, '\t') != NULL) {
		i_error("Mail dir path contains TAB");
		return 1;
	}

	str_printfa(str, "userdb_user=%s\t"
		    "userdb_home=%s\t"
		    "userdb_uid=%s\t"
		    "userdb_gid=%s\t",
		    getenv("USER"), mdirpath,
		    dec2str(getuid()), dec2str(getgid()));

	extra_env = getenv("EXTRA");
	if (extra_env != NULL) {
		for (tmp = t_strsplit(extra_env, " "); *tmp != NULL; tmp++) {
			value = getenv(*tmp);
			if (value != NULL) {
				str_printfa(str, "%s=%s\t",
					    t_str_lcase(*tmp), value);
			}
		}
	}

	if (write_full(4, str_data(str), str_len(str)) < 0) {
		i_error("write_full() failed: %m");
		exit(111);
	}
	return 0;
}


["passdb-checkpassword.c_patch" (passdb-checkpassword.c_patch)]

--- passdb-checkpassword.c	2006-08-17 21:47:56.000000000 +0200
+++ ../../../asd/src/auth/passdb-checkpassword.c	2006-09-28 01:31:54.000000000 +0200
@@ -375,17 +375,47 @@
 	hash_insert(module->clients, POINTER_CAST(pid), chkpw_auth_request);
 }
 
+static int
+checkpassword_parse_args(struct auth_passdb *auth_passdb, struct \
checkpassword_passdb_module *mod, const char *args) { +    const char *start;
+    size_t i = 1;
+
+    if (args[0] != '/' || args[0] == ',') return -1;  /* bad format */
+
+    /* first token always expected */
+    start = args;
+    while (args[i] != '\0' && (args[i] != ',' || args[i-1] == '\\')) i++;
+    mod->checkpassword_path = p_strdup_until(auth_passdb->auth->pool, start, \
start+i); +    if (args[i] == '\0') {
+        mod->checkpassword_reply_path = PKG_LIBEXECDIR"/checkpassword-reply";
+        return 0;
+    }
+
+    /* second token */
+    i++;
+    start = args + i;
+    while (args[i] != '\0' && (args[i] != ',' || args[i-1] == '\\')) i++;
+    if (args[i] != '\0') return -1;      /* more than 2 tokens given */
+    mod->checkpassword_reply_path = p_strdup_until(auth_passdb->auth->pool, start, \
start+i); +
+    return 0;
+}
+
 static struct passdb_module *
 checkpassword_preinit(struct auth_passdb *auth_passdb, const char *args)
 {
 	struct checkpassword_passdb_module *module;
+    int ret;
 
 	module = p_new(auth_passdb->auth->pool,
 		       struct checkpassword_passdb_module, 1);
-	module->checkpassword_path = p_strdup(auth_passdb->auth->pool, args);
-	module->checkpassword_reply_path =
-		PKG_LIBEXECDIR"/checkpassword-reply";
-
+    ret = checkpassword_parse_args(auth_passdb, module, args);
+    if (ret != 0) {
+        i_error("Invalid format for args config opt. Fallback to defaults -- %d", \
ret); +        module->checkpassword_path = "/usr/local/bin/checkpassword";
+        module->checkpassword_reply_path = PKG_LIBEXECDIR"/checkpassword-reply";
+    }
+	
 	module->clients =
 		hash_create(default_pool, default_pool, 0, NULL, NULL);
 


["maildir-storage.c_patch" (maildir-storage.c_patch)]

--- maildir-storage.c	2006-08-10 22:35:11.000000000 +0200
+++ ../../../../../asd/src/lib-storage/index/maildir/maildir-storage.c	2006-09-28 \
01:31:54.000000000 +0200 @@ -56,6 +56,19 @@
 	(void)rename(oldpath, path);
 }
 
+/* string for setting inboxes */
+#define INBOXSTR        ":INBOX="
+#define INBOXLEN        7
+
+/* string for setting indexes */
+#define INDEXSTR        ":INDEX="
+#define INDEXLEN        7
+
+/* string for setting control */
+#define CONTROLSTR      ":CONTROL="
+#define CONTROLLEN      9
+
+
 static struct mail_storage *
 maildir_create(const char *data, const char *user,
 	       enum mail_storage_flags flags,
@@ -99,26 +112,50 @@
 			root_dir = "/";
 		}
 	} else {
+        const char *p_inbox, *p_index, *p_control;
+
 		/* <Maildir> [:INBOX=<dir>] [:INDEX=<dir>] [:CONTROL=<dir>] */
-		if (debug)
-			i_info("maildir: data=%s", data);
-		p = strchr(data, ':');
-		if (p == NULL)
-			root_dir = data;
-		else {
-			root_dir = t_strdup_until(data, p);
+        if (debug)
+            i_info("maildir: data=%s", data);
 
-			do {
-				p++;
-				if (strncmp(p, "INBOX=", 6) == 0)
-					inbox_dir = t_strcut(p+6, ':');
-				else if (strncmp(p, "INDEX=", 6) == 0)
-					index_dir = t_strcut(p+6, ':');
-				else if (strncmp(p, "CONTROL=", 8) == 0)
-					control_dir = t_strcut(p+8, ':');
-				p = strchr(p, ':');
-			} while (p != NULL);
-		}
+        /* expecting INBOX, INDEX, CONTROL in order */
+        p_inbox = strstr(data, INBOXSTR);
+        p_index = strstr(data, INDEXSTR);
+        p_control = strstr(data, CONTROLSTR);
+
+        /* safety check p_inbox < p_index < p_control */
+        if (p_control != NULL && !(p_control > p_index && p_control > p_inbox)) {
+            i_error("inbox/index/control args in config do not appear in the order \
required."); +            return NULL;
+        }
+        if (p_index != NULL && !(p_index > p_inbox)) {
+            i_error("inbox/index/control args in config do not appear in the order \
required."); +            return NULL;
+        }
+
+        /* set root_dir */
+        if ((p = p_inbox) != NULL || (p = p_index) != NULL || (p = p_control) != \
NULL) { +            /* p is where the first option begin */
+            root_dir = t_strdup_until(data, p);
+            if (p_inbox != NULL) {  /* set ":INBOX=" if available */
+                if ((p = p_index) != NULL || (p = p_control) != NULL) {
+                    /* p holds the next arg */
+                    inbox_dir = t_strdup_until(p_inbox + INBOXLEN, p);
+                } else {
+                    inbox_dir = t_strdup(p_inbox + INBOXLEN);
+                }
+            }
+            if (p_index != NULL) {  /* set ":INDEX=" if available */
+                if (p_control != NULL) index_dir = t_strdup_until(p_index + \
INDEXLEN, p_control); +                else index_dir = t_strdup(p_index + INDEXLEN);
+            }
+            if (p_control != NULL) { /* set ":INDEX=" if available */
+                control_dir = t_strdup(p_control + CONTROLLEN);
+            }
+        } else {
+            /* no options set */
+            root_dir = data;
+        }
 	}
 
 	if (root_dir == NULL) {


["Makefile.am_patch" (Makefile.am_patch)]

--- Makefile.am	2006-06-18 11:44:10.000000000 +0200
+++ ../../../asd/src/auth/Makefile.am	2006-09-28 01:31:54.000000000 +0200
@@ -2,7 +2,7 @@
 
 pkglibexecdir = $(libexecdir)/dovecot
 
-pkglibexec_PROGRAMS = dovecot-auth checkpassword-reply
+pkglibexec_PROGRAMS = dovecot-auth checkpassword-reply vmailmgr-reply
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
@@ -112,5 +112,11 @@
 checkpassword_reply_LDADD = \
 	../lib/liblib.a
 
-checkpassword_reply_sources = \
+checkpassword_reply_SOURCES = \
 	checkpassword-reply.c
+
+vmailmgr_reply_LDADD = \
+	../lib/liblib.a
+
+vmailmgr_reply_SOURCES = \
+	vmailmgr-reply.c




[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic