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

List:       qmail-ldap
Subject:    Selective relaying
From:       Nuno Pais Fernandes <npf () eurotux ! com>
Date:       2006-05-17 15:18:49
Message-ID: 200605171618.53603.npf () eurotux ! com
[Download RAW message or body]

[Attachment #2 (multipart/mixed)]


Hi,

Qmail+VpopMail has an interesting feature called selective relaying that 
allows an admin to select which users can send emails to outside domains 
(relaying).

The attached patch does the same thing for qmail-ldap. I've created a new ldap 
entry for each user called allowRelay that denies relay to authenticated 
users if that flag is zero. If it's 1 the env relay variable is created and 
the email is authorized.

In the control dir, you can create a default file called authrelay to allow or 
deny relaying to users that don't have the ldap entry. It's like a default 
value.

qmail-smtpd.rules must have RELAYCLIENT only for localhost, as network-users 
use authenticated connections.

I've also wrote an HORDE/IMP patch (alpha stage, It Works for Me) to do the 
same thing, allowing an user to send outside emails only if the ldap 
allowRelay is 1. If you're interested in this patch mail me off list.

Any comments are welcome. Thanks
Nuno Fernandes

-- 
--------------------------
Nuno Miguel Pais Fernandes
CCNA
<npf@eurotux.com>

["qmail-ldap-1.03-20060201.selectiveRelay.patch" (text/x-diff)]

diff -Naur qmail-1.03.orig/auth_smtp.c qmail-1.03/auth_smtp.c
--- qmail-1.03.orig/auth_smtp.c	2006-05-18 17:29:00.000000000 +0100
+++ qmail-1.03/auth_smtp.c	2006-05-18 17:29:27.000000000 +0100
@@ -56,7 +56,7 @@
 #include "auth_mod.h"
 
 checkfunc	cfuncs[] = {
-	check_ldap,
+	check_ldap_relay,
 	0
 };
 
@@ -134,6 +134,15 @@
 	_exit(0);
 }
 
