Re: [Exim] [PATCH] passwd lookup method

Top Pagina
Delete this message
Reply to this message
Auteur: Colm MacCarthaigh
Datum:  
Aan: exim-users
CC: Colm MacCarthaigh
Onderwerp: Re: [Exim] [PATCH] passwd lookup method
<-- Warning: charset 'unknown-8bit' is not supported -->

--
On Fri, Aug 02, 2002 at 09:58:13AM +0100, Philip Hazel wrote:
> Hmm. I think I must be missing something here. You say you don't want
> that overhead, and yet you've gone and implemented a lookup that does
>
> if ((p = getpwnam(username)) != NULL)
>
> That is *exactly* what check_local_user does. (And it caches the result
> so that it doesn't repeat the lookup later.) Also, depending on your
> operating system, getpwnam() may indeed do a linear search of
> /etc/passwd.
>
> So, what have I missed?


check_local_user does a few things I don't need at that stage I think,
like set up user and groups, the home directory ... etc, they would
just get thrown away. I should definitely be caching the return
though :)

> > So instead I've added a lookup method "passwd",
>
> Full marks for figuring out how to add a lookup. The code looks
> absolutely fine.


the README in lookups/ is excellent

> > amavis:
> > driver = manualroute
> > condition = "${if eq {$interface_port}{10025} {0}{1}}"
> > # if scanning incoming mails, uncomment the following line and
> > # change local_domains accordingly
> > local_parts = dbm;/etc/aliases.db : passwd;$local_part
> > domains = +local_domains
> > transport = amavis
> > route_list = * localhost
> > self = send
>
> Ah. I think I see why you've done that. You want to be able to say "if a
> local user OR one of these users", and you can't do that with
> check_local_user. You would have to use two separate routers.


Yep, this is pretty much one of the main reasons behind it.

> OK, this seems reasonable, but I think two changes should be made:
>
>     1. Why force lower casing? Exim will lowercase $local_part anyway,
>     unless you have set caseful_local_part, and even then you could
>     force it with ${lc.


Didnt realise this at all, *rewrites*

>     2. There's a function called route_finduser() that does getpwnam()
>     lookup in a slightly more sophisticated way, including caching the
>     result of the last lookup. From its heading comment: "Try several times
>     (if configured) to find a local user, in case delays in NIS or NFS
>     whatever cause an incorrect refusal. It's a pity that getpwnam()
>     doesn't have some kind of indication as to why it has failed."


o.k., I've made it use this now, this function wasnt copying the values
of pw_gecos and pw_shell though, so I've changed that, not everything
cares about them, but they should always be there. Posix says the
pw_shell and pw_dir will always be there (POSIX.SysDB, passwd structure)
but makes no mention of the gecos being mandatory, which bothers me.
Even worse, there's no nice way to find out if it's supported on a
system, which is just plain upsetting.


>     3. Why return the user name? Surely it would be more useful (in
>     general) to return the password data as a sequence of name=value
>     fields? In your usage above, it wouldn't make any difference.


I've modified it now to cache the query using route_finduser, it
will return a string now which is in the format :

name=username uid=NN gid=NN dir=xxxx shell=xxxx gecos=xxxx

all values will be quoted and escaped as required, all of the
gecos relevant details will only happen if the HAVE_PW_GECOS
macro is defined.

How to go about defining this macro is another thing. The most
correct thing to do would be to go though every system there
is a header for and determine whether they have it or not,
this would take quiet some time. An alternative option would
be to have it user-configurable in Local/Makefile, which could
be confusing to some people, and there's also the possibility
of automagic detection in scripts/Configure-Makefile.

Whether that road is worth threading I don't know. So
I've attachted a patch which does everything except decide
what way to define HAVE_PW_GECOS and document the router,
I would document it, but I can't seem to find the original
TeX (or whatever :)

I've also attatched changes which could be made to either src/EDITME
or scripts/Configure-Makefile, or OS/ , each is pretty much mutually
exclusive, and the patch to OS/ limited to platforms I have access
to first hand.

The code used for escaping/quoting strings was taken from pgsql.c
but I think it has a small bug in that an original string of say
fieldname="foobar would be set exactly as that. When I think it
should be fieldname="\"foobar", the change is trivial, but it
may be worth a look :)

*goes to do some very mad routing based on the gecos field*

--
Colm MacC?rthaigh
--
diff -uNr exim-4.10/scripts/MakeLinks exim-4.10.passwd/scripts/MakeLinks
--- exim-4.10/scripts/MakeLinks    Mon Jul 22 09:59:47 2002
+++ exim-4.10.passwd/scripts/MakeLinks    Thu Aug  1 16:04:00 2002
@@ -60,6 +60,8 @@
 ln -s ../../src/lookups/nisplus.c        nisplus.c
 ln -s ../../src/lookups/oracle.h         oracle.h
 ln -s ../../src/lookups/oracle.c         oracle.c
