[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