+void
+auth_norelay(const char *login)
+{
+	if (substdio_put(subfdout, "R", 1) == -1) auth_error(ERRNO);
+	if (substdio_puts(subfdout, login) == -1) auth_error(ERRNO);
+	if (substdio_putflush(subfdout, "", 1) == -1) auth_error(ERRNO);
+	_exit(0);
+}
+
 void auth_error(int errnum)
 {
 	logit(2, "warning: auth_error: authorization failed (%s)\n",
@@ -194,6 +203,10 @@
 		/* authdata no longer needed */
 		byte_zero(authdatastr.s, authdatastr.len);
 		auth_success(loginstr.s);
+	case NORELAY:
+		/* authdata no longer needed */
+		byte_zero(authdatastr.s, authdatastr.len);
+		auth_norelay(loginstr.s);
 	case NOSUCH: /* FALLTHROUGH */
 	case BADPASS:
 		/* authdata no longer needed */
diff -Naur qmail-1.03.orig/checkpassword.c qmail-1.03/checkpassword.c
--- qmail-1.03.orig/checkpassword.c	2006-05-18 17:29:01.000000000 +0100
+++ qmail-1.03/checkpassword.c	2006-05-18 17:29:27.000000000 +0100
@@ -228,6 +228,169 @@
 	
 }
 
+int
+check_ldap_relay(stralloc *login, stralloc *authdata,
+    struct credentials *c, int fast)
+{
+	static	stralloc ld = {0};
+	qldap	*q;
+	char	*filter;
+	int	r, status, pwok, needforward;
+	unsigned long count, size, max;
+	const	char	*attrs[] = {
+				LDAP_UID, /* the first 10 attrs are default */
+				LDAP_QMAILUID,
+				LDAP_QMAILGID,
+				LDAP_ISACTIVE,
+				LDAP_MAILHOST,
+				LDAP_MAILSTORE,
+				LDAP_HOMEDIR,
+				LDAP_QUOTA_SIZE,
+				LDAP_QUOTA_COUNT,
+				LDAP_RELAY,
+				LDAP_MAXMSIZE,
+				LDAP_PASSWD, 0}; /* passwd is extra */
+
+	/* TODO more debug output is needed */
+	needforward = 0;
+	q = qldap_new();
+	if (q == 0)
+		return ERRNO;
+	
+	r = qldap_open(q);
+	if (r != OK) goto fail;
+	r = qldap_bind(q, 0, 0);
+	if (r != OK) goto fail;
+	
+	if (fast) {
+		/* just comapre passwords and account status */
+		attrs[0] = LDAP_ISACTIVE;
+		attrs[1] = LDAP_RELAY;
+		if (qldap_need_rebind() == 0) {
+			attrs[2] = LDAP_PASSWD;
+			attrs[3] = 0;
+		} else
+			attrs[2] = 0;
+	} else {
+		if (qldap_need_rebind() != 0)
+			attrs[10] = 0;
+	}
+
+	filter = filter_uid(login->s);
+	if (filter == 0) { r = ERRNO; goto fail; }
+
+	r = qldap_lookup(q, filter, attrs);
+	if (r != OK) goto fail;
+
+	r = qldap_get_status(q, &status);
+	if (r != OK) goto fail;
+	if (status == STATUS_BOUNCE || status == STATUS_NOACCESS ||
+	    status == STATUS_DELETE) {
+		qldap_free(q);
+		return ACC_DISABLED;
+	}
+	
+	if (!fast) {
+#ifdef QLDAP_CLUSTER
+		r = qldap_get_attr(q, LDAP_MAILHOST, &c->forwarder,
+		    SINGLE_VALUE);
+		if (r != OK && r != NOSUCH) goto fail;
+		if (r == OK && cluster(c->forwarder.s) == 1) {
+			/* hostname is different, so I reconnect */
+			logit(8, "check_ldap: forwarding session to %s\n",
+			    c->forwarder.s);
+			needforward = 1;
+		}
+#endif
+
+		r = qldap_get_uid(q, &c->uid);
+		if (r != OK) goto fail;
+		r = qldap_get_relay(q, &c->relay);
+		if (r != OK) goto fail;
+		if (c->relay)
+			logit(8, "check_ldap: allow relay for user %s\n", login->s);
+		else
+			logit(8, "check_ldap: don't allow relay for user %s\n", login->s);
+		r = qldap_get_gid(q, &c->gid);
+		if (r != OK) goto fail;
+		r = qldap_get_mailstore(q, &c->home, &c->maildir);
+		if (r != OK) goto fail;
+		if (!stralloc_0(&c->home) ||
+		    !stralloc_0(&c->maildir))
+			return ERRNO;
+		size = count = max = 0;
+		r = qldap_get_quota(q, &size, &count, &max);
+		if (r != OK) goto fail;
+		if (max != 0) {
+			num[fmt_ulong(num, max)] = 0;
+			if (!env_put2("DATASIZE", num))
+				auth_error(ERRNO);
+		}
+		if (size != 0 || count != 0) {
+			if (!stralloc_copys(&ld, "")) auth_error(ERRNO);
+			if (size != 0) {
+				if (!stralloc_catb(&ld, num,
+					    fmt_ulong(num, size)))
+					auth_error(ERRNO);
+				if (!stralloc_append(&ld, "S"))
+					auth_error(ERRNO);
+			}
+			if (count != 0) {
+				if (size != 0)
+					if (!stralloc_append(&ld, ","))
+						auth_error(ERRNO);
+				if (!stralloc_catb(&ld, num,
+					    fmt_ulong(num, count)))
+					auth_error(ERRNO);
+				if (!stralloc_append(&ld, "C"))
+					auth_error(ERRNO);
+			}
+			if (!stralloc_0(&ld)) auth_error(ERRNO);
+			if (!env_put2(ENV_QUOTA, ld.s )) auth_error(ERRNO);
+		}
+	} else {
+		r = qldap_get_relay(q, &c->relay);
+		if (r != OK) goto fail;
+		if (c->relay)
+			logit(8, "check_ldap: allow relay for user %s\n", login->s);
+		else
+			logit(8, "check_ldap: don't allow relay for user %s\n", login->s);
+	}
+	
+	if (qldap_need_rebind() == 0) {
+		r = qldap_get_attr(q, LDAP_PASSWD, &ld, SINGLE_VALUE);
+		if (r != OK) goto fail;
+		pwok = cmp_passwd(authdata->s, ld.s);
+		if (!(c->relay)) pwok = NORELAY;
+	} else {
+		r = qldap_get_dn(q, &ld);
+		if (r != OK) goto fail;
+		r = qldap_rebind(q, ld.s, authdata->s);
+		switch (r) {
+		case OK:
+			pwok = OK;
+			if (!(c->relay)) pwok = NORELAY;
+			break;
+		case LDAP_BIND_AUTH:
+			pwok = BADPASS;
+			break;
+		default:
+			pwok = r;
+			break;
+		}
+	}
+	logit(32, "check_ldap: password compare was %s\n", 
+	    pwok == OK?"successful":"not successful");
+	qldap_free(q);
+	if (pwok == OK  && needforward == 1)
+		return FORWARD;
+	return pwok;
+fail:
+	qldap_free(q);
+	return r;
+	
+}
+
 void
 change_uid(unsigned int uid, unsigned int gid)
 {
diff -Naur qmail-1.03.orig/checkpassword.h qmail-1.03/checkpassword.h
--- qmail-1.03.orig/checkpassword.h	2006-05-18 17:29:01.000000000 +0100
+++ qmail-1.03/checkpassword.h	2006-05-18 17:29:27.000000000 +0100
@@ -42,12 +42,14 @@
 	stralloc	home;
 	stralloc	maildir;
 	stralloc	forwarder;
+	unsigned int	relay;
 };
 
 typedef int (*checkfunc)(stralloc *, stralloc *, struct credentials *, int);
 
 int check(checkfunc *, stralloc *, stralloc *, struct credentials *, int);
 int check_ldap(stralloc *, stralloc *, struct credentials *, int);
+int check_ldap_relay(stralloc *, stralloc *, struct credentials *, int);
 void check_credentials(struct credentials *);
 void change_uid(unsigned int, unsigned int);
 void setup_env(char *, struct credentials *);
diff -Naur qmail-1.03.orig/qldap.c qmail-1.03/qldap.c
--- qmail-1.03.orig/qldap.c	2006-05-18 17:29:02.000000000 +0100
+++ qmail-1.03/qldap.c	2006-05-18 17:29:27.000000000 +0100
@@ -78,6 +78,7 @@
 unsigned int	ldap_timeout = QLDAP_TIMEOUT;	/* default timeout is 30 secs */
 int		rebind = 0;			/* default off */
 unsigned int	default_uid = 0;
+unsigned int	default_relay = 0;
 unsigned int	default_gid = 0;
 unsigned long	quotasize = 0;
 unsigned long	quotacount = 0;
@@ -147,6 +148,7 @@
 	ldap_timeout = QLDAP_TIMEOUT;	/* default timeout is 30 secs */
 	rebind = 0;			/* default off */
 	default_uid = 0;
+	default_relay = 0;
 	default_gid = 0;
 	quotasize = 0;
 	quotacount = 0;
@@ -182,6 +184,11 @@
 	if (default_uid != 0)
 		logit(64, "init_ldap: control/ldapuid: %i\n", default_uid);
 
+	if (control_readint(&default_relay, "control/authrelay") == -1)
+		return -1;
+	if (default_relay != 0)
+		logit(64, "init_ldap: control/authrelay: %i\n", default_relay);
+
 	if (control_readint(&default_gid, "control/ldapgid") == -1)
 		return -1;
 	if (default_gid != 0)
@@ -575,6 +582,29 @@
 }
 
 int