+ln -s ../../src/lookups/passwd.h         passwd.h
+ln -s ../../src/lookups/passwd.c         passwd.c
 ln -s ../../src/lookups/pgsql.h          pgsql.h
 ln -s ../../src/lookups/pgsql.c          pgsql.c
 ln -s ../../src/lookups/testdb.h         testdb.h
diff -uNr exim-4.10/src/EDITME exim-4.10.passwd/src/EDITME
--- exim-4.10/src/EDITME    Mon Jul 22 09:59:47 2002
+++ exim-4.10.passwd/src/EDITME    Fri Aug  2 13:15:36 2002
@@ -241,6 +241,7 @@
 # LOOKUP_NIS=yes
 # LOOKUP_NISPLUS=yes
 # LOOKUP_ORACLE=yes
+# LOOKUP_PASSWD=yes
 # LOOKUP_PGSQL=yes
 # LOOKUP_WHOSON=yes


@@ -271,7 +272,6 @@

# LOOKUP_INCLUDE=-I /usr/local/ldap/include -I /usr/local/mysql/include -I /usr/local/pgsql/include
# LOOKUP_LIBS=-L/usr/local/lib -lldap -llber -lmysqlclient -lpq
-

 #------------------------------------------------------------------------------
 # Compiling the Exim monitor: If you want to compile the Exim monitor, a
diff -uNr exim-4.10/src/config.h.defaults exim-4.10.passwd/src/config.h.defaults
--- exim-4.10/src/config.h.defaults    Mon Jul 22 09:59:48 2002
+++ exim-4.10.passwd/src/config.h.defaults    Fri Aug  2 13:21:16 2002
@@ -58,9 +58,12 @@
 #define LOOKUP_NIS
 #define LOOKUP_NISPLUS
 #define LOOKUP_ORACLE
+#define LOOKUP_PASSWD
 #define LOOKUP_PGSQL
 #define LOOKUP_TESTDB
 #define LOOKUP_WHOSON
+
+#define HAVE_PW_GECOS


 #define MAX_FILTER_SIZE           (1024*1024)
 #define MAX_LOCALHOST_NUMBER        256
diff -uNr exim-4.10/src/drtables.c exim-4.10.passwd/src/drtables.c
--- exim-4.10/src/drtables.c    Mon Jul 22 09:59:48 2002
+++ exim-4.10.passwd/src/drtables.c    Thu Aug  1 16:04:00 2002
@@ -75,6 +75,10 @@
 #include "lookups/oracle.h"
 #endif


+#ifdef LOOKUP_PASSWD
+#include "lookups/passwd.h"
+#endif
+
 #ifdef LOOKUP_PGSQL
 #include "lookups/pgsql.h"
 #endif
@@ -333,6 +337,23 @@
   NULL,                          /* no close function */
   oracle_tidy,                   /* tidy function */
   oracle_quote                   /* quoting function */
