summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2007-12-03 16:29:02 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2007-12-03 16:29:02 +0000
commit0112e3c07a2b8fcfb642ace4b880575f86fcf933 (patch)
tree73690e7b7b793d1d32b56af2bc90a3ba2fa8e8de
parentd2ff515169cd571e5120641bfa9df2d7cb9f4b7c (diff)
downloadmailutils-0112e3c07a2b8fcfb642ace4b880575f86fcf933.tar.gz
mailutils-0112e3c07a2b8fcfb642ace4b880575f86fcf933.tar.bz2
-rw-r--r--ChangeLog2
-rw-r--r--NEWS29
-rw-r--r--imap4d/preauth.c490
3 files changed, 520 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 9958c8ea3..d57d533e1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,9 @@
2007-12-03 Sergey Poznyakoff <gray@gnu.org.ua>
+ * NEWS: Update.
* gnulib.modules: Add des. Sort lines.
* imap4d/Makefile.am (imap4d_SOURCES): Add preauth.c
+ * imap4d/preauth.c: New file.
* imap4d/authenticate.c (imap4d_authenticate): Use
imap4d_session_setup.
* imap4d/imap4d.c (imap4d_session_setup)
diff --git a/NEWS b/NEWS
index b1b53bb95..9c753c1d7 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-GNU mailutils NEWS -- history of user-visible changes. 2007-11-30
+GNU mailutils NEWS -- history of user-visible changes. 2007-12-03
Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
See the end of file for copying conditions.
@@ -146,6 +146,33 @@ Previous versions incorrectly understood such an URL as `a/b'
* Fixed APOP handling.
+* imap4d supports PREAUTH mode.
+
+Three mechanisms are provided for authentifying the connection in
+PREAUTH mode:
+
+ 1. stdio - PREAUTH mode is enabled automatically if imap4d is started
+ from command line in interactive mode (-i command line
+ option). The current login name is used as the user name.
+
+ 2. ident - The remote machine is asked about the requester identity
+ using the identification protocol (RFC 1413). Both plaintext and
+ DES encrypted replies are understood.
+
+ 3. prog - Imap4d invokes an external program to authenticate the
+ connection. Four arguments are supplied to the program:
+
+ 1) Remote IP address in dotted-quad notation;
+ 2) Remote port number;
+ 3) Local IP address (currently "0.0.0.0");
+ 4) Local port number.
+
+ If the connection is authenticated, the program should print the
+ user name, followed by a newline character, on its standard
+ output and exit with code 0.
+
+ Otherwise, it shoud exit with a non-zero exit code.
+
* Remove v0.6 compatibility layer.
diff --git a/imap4d/preauth.c b/imap4d/preauth.c
new file mode 100644
index 000000000..790d2bc79
--- /dev/null
+++ b/imap4d/preauth.c
@@ -0,0 +1,490 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2001, 2002, 2003, 2004,
+ 2005, 2006, 2007 Free Software Foundation, Inc.
+
+ GNU Mailutils is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GNU Mailutils is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Mailutils; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301 USA */
+
+/* Preauth support for imap4d */
+
+#include "imap4d.h"
+#include "des.h"
+
+
+/* Stdio preauth */
+
+static char *
+do_preauth_stdio (struct sockaddr_in *pcs)
+{
+ struct passwd *pw = getpwuid (getuid ());
+ return pw ? strdup (pw->pw_name) : NULL;
+}
+
+
+/* IDENT (AUTH, RFC1413) preauth */
+#define USERNAME_C "USERID :"
+
+/* If the reply matches sscanf expression
+
+ "%*[^:]: USERID :%*[^:]:%s"
+
+ returns a malloced copy of the %s part. Otherwise, return NULL. */
+
+static char *
+ident_extract_username (char *reply)
+{
+ char *p;
+
+ p = strchr (reply, ':');
+ if (!p)
+ return NULL;
+ if (p[1] != ' ' || strncmp (p + 2, USERNAME_C, sizeof (USERNAME_C) - 1))
+ return NULL;
+ p += 2 + sizeof (USERNAME_C) - 1;
+ p = strchr (p, ':');
+ if (!p)
+ return NULL;
+ do
+ p++;
+ while (*p == ' ');
+ return p;
+}
+
+static int
+trimcrlf (char *buf)
+{
+ int len = strlen (buf);
+ if (len == 0)
+ return 0;
+ if (buf[len-1] == '\n')
+ {
+ len--;
+ if (buf[len-1] == '\r')
+ len--;
+ buf[len] = 0;
+ }
+ return len;
+}
+
+static int
+is_des_p (const char *name)
+{
+ int len = strlen (name);
+ return len > 1 && name[0] == '[' && name[len-1] == ']';
+}
+
+#define smask(step) ((1<<step)-1)
+#define pstep(x,step) (((x)&smask(step))^(((x)>>step)&smask(step)))
+#define parity_char(x) pstep(pstep(pstep((x),4),2),1)
+
+static void
+des_fixup_key_parity (unsigned char key[8])
+{
+ int i;
+ for (i = 0; i < 8; i++)
+ {
+ key[i] &= 0xfe;
+ key[i] |= 1 ^ parity_char (key[i]);
+ }
+}
+
+static void
+des_cbc_cksum (gl_des_ctx *ctx, unsigned char *buf, size_t bufsize,
+ unsigned char out[8], unsigned char key[8])
+{
+ while (bufsize > 0)
+ {
+ if (bufsize >= 8)
+ {
+ unsigned char *p = key;
+ *p++ ^= *buf++;
+ *p++ ^= *buf++;
+ *p++ ^= *buf++;
+ *p++ ^= *buf++;
+ *p++ ^= *buf++;
+ *p++ ^= *buf++;
+ *p++ ^= *buf++;
+ *p++ ^= *buf++;
+ bufsize -= 8;
+ }
+ else
+ {
+ unsigned char *p = key + bufsize;
+ buf += bufsize;
+ switch (bufsize) {
+ case 7:
+ *--p ^= *--buf;
+ case 6:
+ *--p ^= *--buf;
+ case 5:
+ *--p ^= *--buf;
+ case 4:
+ *--p ^= *--buf;
+ case 3:
+ *--p ^= *--buf;
+ case 2:
+ *--p ^= *--buf;
+ case 1:
+ *--p ^= *--buf;
+ }
+ bufsize = 0;
+ }
+ gl_des_ecb_crypt (ctx, key, key, 0);
+ }
+}
+
+static void
+des_string_to_key (char *buf, size_t bufsize, unsigned char key[8])
+{
+ size_t i;
+ int j;
+ unsigned temp;
+ unsigned char *p;
+ char *p_char;
+ char k_char[64];
+ gl_des_ctx context;
+ char *pstr;
+ int forward = 1;
+ p_char = k_char;
+ memset (k_char, 0, sizeof (k_char));
+
+ /* get next 8 bytes, strip parity, xor */
+ pstr = buf;
+ for (i = 1; i <= bufsize; i++)
+ {
+ /* get next input key byte */
+ temp = (unsigned int) *pstr++;
+ /* loop through bits within byte, ignore parity */
+ for (j = 0; j <= 6; j++)
+ {
+ if (forward)
+ *p_char++ ^= (int) temp & 01;
+ else
+ *--p_char ^= (int) temp & 01;
+ temp = temp >> 1;
+ }
+ while (--j > 0);
+
+ /* check and flip direction */
+ if ((i%8) == 0)
+ forward = !forward;
+ }
+
+ p_char = k_char;
+ p = (unsigned char *) key;
+
+ for (i = 0; i <= 7; i++)
+ {
+ temp = 0;
+ for (j = 0; j <= 6; j++)
+ temp |= *p_char++ << (1 + j);
+ *p++ = (unsigned char) temp;
+ }
+
+ des_fixup_key_parity (key);
+ gl_des_setkey (&context, key);
+ des_cbc_cksum (&context, buf, bufsize, key, key);
+ memset (&context, 0, sizeof context);
+ des_fixup_key_parity (key);
+}
+
+static int
+decode64_buf (const char *name, unsigned char **pbuf, size_t *psize)
+{
+ mu_stream_t str = NULL, flt = NULL;
+ size_t namelen;
+ unsigned char buf[512];
+ size_t size;
+
+ name++;
+ namelen = strlen (name) - 1;
+ mu_memory_stream_create (&str, NULL, MU_STREAM_NO_CHECK);
+ mu_filter_create (&flt, str, "base64", MU_FILTER_DECODE,
+ MU_STREAM_READ | MU_STREAM_NO_CHECK);
+ mu_stream_open (str);
+ mu_stream_sequential_write (str, name, namelen);
+ mu_stream_read (flt, buf, sizeof buf, 0, &size);
+ mu_stream_destroy (&flt, NULL);
+ mu_stream_destroy (&str, NULL);
+ *pbuf = malloc (size);
+ if (!*pbuf)
+ return 1;
+ memcpy (*pbuf, buf, size);
+ *psize = size;
+ return 0;
+}
+
+struct ident_info
+{
+ uint32_t checksum;
+ uint16_t random;
+ uint16_t uid;
+ uint32_t date;
+ uint32_t ip_local;
+ uint32_t ip_remote;
+ uint16_t port_local;
+ uint16_t port_remote;
+};
+
+union ident_data
+{
+ struct ident_info fields;
+ unsigned long longs[6];
+ unsigned char chars[24];
+};
+
+char *
+ident_decrypt (const char *file, const char *name)
+{
+ unsigned char *buf = NULL;
+ size_t size = 0;
+ int fd;
+ char keybuf[1024];
+ union ident_data id;
+
+ if (decode64_buf (name, &buf, &size))
+ return NULL;
+
+ if (size != 24)
+ {
+ mu_diag_output (MU_DIAG_ERROR,
+ _("Incorrect length of IDENT DES packet"));
+ free (buf);
+ return NULL;
+ }
+
+ fd = open (file, O_RDONLY);
+ if (fd < 0)
+ {
+ mu_diag_output (MU_DIAG_ERROR,
+ _("Cannot open file %s: %s"),
+ file, mu_strerror (errno));
+ return NULL;
+ }
+
+ while (read (fd, keybuf, sizeof (keybuf)) == sizeof (keybuf))
+ {
+ int i;
+ unsigned char key[8];
+ gl_des_ctx ctx;
+
+ des_string_to_key (keybuf, sizeof (keybuf), key);
+ gl_des_setkey (&ctx, key);
+
+ memcpy (id.chars, buf, size);
+
+ gl_des_ecb_decrypt (&ctx, (char *)&id.longs[4], (char *)&id.longs[4]);
+ id.longs[4] ^= id.longs[2];
+ id.longs[5] ^= id.longs[3];
+
+ gl_des_ecb_decrypt (&ctx, (char *)&id.longs[2], (char *)&id.longs[2]);
+ id.longs[2] ^= id.longs[0];
+ id.longs[3] ^= id.longs[1];
+
+ gl_des_ecb_decrypt (&ctx, (char *)&id.longs[0], (char *)&id.longs[0]);
+
+ for (i = 1; i < 6; i++)
+ id.longs[0] ^= id.longs[i];
+
+ if (id.fields.checksum == 0)
+ break;
+ }
+ close (fd);
+ free (buf);
+
+ if (id.fields.checksum == 0)
+ {
+ uid_t uid = ntohs (id.fields.uid);
+ auth_data = mu_get_auth_by_uid (uid);
+ if (!auth_data)
+ {
+ mu_diag_output (MU_DIAG_ERROR, _("No user with UID %u"), uid);
+ return NULL;
+ }
+ return auth_data->name;
+ }
+ else
+ mu_diag_output (MU_DIAG_ERROR, _("Failed to decrypt IDENT reply"));
+ return NULL;
+}
+
+static char *
+do_preauth_ident (struct sockaddr_in *pcs)
+{
+ mu_stream_t stream;
+ char hostaddr[16];
+ char *p = inet_ntoa (pcs->sin_addr);
+ int rc;
+ char *buf = NULL;
+ size_t size = 0;
+ char *name = NULL;
+
+ memcpy (hostaddr, p, 15);
+ hostaddr[15] = 0;
+ rc = mu_tcp_stream_create (&stream, hostaddr, ident_port,
+ MU_STREAM_RDWR | MU_STREAM_NO_CHECK);
+ if (rc)
+ {
+ mu_diag_output (MU_DIAG_INFO, _("Cannot create TCP stream: %s"),
+ mu_strerror (rc));
+ return NULL;
+ }
+
+ rc = mu_stream_open (stream);
+ if (rc)
+ {
+ mu_diag_output (MU_DIAG_INFO, _("Cannot open TCP stream to %s:%d: %s"),
+ hostaddr, ident_port, mu_strerror (rc));
+ return NULL;
+ }
+
+ mu_stream_sequential_printf (stream, "%u , %u\r\n", ntohs (pcs->sin_port),
+ mu_gocs_daemon.port);
+ mu_stream_shutdown (stream, MU_STREAM_WRITE);
+
+ rc = mu_stream_sequential_getline (stream, &buf, &size, NULL);
+ mu_stream_close (stream);
+ mu_stream_destroy (&stream, NULL);
+ if (rc)
+ {
+ mu_diag_output (MU_DIAG_INFO, _("Cannot read answer from %s:%d: %s"),
+ hostaddr, ident_port, mu_strerror (rc));
+ return NULL;
+ }
+ mu_diag_output (MU_DIAG_INFO, "Got %s", buf);
+ trimcrlf (buf);
+ name = ident_extract_username (buf);
+ if (!name)
+ mu_diag_output (MU_DIAG_INFO,
+ _("Malformed IDENT response: `%s', from %s:%d"),
+ buf, hostaddr, ident_port);
+ else if (is_des_p (name))
+ {
+ if (!ident_keyfile)
+ {
+ mu_diag_output (MU_DIAG_ERROR,
+ _("Keydile not specified in config; "
+ "use `ident-keyfile FILE'"));
+ name = NULL;
+ }
+ else
+ name = ident_decrypt (ident_keyfile, name);
+ }
+ else if (ident_encrypt_only)
+ {
+ mu_diag_output (MU_DIAG_ERROR,
+ _("Refusing unencrypted ident reply from %s:%d"),
+ hostaddr, ident_port);
+ name = NULL;
+ }
+ else
+ {
+ mu_diag_output (MU_DIAG_INFO, "USERNAME %s", name);
+ name = strdup (name);
+ }
+
+ free (buf);
+ return name;
+}
+
+
+/* External (program) preauth */
+static char *
+do_preauth_program (struct sockaddr_in *pcs)
+{
+ FILE *fp;
+ char *p = inet_ntoa (pcs->sin_addr);
+ char *cmd = 0;
+ char *buf = NULL;
+ size_t size;
+ ssize_t rc;
+
+ asprintf (&cmd, "%s %s %u %s %u",
+ preauth_program,
+ p,
+ ntohs (pcs->sin_port),
+ "0.0.0.0", /* FIXME */
+ mu_gocs_daemon.port);
+ fp = popen (cmd, "r");
+ free (cmd);
+ rc = getline (&buf, &size, fp);
+ pclose (fp);
+ if (rc > 0)
+ {
+ if (trimcrlf (buf) == 0)
+ {
+ free (buf);
+ return NULL;
+ }
+ return buf;
+ }
+ return NULL;
+}
+
+int
+imap4d_preauth_setup (int fd)
+{
+ struct sockaddr_in cs;
+ int len = sizeof cs;
+ char *username = NULL;
+
+ mu_diag_output (MU_DIAG_INFO, _("Incoming connection opened"));
+ if (getpeername (fd, (struct sockaddr *) &cs, &len) < 0)
+ mu_diag_output (MU_DIAG_ERROR,
+ _("Cannot obtain IP address of client: %s"),
+ strerror (errno));
+ else
+ mu_diag_output (MU_DIAG_INFO, _("Connect from %s"),
+ inet_ntoa (cs.sin_addr));
+
+ auth_data = NULL;
+ switch (preauth_mode)
+ {
+ case preauth_none:
+ return 0;
+
+ case preauth_stdio:
+ username = do_preauth_stdio (&cs);
+ break;
+
+ case preauth_ident:
+ username = do_preauth_ident (&cs);
+ break;
+
+ case preauth_prog:
+ username = do_preauth_program (&cs);
+ break;
+ }
+
+ if (username)
+ {
+ int rc;
+
+ if (auth_data)
+ rc = imap4d_session_setup0 ();
+ else
+ {
+ rc = imap4d_session_setup (username);
+ free (username);
+ }
+ if (rc == 0)
+ {
+ state = STATE_AUTH;
+ return 0;
+ }
+ }
+
+ return preauth_only;
+}

Return to:

Send suggestions and report system problems to the System administrator.