Attached is a diff to policyd v1.80 that enables it to process IPv6
addresses and accept connections from postfix servers over IPv6. The code
changes in the diff have been designed to be independent of IP version as
much as possible. I've also added BINDHOST examples in the sample
policyd.conf. Note that several sql database fields have increased in
length to accomodate the longer IPv6 addresses.
One known issue: policyd's current design doesn't allow for arbitrary
prefix length checks. Instead it uses a classful, octet based, address
comparison controlled by the GREYLIST_HOST config variable. When IPv6
addresses are passed from postfix, the address comparison functions will
use the entire IPv6 address and ignore GREYLIST_HOST. To fully support
IPv6 subnets, the GREYLIST_HOST paradigm will probably need to change to
something more general such as a prefix length.
Enjoy!
Antonio Querubin
[EMAIL PROTECTED]
diff -uwr policyd-v1.80.orig/Changelog.txt policyd-v1.80/Changelog.txt
--- policyd-v1.80.orig/Changelog.txt 2006-07-09 22:48:04.000000000 -1000
+++ policyd-v1.80/Changelog.txt 2007-04-24 19:38:26.000000000 -1000
@@ -1,3 +1,21 @@
+[?? Apr 2007]
+Various modifications to handle IPv6 connections and addresses
+
+ * DATABASE.mysql: increased field widths to accomodate IPv6 addresses
+ * *.c: various comment corrections to function description
+ * names
+ * generic.c: modified address syntax checks to handle IPv6 addresses
+ * policyd.c: replaced IPv4-dependent code with version-independent code
+ * policyd.conf: added new examples for BINDHOST and CONN_ACL
+ * policyd.h: updated prototypes for w_inet_ntop() and w_bind();
+ * added sockaddrport() prototype
+ * README.txt: added IPv6 example for postfix main.cf
+ * sockets.c: most IP version-dependent code moved into here;
+ * w_bind() passes bind() return value back to caller;
+ * w_inet_ntop() figures out address family automatically;
+ * sockaddrport() function added to obtain
+ * the port number from a sockaddr structure
+
[10 Jul 2006]
Changes from v1.79 -> v1.80 (bug fixes & new functionality)
diff -uwr policyd-v1.80.orig/cidr.c policyd-v1.80/cidr.c
--- policyd-v1.80.orig/cidr.c 2006-07-03 22:29:12.000000000 -1000
+++ policyd-v1.80/cidr.c 2007-04-24 20:11:55.000000000 -1000
@@ -30,6 +30,8 @@
/*
+ * XXX Not used - called from w_tcp_conn_acl() which is currently not
+ * XXX used. Needs updating to handle IPv6 addresses.
* function: cidr_ip_match
* purpose:
* return:
diff -uwr policyd-v1.80.orig/DATABASE.mysql policyd-v1.80/DATABASE.mysql
--- policyd-v1.80.orig/DATABASE.mysql 2005-12-22 21:38:47.000000000 -1000
+++ policyd-v1.80/DATABASE.mysql 2007-01-24 14:19:23.000000000 -1000
@@ -61,7 +61,7 @@
CREATE TABLE triplet (
_from char(60) NOT NULL default '',
_rcpt char(60) NOT NULL default '',
- _host char(15) NOT NULL default '',
+ _host char(46) NOT NULL default '',
_datenew int(10) unsigned NOT NULL default '0',
_datelast int(10) unsigned NOT NULL default '0',
_count smallint(5) unsigned NOT NULL default '0',
@@ -71,7 +71,7 @@
) TYPE=MyISAM;
CREATE TABLE whitelist (
- _whitelist char(15) NOT NULL default '',
+ _whitelist char(46) NOT NULL default '',
_description char(60) NOT NULL default '',
_expire int(10) unsigned NOT NULL default '0',
UNIQUE KEY _whitelist (_whitelist),
@@ -95,7 +95,7 @@
) TYPE=MyISAM;
CREATE TABLE blacklist (
- _blacklist char(15) NOT NULL default '',
+ _blacklist char(46) NOT NULL default '',
_description char(60) NOT NULL default '',
_expire int(10) unsigned NOT NULL default '0',
UNIQUE KEY _blacklist (_blacklist),
@@ -130,7 +130,7 @@
) TYPE=MyISAM;
CREATE TABLE helo (
- _host char(15) NOT NULL default '',
+ _host char(46) NOT NULL default '',
_helo char(60) NOT NULL default '',
_expire int(10) unsigned NOT NULL default '0',
UNIQUE KEY _host (_host,_helo),
diff -uwr policyd-v1.80.orig/generic.c policyd-v1.80/generic.c
--- policyd-v1.80.orig/generic.c 2006-07-03 05:33:31.000000000 -1000
+++ policyd-v1.80/generic.c 2007-04-24 20:02:37.000000000 -1000
@@ -959,11 +959,12 @@
for( ; clen <= tlen && clen <= 63 ; clen++) {
if(token[clen]=='=') {
for(clen++,y=0; clen<=tlen&&clen<=63;clen++) {
- /* we only want characters [A-Z][a-z][0-9]/@ and . */
+ /* we only want characters [A-Z][a-z][0-9]/@: and . */
if((isalnum(token[clen]) != 0)
|| (token[clen] == '@')
|| (token[clen] == '|')
|| (token[clen] == '.')
+ || (token[clen] == ':')
|| (token[clen] == '_')
|| (token[clen] == '-')
|| (token[clen] == ' ')
@@ -1009,6 +1010,8 @@
/*
+ * XXX Needs updating to handle IPv6 subnets (use prefix length
+ * XXX instead of number of octets?)
* function: extract_ip
* purpose: extract ip address from policy variable
* return: policy variable (into an array)
@@ -1024,8 +1027,11 @@
for(x=15,z=0,y=0;x<len||x<64;x++) {
if(token[x] == '\n') break;
- /* we only want characters [0-9] and . */
- if((isdigit(token[x]) != 0) || (token[x] == '.'))
+ /* we only want characters [0-9][a-f][A-F]/: and . */
+ if ( (isdigit(token[x]) != 0)
+ || ((token[x] == '.') || (token[x] == ':'))
+ || ((token[x] >= 'a') && (token[x] <= 'f'))
+ || ((token[x] >= 'A') && (token[x] <= 'F')) )
{
if(token[x] == '.') z++;
if(z == GREYLIST_HOSTADDR) break;
@@ -1038,7 +1044,9 @@
/*
- * function: extract_ip_array
+ * XXX Needs updating to handle IPv6 subnets (use prefix length
+ * XXX instead of number of octets?)
+ * function: extract_ipfill
* purpose: extract ip address from policy variable
* return: policy variable (into an array)
*/
@@ -1053,8 +1061,11 @@
for(x=15,z=0,y=0;x<len||x<64;x++) {
if(token[x] == '\n') break;
- /* we only want characters [0-9] and . */
- if((isdigit(token[x]) != 0) || (token[x] == '.'))
+ /* we only want characters [0-9][a-f][A-F]: and . */
+ if ( (isdigit(token[x]) != 0)
+ || ((token[x] == '.') || (token[x] == ':'))
+ || ((token[x] >= 'a') && (token[x] <= 'f'))
+ || ((token[x] >= 'A') && (token[x] <= 'F')) )
{
if(token[x] == '.')
{
diff -uwr policyd-v1.80.orig/policyd.c policyd-v1.80/policyd.c
--- policyd-v1.80.orig/policyd.c 2006-07-04 02:57:01.000000000 -1000
+++ policyd-v1.80/policyd.c 2007-01-25 02:19:22.000000000 -1000
@@ -38,8 +38,11 @@
fd_set rset, wset, rallset, wallset;
char host[48];
socklen_t clilen;
- struct sockaddr_in cliaddr, servaddr;
+ struct sockaddr_storage cliaddr, servaddr;
struct rlimit rlimit_nofile;
+ struct addrinfo aihints, *airesults;
+ char portstr[6];
+ int gaierrnum;
if(argc < 2) usage(argv[0]);
while ((c = getopt(argc,argv,":c:v")) != EOF)
@@ -90,13 +93,34 @@
/*
* bind to port
*/
- listenfd = w_socket(AF_INET, SOCK_STREAM, 0);
- bzero(&servaddr, sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_addr.s_addr=inet_addr(BINDHOST);
- servaddr.sin_port=htons(BINDPORT);
+ bzero(&aihints, sizeof (struct addrinfo));
+ aihints.ai_family = AF_UNSPEC;
+ aihints.ai_socktype = SOCK_STREAM;
+ aihints.ai_flags = AI_PASSIVE;
+ snprintf(portstr, sizeof portstr, "%d", BINDPORT);
+ if ( (gaierrnum = getaddrinfo(BINDHOST, portstr, &aihints, &airesults)) )
+ {
+ logmessage("fatal: getaddrinfo(): %s\n", gai_strerror(gaierrnum));
+ exit(EXIT_FAILURE);
+ }
+ while ( airesults != NULL )
+ {
+ if ( ( listenfd = w_socket(airesults->ai_family, airesults->ai_socktype,
+ airesults->ai_protocol) ) >= 0 )
+ {
+ bcopy(airesults->ai_addr, &servaddr, airesults->ai_addrlen);
+ if ( w_bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))
+ == 0 ) break;
+ }
+ close(listenfd);
+ airesults = airesults->ai_next;
+ }
+ if ( airesults == NULL )
+ {
+ logmessage("fatal: can't bind to any address\n");
+ exit(EXIT_FAILURE);
+ }
- w_bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
w_listen(listenfd, LISTENQ);
/*
@@ -166,10 +190,11 @@
/* tcp acl check */
/*
- if(w_tcp_conn_acl(w_inet_ntop(AF_INET, &cliaddr.sin_addr, host,
sizeof(host))) == -1)
+ if(w_tcp_conn_acl((struct sockaddr *) &cliaddr, host,
+ sizeof(host))) == -1)
{
logmessage("WARNING: connection attempt from: %s\n",
- w_inet_ntop(AF_INET, &cliaddr.sin_addr, host, sizeof(host)));
+ w_inet_ntop((struct sockaddr *) &cliaddr, host, sizeof(host)));
w_close(connfd);
continue;
}
@@ -179,16 +204,16 @@
if(numi==MAXFDS)
{
logmessage("connection from: %s port: %d slots: %d of %d used
(**WARNING**)\n",
- w_inet_ntop(AF_INET, &cliaddr.sin_addr, host, sizeof(host)),
- ntohs(cliaddr.sin_port), numi, MAXFDS);
+ w_inet_ntop((struct sockaddr *) &cliaddr, host, sizeof(host)),
+ sockaddrport((struct sockaddr *) &cliaddr), numi, MAXFDS);
w_close(connfd);
continue;
}
logmessage("connection from: %s port: %d slots: %d of %d used\n",
- w_inet_ntop(AF_INET, &cliaddr.sin_addr, host, sizeof(host)),
- ntohs(cliaddr.sin_port), numi, MAXFDS);
+ w_inet_ntop((struct sockaddr *) &cliaddr, host, sizeof(host)),
+ sockaddrport((struct sockaddr *) &cliaddr), numi, MAXFDS);
FD_SET(connfd, &rallset); /* add new descriptor to set
*/
buf_counter[connfd] = 0; /* zero current buffer counters
*/
diff -uwr policyd-v1.80.orig/policyd.conf policyd-v1.80/policyd.conf
--- policyd-v1.80.orig/policyd.conf 2006-06-28 00:41:37.000000000 -1000
+++ policyd-v1.80/policyd.conf 2007-04-24 20:23:04.000000000 -1000
@@ -97,7 +97,14 @@
#
# ip address which the policy daemon will listen on
#
+# IPv4 loopback
BINDHOST=127.0.0.1
+# IPv6 loopback
+#BINDHOST=::1
+# any IPv4 address
+#BINDHOST=0
+# any IPv6 or IPv4 address
+#BINDHOST=::
#
# port to bind to:
@@ -154,7 +161,7 @@
# will be allowed to connect to policyd. leaving this blank causes
# policyd to reject all connection attempts.
#
-CONN_ACL="127.0.0.1 192.168.0.0/24"
+CONN_ACL="::1 127.0.0.1 192.168.0.0/24"
#####################################################################
@@ -490,8 +497,8 @@
#
# greylist host address: default: off
#
-# by default policyd will only use 3 octets when dealing
-# with greylisting information. this allows policyd to
+# by default policyd will only use 3 octets of an IPv4 address when
+# dealing with greylisting information. this allows policyd to
# work around roaming MTAs which are known to move mail
# between different queues after a 450/temp rejection.
#
@@ -504,6 +511,8 @@
# 3=192.168.0 <- default/recommended
# 4=192.168.0.1
#
+# greylisting currently uses all 128-bits of an IPv6 address
+#
GREYLIST_HOSTADDR=3
#
Only in policyd-v1.80: policyd.c.save
diff -uwr policyd-v1.80.orig/policyd.h policyd-v1.80/policyd.h
--- policyd-v1.80.orig/policyd.h 2006-07-10 04:15:27.000000000 -1000
+++ policyd-v1.80/policyd.h 2007-01-25 02:18:06.000000000 -1000
@@ -220,12 +220,13 @@
int w_tcp_conn_acl (const char *host);
int cidr_ip_match (unsigned long ip, char *range);
pid_t w_fork(void);
-const char *w_inet_ntop(int family, const void *addrptr, char *strptr, size_t
len);
+ const char *w_inet_ntop(struct sockaddr *addrptr, char *strptr, size_t len);
+ uint16_t sockaddrport(struct sockaddr *addrptr);
ssize_t w_read(unsigned int fd, char *ptr);
ssize_t w_write(unsigned int fd, const void *vbuf);
ssize_t f_write(unsigned int volatile fd, const void *vptr, size_t n);
void w_close(unsigned int fd);
- void w_bind(unsigned int fd, const struct sockaddr *sa, socklen_t salen);
+ int w_bind(unsigned int fd, const struct sockaddr *sa, socklen_t salen);
void w_listen(unsigned int fd, unsigned int backlog);
void sigalrm_handler (void);
diff -uwr policyd-v1.80.orig/README.txt policyd-v1.80/README.txt
--- policyd-v1.80.orig/README.txt 2006-07-06 21:18:51.000000000 -1000
+++ policyd-v1.80/README.txt 2007-04-24 19:02:11.000000000 -1000
@@ -293,6 +293,10 @@
Please ensure that it matches your policyd.conf settings
for BINDHOST and BINDPORT.
+ If policyd is listening on an IPv6 address you should surround the IPv6
+ address with brackets in main.cf.
+
+ check_policy_service inet:[::1]:10031
###
diff -uwr policyd-v1.80.orig/sockets.c policyd-v1.80/sockets.c
--- policyd-v1.80.orig/sockets.c 2006-07-03 22:04:48.000000000 -1000
+++ policyd-v1.80/sockets.c 2007-04-24 20:12:22.000000000 -1000
@@ -100,10 +100,11 @@
* function: w_bind (wrapper_bind)
* purpose: wrapper for bind()
*/
-void
+int
w_bind(unsigned int fd, const struct sockaddr *sa, socklen_t salen)
{
u_int yes=1;
+ int n;
/* set options on socket (reuse port/address) */
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
@@ -113,11 +114,11 @@
}
/* bind a name to a socket */
- if (bind(fd, sa, salen) < 0)
+ if ( (n = bind(fd, sa, salen)) < 0)
{
- logmessage("fatal: bind(): %s\n", strerror(errno));
- exit(-1);
+ logmessage("warning: bind(): %s\n", strerror(errno));
}
+ return( n );
}
@@ -307,6 +308,7 @@
/*
+ * XXX Currently not used. Needs updating to handle IPv6 addresses.
* function: w_tcp_conn_acl
* purpose: check if connecting host is allowed
* return: 0=allow, -1=disallow
@@ -356,8 +358,7 @@
/* create an endpoint for communication */
if ((n = socket(family, type, protocol)) < 0)
{
- logmessage("fatal: socket(): %s\n", strerror(errno));
- exit(-1);
+ logmessage("warning: socket(): %s\n", strerror(errno));
}
return (n);
@@ -366,22 +367,26 @@
/*
- * function: w_socket (wrapper_socket)
+ * function: w_inet_ntop (wrapper_ntop)
* purpose:
- * return: file descriptor
+ * return: string pointer
*/
const char *
-w_inet_ntop(int family, const void *addrptr, char *strptr, size_t len)
+w_inet_ntop(struct sockaddr *saptr, char *strptr, size_t len)
{
+ const void *addrptr;
const char *ptr;
if (strptr == NULL) /* check for old code */
{
- logmessage("fatal: NULL 3rd argument to w_inet_ntop");
+ logmessage("fatal: NULL 2nd argument to w_inet_ntop");
exit(-1);
}
-
- if ((ptr=inet_ntop(family, addrptr, strptr, len)) == NULL)
+ if ( saptr->sa_family == AF_INET )
+ addrptr = (void *) &(((struct sockaddr_in *) saptr)->sin_addr);
+ else
+ addrptr = (void *) &(((struct sockaddr_in6 *) saptr)->sin6_addr);
+ if ((ptr=inet_ntop(saptr->sa_family, addrptr, strptr, len)) == NULL)
{
logmessage("fatal: inet_ntop(): %s\n", strerror(errno));
exit(-1);
@@ -390,6 +395,20 @@
return(ptr);
}
+/*
+ * function: sockaddrport
+ * purpose:
+ * return: port
+ */
+uint16_t
+sockaddrport(struct sockaddr *saptr)
+{
+ if ( saptr->sa_family == AF_INET )
+ return( ntohs( ((struct sockaddr_in *) saptr)->sin_port ) );
+ else
+ return( ntohs( ((struct sockaddr_in6 *) saptr)->sin6_port ) );
+}
+
diff -uwr policyd-v1.80.orig/TODO policyd-v1.80/TODO
--- policyd-v1.80.orig/TODO 2006-07-03 22:31:17.000000000 -1000
+++ policyd-v1.80/TODO 2007-04-24 20:13:51.000000000 -1000
@@ -1,3 +1,4 @@
* pgsql support
* database restructuring so that database is portable
* load conn ACL into multi-dimensional arrays
+ * fixup extract_ip(), extract_ipfill() to correctly handle IPv6 subnets
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
policyd-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/policyd-users