+#else
+  NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
+#endif
+  },
+
+/* PASSWD lookup, */
+
+  {
+  US"passwd",                    /* lookup name */
+  lookup_querystyle,             /* query-style lookup */
+#ifdef LOOKUP_PASSWD
+  passwd_open,                   /* open function */
+  NULL,                          /* check function */
+  passwd_find,                   /* find function */
+  NULL,                          /* no close function */
+  NULL,                          /* no tidy function */
+  NULL                           /* no quoting function */
 #else
   NULL, NULL, NULL, NULL, NULL, NULL /* lookup not present */
 #endif
diff -uNr exim-4.10/src/lookups/Makefile exim-4.10.passwd/src/lookups/Makefile
--- exim-4.10/src/lookups/Makefile    Mon Jul 22 09:59:49 2002
+++ exim-4.10.passwd/src/lookups/Makefile    Thu Aug  1 16:04:00 2002
@@ -4,7 +4,8 @@
 # defined, dummy modules get compiled.


 OBJ = cdb.o dbmdb.o dnsdb.o dsearch.o ldap.o lsearch.o mysql.o nis.o \
-      nisplus.o oracle.o pgsql.o testdb.o whoson.o lf_check_file.o
+      nisplus.o oracle.o passwd.o pgsql.o testdb.o whoson.o \
+      lf_check_file.o


 lookups.a:       $(OBJ)
          /bin/rm -f lookups.a
@@ -27,6 +28,7 @@
 nis.o:           $(HDRS) nis.c       nis.h
 nisplus.o:       $(HDRS) nisplus.c   nisplus.h
 oracle.o:        $(HDRS) oracle.c    oracle.h
+passwd.o:        $(HDRS) passwd.c    passwd.h
 pgsql.o:         $(HDRS) pgsql.c     pgsql.h
 testdb.o:        $(HDRS) testdb.c    testdb.h
 whoson.o:        $(HDRS) whoson.c    whoson.h
diff -uNr exim-4.10/src/lookups/passwd.c exim-4.10.passwd/src/lookups/passwd.c
--- exim-4.10/src/lookups/passwd.c    Thu Jan  1 01:00:00 1970
+++ exim-4.10.passwd/src/lookups/passwd.c    Fri Aug  2 12:50:38 2002
@@ -0,0 +1,167 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* Copyright (c) University of Cambridge 1995 - 2002 */
+/* See the file NOTICE for conditions of use and distribution. */
+
+#include "../exim.h"
+#include "passwd.h"
+
+/* We can't just compile this code and allow the library mechanism to omit the
+functions if they are not wanted, because we need to have the NIS header
+available for compiling. Therefore, compile these functions only if LOOKUP_PASSWD
+is defined. However, some compilers don't like compiling empty modules, so keep
+them happy with a dummy when skipping the rest. Make it reference itself to
+stop picky compilers complaining that it is unused, and put in a dummy argument
+to stop even pickier compilers complaining about infinite loops. */
+
+#ifndef LOOKUP_PASSWD
+static void dummy(int x) { dummy(x-1); }
+#else
+
+#include <sys/types.h>
+#include <pwd.h>
+
+/*************************************************
+*              Open entry point                  *
+*************************************************/
+
+/* See local README for interface description. */
+
+void *
+passwd_open(uschar *filename, uschar **errmsg)
+{
+return (void *)(1); /* Just return something non-null */
+}
+
+
+/*************************************************
+*          Find entry point for passwd           *
+*************************************************/
+
+/* See local README for interface description. */
+
+int
+passwd_find(void *handle, uschar *filename, uschar *query, int length,
+  uschar **result, uschar **errmsg)
+{
+struct passwd * pw;
+
+/* check if it's a local user, we set return_uid to NULL to eliminate
+numerical uid's, we only want usernames */
+if (route_finduser(query, &pw, NULL) == TRUE)
+  {
+  if (pw == NULL)
+    {
+    *errmsg = US"passwd lookup failed";
+    return DEFER;
+    }
+  else
+    {
+    int ssize = 0;
+    int offset = 0;
+    /* POSIX_SYS_DB says username must be set. According to POSIX we can ignore
+    the possibilities of spaces, quotes and slashes in usernames. Some systems
+    such as BSD allow these, but that's an admin issue. */
+    if (pw->pw_name == NULL)
+      {
+        *errmsg = US"passwd lookup failed";
+        return DEFER;
+      }
+    else
+      {
+        *result = string_sprintf("name=%s uid=%ld gid=%ld", pw->pw_name, pw->pw_uid, pw->pw_gid);
+      }
+
+    /* POSIX says to use NULL for empty values */
+    if(pw->pw_dir == NULL)
+      {
+      pw->pw_dir = US"";
+      }
+
+    /* we quote strings if they have spaces, slashes or quotes in them, code is from
+    pgsql.c , with the only change being that we detect strings which have quotes
+    already. E.G. "foobar becomes "\"foobar" */
+
+    offset = ssize = Ustrlen(*result);
+    /* append pw_dir and quote if neccessary */
+    *result = string_cat(*result, &ssize , &offset, US" pw_dir=", 8);
+    if(*pw->pw_dir == 0 || (Ustrchr(pw->pw_dir, ' ' ) != NULL) || (Ustrchr(pw->pw_dir, '\"') != NULL))
+      {
+      int k, t_len = Ustrlen(pw->pw_dir);
+      *result = string_cat(*result, &ssize, &offset, US"\"", 1);
+      for(k = 0; k < t_len; k++)
+        {
+        if (pw->pw_dir[k] == '\"' || pw->pw_dir[k] == '\\')
+           *result = string_cat(*result, &ssize, &offset, US"\\", 1);
+        *result = string_cat(*result, &ssize, &offset, pw->pw_dir+k, 1);
+        }
+      *result = string_cat(*result, &ssize, &offset, US"\"", 1);
+      }
+    else
+      {
+      *result = string_cat(*result, &ssize, &offset, pw->pw_dir, Ustrlen(pw->pw_dir));
+      }
+
+
+    if(pw->pw_shell == NULL)
+      {
+      pw->pw_shell = US"";
+      }
+
+    /* append pw_shell and quote if neccessary */
+    *result = string_cat(*result, &ssize , &offset, US" pw_shell=", 10);
+    if(*pw->pw_shell == 0 || (Ustrchr(pw->pw_shell, ' ') != NULL) || (Ustrchr(pw->pw_shell, '\"') != NULL))
+      {
+      int k, t_len = Ustrlen(pw->pw_shell);
+      *result = string_cat(*result, &ssize, &offset, US"\"", 1);
+      for(k = 0; k < t_len; k++)
+        {
+        if (pw->pw_shell[k] == '\"' || pw->pw_shell[k] == '\\')
+           *result = string_cat(*result, &ssize, &offset, US"\\", 1);
+        *result = string_cat(*result, &ssize, &offset, pw->pw_shell+k, 1);
+        }
+      *result = string_cat(*result, &ssize, &offset, US"\"", 1);
+      }
+    else
+      {
+      *result = string_cat(*result, &ssize, &offset, pw->pw_shell, Ustrlen(pw->pw_shell));
+      }
+
+#ifdef HAVE_PW_GECOS
+    if(pw->pw_gecos == NULL)
+      {
+      pw->pw_gecos = US"";
+      }
+
+    /* append pw_gecos and quote if neccessary */
+    *result = string_cat(*result, &ssize , &offset, US" pw_gecos=", 10);
+    if(*pw->pw_gecos == 0 || (Ustrchr(pw->pw_gecos, ' ') != NULL) || (Ustrchr(pw->pw_gecos, '\"') != NULL))
+      {
+      int k, t_len = Ustrlen(pw->pw_gecos);
+      *result = string_cat(*result, &ssize, &offset, US"\"", 1);
+      for(k = 0; k < t_len; k++)
+        {
+        if (pw->pw_gecos[k] == '\"' || pw->pw_gecos[k] == '\\')
+           *result = string_cat(*result, &ssize, &offset, US"\\", 1);
+        *result = string_cat(*result, &ssize, &offset, pw->pw_gecos+k, 1);
+        }
+      *result = string_cat(*result, &ssize, &offset, US"\"", 1);
+      }
+    else
+      {
+      *result = string_cat(*result, &ssize, &offset, pw->pw_gecos, Ustrlen(pw->pw_gecos));
+      }
+#endif
+    }
+
+  return OK;
+  }
+
+return FAIL;
+}
+
+#endif  /* LOOKUP_PASSWD */
+
+/* End of lookups/passwd.c */
diff -uNr exim-4.10/src/lookups/passwd.h exim-4.10.passwd/src/lookups/passwd.h
--- exim-4.10/src/lookups/passwd.h    Thu Jan  1 01:00:00 1970
+++ exim-4.10.passwd/src/lookups/passwd.h    Thu Aug  1 16:04:00 2002
@@ -0,0 +1,13 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* Copyright (c) University of Cambridge 1995 - 2002 */
+/* See the file NOTICE for conditions of use and distribution. */
+
+/* Header for the passwd lookup */
+
+extern void *passwd_open(uschar *, uschar **);
+extern int   passwd_find(void *, uschar *, uschar *, int, uschar **, uschar **);
+
+/* End of lookups/passwd.h */
diff -uNr exim-4.10/src/route.c exim-4.10.passwd/src/route.c
--- exim-4.10/src/route.c    Mon Jul 22 09:59:50 2002
+++ exim-4.10.passwd/src/route.c    Fri Aug  2 12:53:58 2002
@@ -937,6 +937,10 @@
     lastdir[sizeof(lastdir)-1] = 0;          /* just in case */
     pwcopy.pw_name = CS lastname;
     pwcopy.pw_dir = CS lastdir;
+#ifdef HAVE_PW_GECOS
+    pwcopy.pw_gecos = lastpw->pw_gecos;
+#endif
+    pwcopy.pw_shell = lastpw->pw_shell;
     lastpw = &pwcopy;
     }
   }