+qldap_get_relay(qldap *q, unsigned int *relay)
+{
+	unsigned long	ul;
+	int		r;
+
+	/* get and check the relay */
+	r = qldap_get_attr(q, LDAP_RELAY, &ldap_attr, SINGLE_VALUE);
+	if (r == OK) {
+		if (ldap_attr.s[scan_ulong(ldap_attr.s, &ul)] != '\0')
+			r = BADVAL;
+		else if (ul==0 || ul==1)
+			*relay = ul;
+		else
+			r = ILLVAL;
+	} else if (r == NOSUCH && default_relay != 0) {
+		*relay = default_relay;
+		return NEEDED;
+	} else if (r == NOSUCH)
+		return OK;
+	return r;
+}
+
+int
 qldap_get_gid(qldap *q, unsigned int *gid)
 {
 	unsigned long	ul;
diff -Naur qmail-1.03.orig/qldap-errno.h qmail-1.03/qldap-errno.h
--- qmail-1.03.orig/qldap-errno.h	2006-05-18 17:29:02.000000000 +0100
+++ qmail-1.03/qldap-errno.h	2006-05-18 17:29:27.000000000 +0100
@@ -54,6 +54,7 @@
 
 #define BADPASS			10	/* auth failed wrong password */
 #define FORWARD			11	/* session needs to be forwarded */
+#define NORELAY			19	/* user not allowed to relay */
 
 /* auth_mod and checkpassword specific errors */
 #define BADCLUSTER		20	/* bad settings for clustering */
diff -Naur qmail-1.03.orig/qmail-ldap.h qmail-1.03/qmail-ldap.h
--- qmail-1.03.orig/qmail-ldap.h	2006-05-18 17:29:02.000000000 +0100
+++ qmail-1.03/qmail-ldap.h	2006-05-18 17:29:55.000000000 +0100
@@ -109,6 +109,7 @@
 #define LDAP_REPLYTEXT		"mailReplyText"
 #define LDAP_DOTMODE		"qmailDotMode"
 #define LDAP_UID		"uid"
+#define LDAP_RELAY		"allowRelay"
 #define LDAP_PASSWD		"userPassword"
 #define LDAP_OBJECTCLASS	"objectClass"
 #define LDAP_ISACTIVE		"accountStatus"
diff -Naur qmail-1.03.orig/qmail.schema qmail-1.03/qmail.schema
--- qmail-1.03.orig/qmail.schema	2006-05-18 17:29:02.000000000 +0100
+++ qmail-1.03/qmail.schema	2006-05-18 17:29:27.000000000 +0100
@@ -107,6 +107,11 @@
 	EQUALITY integerMatch
 	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
 
+attributetype ( 1.3.6.1.4.1.7914.1.2.1.18 NAME 'allowRelay'
+	DESC 'Allow user to relay'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
 #
 # qmailGroup attributes
 #
@@ -256,7 +261,7 @@
 	      mailHost $ mailForwardingAddress $ deliveryProgramPath $
 	      qmailDotMode $ deliveryMode $ mailReplyText $
 	      accountStatus $ qmailAccountPurge $ 
-	      mailQuotaSize $ mailQuotaCount $ mailSizeMax ) )
+	      mailQuotaSize $ mailQuotaCount $ mailSizeMax $ allowRelay ) )
 
 objectclass ( 1.3.6.1.4.1.7914.1.3.2.1 NAME 'qmailGroup'
 	DESC 'QMail-LDAP Group'
diff -Naur qmail-1.03.orig/qmail-smtpd.c qmail-1.03/qmail-smtpd.c
--- qmail-1.03.orig/qmail-smtpd.c	2006-05-18 17:29:02.000000000 +0100
+++ qmail-1.03/qmail-smtpd.c	2006-05-18 17:29:27.000000000 +0100
@@ -787,6 +787,9 @@
   if (call_getc(&ccverify, &ch) != 1)
     goto fail;
   switch (ch) {
+  case 'R':
+    flagauth=2;
+    return 1;
   case 'K':
     return 1;
   case 'D':
@@ -902,7 +905,7 @@
   }
 
   /* check if we are authenticated, if yes enable relaying */
-  if (flagauthok && relayclient == 0) {
+  if (flagauthok==1 && relayclient == 0) {
     relayclient = "";
     if (!env_put("RELAYCLIENT=")) die_nomem();
   }
@@ -1592,7 +1595,10 @@
   status = auth_close(&cct, &line, authprepend);
   switch (*status) {
   case '2':
-    flagauthok = 1;
+    if (*(status+1)=='9') // no relay
+    	flagauthok = 2;
+    else
+	flagauthok = 1;
     remoteinfo = line.s;
     out(status);
     logline2(2,"authentication success, user ", remoteinfo);
diff -Naur qmail-1.03.orig/smtpcall.c qmail-1.03/smtpcall.c
--- qmail-1.03.orig/smtpcall.c	2006-05-18 17:29:03.000000000 +0100
+++ qmail-1.03/smtpcall.c	2006-05-18 17:29:27.000000000 +0100
@@ -211,6 +211,7 @@
 	int wstat;
 	int exitcode;
 	char c;
+	char npf;
 
 	s = 0; c = 0;
 	if ((long)cc->pid == -1)
@@ -228,7 +229,9 @@
 			    "(#4.3.0)\r\n";
 		} else
 		switch (c) {
+		case 'R':
 		case 'K':
+			npf=c;
 			if (!stralloc_copys(user, pre!=0?pre:"")) {
 				s = "421 out of memory (#4.3.0)\r\n"; 
 				break;
@@ -242,8 +245,12 @@
 			if (cc->flagerr)
 				s = "454 authentication process read "
 				    "failure. (#4.3.0)\r\n";
-			else
-				s = "235 nice to meet you\r\n";
+			else {
+				if (npf=='R')
+					s = "299 nice to meet you (no relay)\r\n";
+				else
+					s = "235 nice to meet you (allow relay)\r\n";
+			}
 			break;
 		case 'D':
 			s = "535 authentication failure\r\n";

[Attachment #6 (application/pgp-signature)]

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

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