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

Reply via email to