--
--- exim-4.10/src/EDITME    Mon Jul 22 09:59:47 2002
+++ exim-4.10.passwd/src/EDITME    Fri Aug  2 13:40:18 2002
@@ -241,6 +241,7 @@
 # LOOKUP_NIS=yes
 # LOOKUP_NISPLUS=yes
 # LOOKUP_ORACLE=yes
+# LOOKUP_PASSWD=yes
 # LOOKUP_PGSQL=yes
 # LOOKUP_WHOSON=yes


@@ -272,6 +273,12 @@
# LOOKUP_INCLUDE=-I /usr/local/ldap/include -I /usr/local/mysql/include -I /usr/local/pgsql/include
# LOOKUP_LIBS=-L/usr/local/lib -lldap -llber -lmysqlclient -lpq

+#------------------------------------------------------------------------------
+# If you have set LOOKUP_PASSWD=yes, you should set HAVE_PW_GECOS to yes if
+# is available on your system, it's available on almost all systems. If
+# it is not set, the passwd lookup will not return any gecos information.
+
+# HAVE_PW_GECOS=yes

 #------------------------------------------------------------------------------
 # Compiling the Exim monitor: If you want to compile the Exim monitor, a
--
diff -uNr exim-4.10/OS/os.h-FreeBSD exim-4.10.passwd/OS/os.h-FreeBSD
--- exim-4.10/OS/os.h-FreeBSD    Mon Jul 22 09:59:46 2002
+++ exim-4.10.passwd/OS/os.h-FreeBSD    Fri Aug  2 13:42:21 2002
@@ -2,6 +2,7 @@


