This is a multi-part message in MIME format.
--
Alexander Sabourenkov wrote:
> Hello.
>
> This patch against 4.20 adds the saslauthd condition, which is like
> pwcheck, but not entirely.
Damn. Forgot to attach the file.
--
./lxnt
--
Index: src/auths/call_pwcheck.c
===================================================================
--- src/auths/call_pwcheck.c (revision 319)
+++ src/auths/call_pwcheck.c (working copy)
@@ -13,7 +13,7 @@
* External entry point *
*************************************************/
-/* This function calls the Cyrus-SASL authentication daemon, passing over a
+/* This function calls a Cyrus-SASL authentication daemon, pwcheck, passing over a
colon-separated user name and password. As this is called from the string
expander, the string will always be in dynamic store and can be overwritten.
@@ -50,7 +50,7 @@
DEBUG(D_auth) debug_printf("pwcheck: success (%s)\n", reply);
return OK;
- case PWCHECK_BAD:
+ case PWCHECK_NO:
DEBUG(D_auth) debug_printf("pwcheck: access denied (%s)\n", reply);
return FAIL;
@@ -61,4 +61,56 @@
}
}
+/*************************************************
+* External entry point *
+*************************************************/
+
+/* This function calls a Cyrus-SASL authentication daemon, saslauthd,
+As this is called from the string expander, all the strings will always
+be in dynamic store and can be overwritten.
+
+Arguments:
+ username username
+ password password
+ service optional service
+ realm optional realm
+ errptr where to point an error message
+
+Returns: OK if authentication succeeded
+ FAIL if authentication failed
+ ERROR some other error condition
+*/
+
+
+int
+auth_call_saslauthd(uschar *username, uschar *password, uschar *service, uschar *realm, uschar **errptr)
+{
+uschar *reply = NULL;
+
+if (service == NULL)
+ service = "";
+if (realm == NULL)
+ realm = "";
+
+DEBUG(D_auth)
+ debug_printf("Running saslauthd authentication for user \"%s\" \n", username);
+
+switch (saslauthd_verify_password(CS username, CS password, CS service, CS realm, (const char **)(&reply)))
+ {
+ case PWCHECK_OK:
+ DEBUG(D_auth) debug_printf("saslauthd: success (%s)\n", reply);
+ return OK;
+
+ case PWCHECK_NO:
+ DEBUG(D_auth) debug_printf("saslauthd: access denied (%s)\n", reply);
+ return FAIL;
+
+ default:
+ DEBUG(D_auth) debug_printf("saslauthd: query failed (%s)\n", reply);
+ *errptr = reply;
+ return ERROR;
+ }
+}
+
+
/* End of call_pwcheck.c */
Index: src/auths/pwcheck.c
===================================================================
--- src/auths/pwcheck.c (revision 319)
+++ src/auths/pwcheck.c (working copy)
@@ -45,7 +45,7 @@
/*
* Taken from Cyrus-SASL library and adapted by Alexander S. Sabourenkov
- * Oct 2001 - Apr 2002. Slightly modified by Philip Hazel.
+ * Oct 2001 - Apr 2003. Slightly modified by Philip Hazel.
*
* screwdriver@???
*
@@ -55,6 +55,17 @@
#include "../exim.h"
#include "pwcheck.h"
+#if defined(CYRUS_PWCHECK_SOCKET) || defined(CYRUS_SASLAUTHD_SOCKET)
+
+#include <sys/uio.h>
+
+static int retry_read(int, void *, unsigned );
+static int retry_writev(int, struct iovec *, int );
+static int read_string(int, uschar **);
+static int write_string(int, const uschar *, int);
+
+#endif
+
/* A dummy function that always fails if pwcheck support is not
wanted. */
@@ -73,84 +84,22 @@
/* This is the real function */
#else
-#include <sys/uio.h>
-
/* taken from cyrus-sasl file checkpw.c */
- /*
- * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
- * until all the data is written out or an error occurs.
- */
- static int retry_writev(int fd, struct iovec *iov, int iovcnt)
- {
- int n;
- int i;
- int written = 0;
- static int iov_max =
- #ifdef MAXIOV
- MAXIOV
- #else
- #ifdef IOV_MAX
- IOV_MAX
- #else
- 8192
- #endif
- #endif
- ;
-
- for (;;) {
- while (iovcnt && iov[0].iov_len == 0) {
- iov++;
- iovcnt--;
- }
-
- if (!iovcnt) return written;
-
- n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
- if (n == -1) {
- if (errno == EINVAL && iov_max > 10) {
- iov_max /= 2;
- continue;
- }
- if (errno == EINTR) continue;
- return -1;
- }
-
- written += n;
-
- for (i = 0; i < iovcnt; i++) {
- if (iov[i].iov_len > n) {
- iov[i].iov_base = (char *)iov[i].iov_base + n;
- iov[i].iov_len -= n;
- break;
- }
- n -= iov[i].iov_len;
- iov[i].iov_len = 0;
- }
-
- if (i == iovcnt) return written;
- }
- }
-
-
- /* taken from cyrus-sasl file checkpw.c */
/* pwcheck daemon-authenticated login */
int pwcheck_verify_password(const char *userid,
const char *passwd,
const char **reply)
{
- int s;
+ int s, start, r, n;
struct sockaddr_un srvaddr;
- int r;
- struct iovec iov[10];
+ struct iovec iov[2];
static char response[1024];
- int start, n;
- /* char pwpath[1024]; is not used - PH */
if (reply) { *reply = NULL; }
s = socket(AF_UNIX, SOCK_STREAM, 0);
- if (s == -1) return errno;
+ if (s == -1) { return PWCHECK_FAIL; }
memset((char *)&srvaddr, 0, sizeof(srvaddr));
srvaddr.sun_family = AF_UNIX;
@@ -185,9 +134,307 @@
response[start] = '\0';
if (reply) { *reply = response; }
- return PWCHECK_BAD;
+ return PWCHECK_NO;
}
#endif
+
+ /* A dummy function that always fails if saslauthd support is not
+wanted. */
+
+#ifndef CYRUS_SASLAUTHD_SOCKET
+int saslauthd_verify_password(const char *userid,
+ const char *passwd,
+ const char *service,
+ const char *realm,
+ const char **reply)
+{
+userid = userid; /* Keep picky compilers happy */
+passwd = passwd;
+service = service;
+realm = realm;
+*reply = "saslauthd support is not included in this Exim binary";
+return PWCHECK_FAIL;
+}
+
+
+/* This is the real function */
+
+#else
+ /* written from scratch */
+ /* saslauthd daemon-authenticated login */
+
+int saslauthd_verify_password(const char *userid,
+ const char *password,
+ const char *service,
+ const char *realm,
+ const char **reply)
+{
+ uschar *daemon_reply;
+ int s, r;
+ struct sockaddr_un srvaddr;
+
+ DEBUG(D_auth)
+ debug_printf("saslauthd userid='%s' password='%s' servicename='%s'"
+ " realm='%s'\n", userid, password, service, realm );
+
+ if (reply)
+ *reply = NULL;
+
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (s == -1) {
+ if (reply)
+ *reply = strerror(errno);
+ return PWCHECK_FAIL;
+ }
+
+ memset((char *)&srvaddr, 0, sizeof(srvaddr));
+ srvaddr.sun_family = AF_UNIX;
+ strncpy(srvaddr.sun_path, CYRUS_SASLAUTHD_SOCKET,
+ sizeof(srvaddr.sun_path));
+ r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
+ if (r == -1) {
+ DEBUG(D_auth)
+ debug_printf("Cannot connect to saslauthd daemon (at '%s'): %s\n",
+ CYRUS_SASLAUTHD_SOCKET, strerror(errno));
+ if (reply)
+ *reply = string_sprintf("cannot connect to saslauthd daemon at "
+ "%s: %s", strerror(errno));
+ return PWCHECK_FAIL;
+ }
+
+ if ( write_string(s, userid, strlen(userid)) < 0) {
+ DEBUG(D_auth)
+ debug_printf("Failed to send userid to saslauthd daemon \n");
+ close(s);
+ return PWCHECK_FAIL;
+ }
+
+ if ( write_string(s, password, strlen(password)) < 0) {
+ DEBUG(D_auth)
+ debug_printf("Failed to send password to saslauthd daemon \n");
+ close(s);
+ return PWCHECK_FAIL;
+ }
+
+ memset(password, 0, strlen(password));
+
+ if ( write_string(s, service, strlen(service)) < 0) {
+ DEBUG(D_auth)
+ debug_printf("Failed to send service name to saslauthd daemon \n");
+ close(s);
+ return PWCHECK_FAIL;
+ }
+
+ if ( write_string(s, realm, strlen(realm)) < 0) {
+ DEBUG(D_auth)
+ debug_printf("Failed to send realm to saslauthd daemon \n");
+ close(s);
+ return PWCHECK_FAIL;
+ }
+
+ if ( read_string(s, &daemon_reply ) < 2) {
+ DEBUG(D_auth)
+ debug_printf("Corrupted answer '%s' received. \n", daemon_reply);
+ close(s);
+ return PWCHECK_FAIL;
+ }
+
+ close(s);
+
+ DEBUG(D_auth)
+ debug_printf("Answer '%s' received. \n", daemon_reply);
+
+ *reply = daemon_reply;
+
+ if ( (daemon_reply[0] == 'O') && (daemon_reply[1] == 'K') )
+ return PWCHECK_OK;
+
+ if ( (daemon_reply[0] == 'N') && (daemon_reply[1] == 'O') )
+ return PWCHECK_NO;
+
+ return PWCHECK_FAIL;
+}
+
+#endif
+
+
+/* helper functions */
+#if defined(CYRUS_PWCHECK_SOCKET) || defined(CYRUS_SASLAUTHD_SOCKET)
+
+#define MAX_REQ_LEN 1024
+
+/* written from scratch */
+
+/* FUNCTION: read_string */
+
+/* SYNOPSIS
+ * read a sasld-style counted string into
+ * store-allocated buffer, set pointer to the buffer,
+ * return number of bytes read or -1 on failure.
+ * END SYNOPSIS */
+
+static int read_string(int fd, uschar **retval) {
+ unsigned short count;
+ int rc;
+
+ rc = (retry_read(fd, &count, sizeof(count)) < (int) sizeof(count));
+ if (!rc) {
+ count = ntohs(count);
+ if (count > MAX_REQ_LEN) {
+ return -1;
+ } else {
+ *retval = store_get(count + 1);
+ rc = (retry_read(fd, *retval, count) < (int) count);
+ (*retval)[count] = '\0';
+ return count;
+ }
+ }
+}
+
+
+/* FUNCTION: write_string */
+
+/* SYNOPSIS
+ * write a sasld-style counted string into given fd
+ * written bytes on success, -1 on failure.
+ * END SYNOPSIS */
+
+static int write_string(int fd, const uschar *string, int len) {
+ unsigned short count;
+ int rc;
+ struct iovec iov[2];
+
+ count = htons(len);
+
+ iov[0].iov_base = (void *) &count;
+ iov[0].iov_len = sizeof(count);
+ iov[1].iov_base = (void *) string;
+ iov[1].iov_len = len;
+
+ rc = retry_writev(fd, iov, 2);
+
+ return rc;
+}
+
+
+/* taken from cyrus-sasl file saslauthd/saslauthd-unix.c */
+
+/* FUNCTION: retry_read */
+
+/* SYNOPSIS
+ * Keep calling the read() system call with 'fd', 'buf', and 'nbyte'
+ * until all the data is read in or an error occurs.
+ * END SYNOPSIS */
+static int retry_read(int fd, void *inbuf, unsigned nbyte)
+{
+ int n;
+ int nread = 0;
+ char *buf = (char *)inbuf;
+
+ if (nbyte == 0) return 0;
+
+ for (;;) {
+ n = read(fd, buf, nbyte);
+ if (n == 0) {
+ /* end of file */
+ return -1;
+ }
+ if (n == -1) {
+ if (errno == EINTR) continue;
+ return -1;
+ }
+
+ nread += n;
+
+ if (n >= (int) nbyte) return nread;
+
+ buf += n;
+ nbyte -= n;
+ }
+}
+
+/* END FUNCTION: retry_read */
+
+/* FUNCTION: retry_writev */
+
+/* SYNOPSIS
+ * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
+ * until all the data is written out or an error occurs.
+ * END SYNOPSIS */
+
+static int /* R: bytes written, or -1 on error */
+retry_writev (
+ /* PARAMETERS */
+ int fd, /* I: fd to write on */
+ struct iovec *iov, /* U: iovec array base
+ * modified as data written */
+ int iovcnt /* I: number of iovec entries */
+ /* END PARAMETERS */
+ )
+{
+ /* VARIABLES */
+ int n; /* return value from writev() */
+ int i; /* loop counter */
+ int written; /* bytes written so far */
+ static int iov_max; /* max number of iovec entries */
+ /* END VARIABLES */
+
+ /* initialization */
+#ifdef MAXIOV
+ iov_max = MAXIOV;
+#else /* ! MAXIOV */
+# ifdef IOV_MAX
+ iov_max = IOV_MAX;
+# else /* ! IOV_MAX */
+ iov_max = 8192;
+# endif /* ! IOV_MAX */
+#endif /* ! MAXIOV */
+ written = 0;
+
+ for (;;) {
+
+ while (iovcnt && iov[0].iov_len == 0) {
+ iov++;
+ iovcnt--;
+ }
+
+ if (!iovcnt) {
+ return written;
+ }
+
+ n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
+ if (n == -1) {
+ if (errno == EINVAL && iov_max > 10) {
+ iov_max /= 2;
+ continue;
+ }
+ if (errno == EINTR) {
+ continue;
+ }
+ return -1;
+ } else {
+ written += n;
+ }
+
+ for (i = 0; i < iovcnt; i++) {
+ if (iov[i].iov_len > (unsigned) n) {
+ iov[i].iov_base = (char *)iov[i].iov_base + n;
+ iov[i].iov_len -= n;
+ break;
+ }
+ n -= iov[i].iov_len;
+ iov[i].iov_len = 0;
+ }
+
+ if (i == iovcnt) {
+ return written;
+ }
+ }
+ /* NOTREACHED */
+}
+
+/* END FUNCTION: retry_writev */
+#endif
/* End of auths/pwcheck.c */
Index: src/auths/pwcheck.h
===================================================================
--- src/auths/pwcheck.h (revision 319)
+++ src/auths/pwcheck.h (working copy)
@@ -9,13 +9,18 @@
daemon. */
/* Error codes */
+/* OK - auth successful
+ NO - access denied
+ FAIL - [temporary] failure */
+
#define PWCHECK_OK 0
-#define PWCHECK_BAD 1
+#define PWCHECK_NO 1
#define PWCHECK_FAIL 2
-/* Cyrus function for doing the business */
+/* Cyrus functions for doing the business */
extern int pwcheck_verify_password(const char *, const char *, const char **);
+extern int saslauthd_verify_password(const char *, const char *, const char *, const char *, const char **);
/* End of pwcheck.h */
Index: src/functions.h
===================================================================
--- src/functions.h (revision 319)
+++ src/functions.h (working copy)
@@ -42,6 +42,7 @@
extern int auth_b64decode(uschar *, uschar **);
extern int auth_call_pam(uschar *, uschar **);
extern int auth_call_pwcheck(uschar *, uschar **);
+extern int auth_call_saslauthd(uschar *, uschar *, uschar *, uschar *, uschar **);
extern int auth_call_radius(uschar *, uschar **);
extern int auth_get_data(uschar **, uschar *);
extern int auth_get_no64_data(uschar **, uschar *);
Index: src/EDITME
===================================================================
--- src/EDITME (revision 319)
+++ src/EDITME (working copy)
@@ -523,6 +523,20 @@
#------------------------------------------------------------------------------
+# Support for authentication via the Cyrus SASL saslauthd daemon is available.
+# The Exim support, which is intented for use in conjunction with the SMTP AUTH
+# facilities, is included only when requested by setting the following
+# parameter to the location of the saslauthd daemon's socket directory.
+#
+# There is no need to install all of SASL on your system. You just need to run
+# ./configure --with-saslauthd, cd to the saslauthd directory with sources, make
+# and make install. You must create the socket directory (default /var/saslauthd)
+# and chown it to exim's user and group. Once you have installed saslauthd, you
+# should arrange for it to be started by root at boot time.
+
+# CYRUS_SASLAUTHD_SOCKET=/var/saslauthd/saslauthd
+
+#------------------------------------------------------------------------------
# TCP wrappers: If you want to use tcpwrappers from within Exim, uncomment
# this setting. See the manual section entitled "Use of tcpwrappers" in the
# chapter on building and installing Exim.
Index: src/expand.c
===================================================================
--- src/expand.c (revision 319)
+++ src/expand.c (working copy)
@@ -1271,14 +1271,14 @@
|| Ustrcmp(name, "pam") == 0
#endif /* SUPPORT_PAM */
#ifdef RADIUS_CONFIG_FILE
- || Ustrcmp(name, "radius") == 0
- #endif /* RADIUS_CONFIG_FILE */
+ || Ustrcmp(name, "radius") == 0
+ #endif /* RADIUS_CONFIG_FILE */
#ifdef LOOKUP_LDAP
|| Ustrcmp(name, "ldapauth") == 0
#endif
#ifdef CYRUS_PWCHECK_SOCKET
- || Ustrcmp(name, "pwcheck") == 0
- #endif
+ || Ustrcmp(name, "pwcheck") == 0
+ #endif
)
{
uschar *sub;
@@ -1330,7 +1330,7 @@
#ifdef CYRUS_PWCHECK_SOCKET
if (name[0] == 'p')
- rc = auth_call_pwcheck(sub, &expand_string_message);
+ rc = auth_call_pwcheck(sub, &expand_string_message);
#endif /* CYRUS_PWCHECK_SOCKET */
if (rc == ERROR || rc == DEFER) return NULL;
@@ -1340,6 +1340,57 @@
return s;
}
+#ifdef CYRUS_SASLAUTHD_SOCKET
+/* saslauthd {{username}{password}[{service}[{realm}]]} */
+else if ( Ustrcmp(name, "saslauthd") == 0)
+ {
+ int i = 0;
+ int sub_number = 0;
+ int sub_start_idx = 0;
+ const uschar *rerror;
+ uschar *sub[4];
+ int inside_curly = 0;
+ int esl;
+
+ sub[2] = NULL; sub[3] = NULL;
+
+ while (isspace(*s)) s++;
+ if (*s != '{')
+ goto COND_FAILED_CURLY_START;
+ s++;
+ for (i = 0; i < 4; i++)
+ {
+ while (isspace(*s)) s++;
+ if (*s != '{')
+ {
+ if (i == 0) goto COND_FAILED_CURLY_START;
+ if (i > 1) break;
+ expand_string_message = string_sprintf("missing 2nd string in {} "
+ "after \"%s\"", name);
+ return NULL;
+ }
+ sub[i] = expand_string_internal(s+1, TRUE, &s, yield == NULL);
+ if (sub[i] == NULL) return NULL;
+ if (*s++ != '}') goto COND_FAILED_CURLY_END;
+
+ }
+ while (isspace(*s)) s++;
+ if (*s != '}')
+ goto COND_FAILED_CURLY_END;
+ s++;
+
+ if (yield != NULL)
+ {
+ int rc;
+ rc = auth_call_saslauthd(sub[0], sub[1], sub[2], sub[3], &expand_string_message);
+ if (rc == ERROR || rc == DEFER) return NULL;
+ *yield = (rc == OK) == testfor;
+ }
+ return s;
+}
+
+#endif /* CYRUS_SASLAUTHD_SOCKET */
+
/* eq: tests for string equality
match: does a regular expression match and sets up the numerical
variables if it succeeds
Index: src/config.h.defaults
===================================================================
--- src/config.h.defaults (revision 319)
+++ src/config.h.defaults (working copy)
@@ -23,6 +23,7 @@
#define CONFIGURE_FILE_USE_EUID
#define CONFIGURE_FILE_USE_NODE
#define CYRUS_PWCHECK_SOCKET
+#define CYRUS_SASLAUTHD_SOCKET
#define DEFAULT_CRYPT crypt
#define DELIVER_IN_BUFFER_SIZE 8192
--