#define HAVE_BSD_GETLOADAVG
#define HAVE_MMAP
+#define HAVE_PW_GECOS
#define HAVE_SYS_MOUNT_H
#define SIOCGIFCONF_GIVES_ADDR

diff -uNr exim-4.10/OS/os.h-Linux exim-4.10.passwd/OS/os.h-Linux
--- exim-4.10/OS/os.h-Linux    Mon Jul 22 09:59:46 2002
+++ exim-4.10.passwd/OS/os.h-Linux    Fri Aug  2 13:41:28 2002
@@ -3,6 +3,7 @@
 #define CRYPT_H
 #define HAVE_MMAP
 #define HAVE_SYS_VFS_H
+#define HAVE_PW_GECOS
 #define NEED_SYNC_DIRECTORY
 #define NO_IP_VAR_H
 #define GLIBC_IP_OPTIONS
diff -uNr exim-4.10/OS/os.h-Linux-libc5 exim-4.10.passwd/OS/os.h-Linux-libc5
--- exim-4.10/OS/os.h-Linux-libc5    Mon Jul 22 09:59:46 2002
+++ exim-4.10.passwd/OS/os.h-Linux-libc5    Fri Aug  2 13:41:39 2002
@@ -2,6 +2,7 @@


#define HAVE_MMAP
#define HAVE_SYS_VFS_H
+#define HAVE_PW_GECOS
#define NO_IP_VAR_H
#define GLIBC_IP_OPTIONS

diff -uNr exim-4.10/OS/os.h-OSF1 exim-4.10.passwd/OS/os.h-OSF1
--- exim-4.10/OS/os.h-OSF1    Mon Jul 22 09:59:46 2002
+++ exim-4.10.passwd/OS/os.h-OSF1    Fri Aug  2 13:43:08 2002
@@ -1,6 +1,7 @@
 /* Exim: OS-specific C header file for OSF1 */


 #define HAVE_SYS_MOUNT_H
+#define HAVE_PW_GECOS
 #define HAVE_GETIPNODEBYNAME    1


 typedef struct flock flock_t;
diff -uNr exim-4.10/OS/os.h-SunOS5 exim-4.10.passwd/OS/os.h-SunOS5
--- exim-4.10/OS/os.h-SunOS5    Mon Jul 22 09:59:47 2002
+++ exim-4.10.passwd/OS/os.h-SunOS5    Fri Aug  2 13:42:05 2002
@@ -3,6 +3,7 @@
 #define CRYPT_H
 #define HAVE_MMAP
 #define HAVE_SYS_STATVFS_H
+#define HAVE_PW_GECOS
 #define F_FAVAIL                f_favail
 #define SIOCGIFCONF_GIVES_ADDR


--
diff -uNr exim-4.10/scripts/Configure-Makefile exim-4.10.passwd/scripts/Configure-Makefile
--- exim-4.10/scripts/Configure-Makefile    Fri Aug  2 13:17:44 2002
+++ exim-4.10.passwd/scripts/Configure-Makefile    Fri Aug  2 13:20:04 2002
@@ -108,6 +108,9 @@
      fi
 done >> $mft || exit 1


+# Detect pw_gecos support in the system
+grep pw_gecos /usr/include/pwd.h 2>&1 > /dev/null && echo "HAVE_PW_GECOS=yes" >> $mft
+
# See if there is a definition of EXIM_PERL in what we have built so far.
# If so, run Perl to find the default values for PERL_CC, PERL_CCOPTS,
# and PERL_LIBS. These need to be put at the top of the Makefile, so we rename
--