Version 3 of the patch.

This version sent also to Samba team, in hope they have more chances to test it properly.



Dmitry Butskoy

diff -Nrbu dovecot-1.0.1/dovecot-example.conf 
dovecot-1.0.1-OK/dovecot-example.conf
--- dovecot-1.0.1/dovecot-example.conf  2007-06-13 04:47:39.000000000 +0400
+++ dovecot-1.0.1-OK/dovecot-example.conf       2007-07-06 18:40:35.000000000 
+0400
@@ -748,6 +748,27 @@
 # default (usually /etc/krb5.keytab) if not specified.
 #auth_krb5_keytab = 
 
+# If you have "ntlm_auth" helper and running winbind daemon
+# (both from Samba package, see http://www.samba.org),
+# you can use it for "ntlm" and "gss-spnego" authentication mechanisms.
+# It is useful when you need to authenticate users against a Windows domain
+# (either AD or NT).
+#
+# The usernames, returned by winbind, can contain some domain part
+# (either "DOMAIN\user" or "[EMAIL PROTECTED]"). Such usernames
+# are always transformed to the form of "[EMAIL PROTECTED]". To strip domain 
part
+# (to obtain correspond local username, for example), specify
+# auth_username_format = %n
+
+# Use "ntlm-auth + winbind" way for "ntlm" mechanism,
+# instead of own Dovecot's ntlm support ("gss-spnego" always requires winbind)
+#auth_ntlm_use_winbind = no
+
+# Specify an alternate path and command line for the helper for "ntlm"
+#auth_winbind_helper_ntlm = /usr/bin/ntlm_auth 
--helper-protocol=squid-2.5-ntlmssp"
+# The same for "gss-spnego"
+#auth_winbind_helper_spnego = /usr/bin/ntlm_auth --helper-protocol=gss-spnego
+
 auth default {
   # Space separated list of wanted authentication mechanisms:
   #   plain login digest-md5 cram-md5 ntlm rpa apop anonymous gssapi
diff -Nrbu dovecot-1.0.1/src/auth/Makefile.am 
dovecot-1.0.1-OK/src/auth/Makefile.am
--- dovecot-1.0.1/src/auth/Makefile.am  2007-05-19 15:14:04.000000000 +0400
+++ dovecot-1.0.1-OK/src/auth/Makefile.am       2007-07-03 17:53:14.000000000 
+0400
@@ -56,6 +56,7 @@
        mech-cram-md5.c \
        mech-digest-md5.c \
        mech-ntlm.c \
+       mech-winbind.c \
        mech-gssapi.c \
        mech-rpa.c \
        mech-apop.c \
diff -Nrbu dovecot-1.0.1/src/auth/Makefile.in 
dovecot-1.0.1-OK/src/auth/Makefile.in
--- dovecot-1.0.1/src/auth/Makefile.in  2007-06-14 16:02:13.000000000 +0400
+++ dovecot-1.0.1-OK/src/auth/Makefile.in       2007-07-03 17:53:41.000000000 
+0400
@@ -78,6 +78,7 @@
        mech.$(OBJEXT) mech-anonymous.$(OBJEXT) mech-plain.$(OBJEXT) \
        mech-login.$(OBJEXT) mech-cram-md5.$(OBJEXT) \
        mech-digest-md5.$(OBJEXT) mech-ntlm.$(OBJEXT) \
+       mech-winbind.$(OBJEXT) \
        mech-gssapi.$(OBJEXT) mech-rpa.$(OBJEXT) mech-apop.$(OBJEXT) \
        passdb.$(OBJEXT) passdb-blocking.$(OBJEXT) \
        passdb-bsdauth.$(OBJEXT) passdb-cache.$(OBJEXT) \
@@ -325,6 +326,7 @@
        mech-cram-md5.c \
        mech-digest-md5.c \
        mech-ntlm.c \
+       mech-winbind.c \
        mech-gssapi.c \
        mech-rpa.c \
        mech-apop.c \
@@ -494,6 +496,7 @@
 @AMDEP_TRUE@@am__include@ @[EMAIL PROTECTED]/$(DEPDIR)/[EMAIL PROTECTED]@
 @AMDEP_TRUE@@am__include@ @[EMAIL PROTECTED]/$(DEPDIR)/[EMAIL PROTECTED]@
 @AMDEP_TRUE@@am__include@ @[EMAIL PROTECTED]/$(DEPDIR)/[EMAIL PROTECTED]@
[EMAIL PROTECTED]@@am__include@ @[EMAIL PROTECTED]/$(DEPDIR)/[EMAIL PROTECTED]@
 @AMDEP_TRUE@@am__include@ @[EMAIL PROTECTED]/$(DEPDIR)/[EMAIL PROTECTED]@
 @AMDEP_TRUE@@am__include@ @[EMAIL PROTECTED]/$(DEPDIR)/[EMAIL PROTECTED]@
 @AMDEP_TRUE@@am__include@ @[EMAIL PROTECTED]/$(DEPDIR)/[EMAIL PROTECTED]@
diff -Nrbu dovecot-1.0.1/src/auth/mech-winbind.c 
dovecot-1.0.1-OK/src/auth/mech-winbind.c
--- dovecot-1.0.1/src/auth/mech-winbind.c       1970-01-01 03:00:00.000000000 
+0300
+++ dovecot-1.0.1-OK/src/auth/mech-winbind.c    2007-07-06 17:53:00.000000000 
+0400
@@ -0,0 +1,310 @@
+/*
+ * NTLM and Negotiate authentication mechanisms,
+ * using Samba winbind daemon
+ *
+ * Copyright (c) 2007 Dmitry Butskoy <[EMAIL PROTECTED]>
+ *
+ * This software is released under the MIT license.
+ */
+
+#include "common.h"
+#include "mech.h"
+#include "str.h"
+#include "buffer.h"
+#include "safe-memset.h"
+#include "base64.h"
+#include "istream.h"
+#include "ostream.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+
+#define WINBIND_HELPER_NTLM \
+       "/usr/bin/ntlm_auth --helper-protocol=squid-2.5-ntlmssp"
+#define WINBIND_HELPER_SPNEGO \
+       "/usr/bin/ntlm_auth --helper-protocol=gss-spnego"
+
+
+struct winbind_auth_request {
+       struct auth_request auth_request;
+
+       pool_t pool;
+
+       bool continued;
+
+       bool negotiate;     /*  TRUE for GSS-SPNEGO case, else NTLM   */
+
+};
+
+enum helper_result {
+       HR_OK   = 0,    /*  OK or continue   */
+       HR_FAIL = -1,   /*  authentication failed   */
+       HR_RESTART = -2 /*  FAIL + try to restart helper   */
+};
+
+
+static struct istream *in_pipe = NULL;
+static struct ostream *out_pipe = NULL;
+
+
+static void init_helper (bool negotiate) {
+       int infd[2], outfd[2];
+       pid_t pid;
+
+       if (in_pipe)  i_stream_destroy (&in_pipe);
+       if (out_pipe)  o_stream_destroy (&out_pipe);
+
+
+       if (pipe (infd) < 0 || pipe (outfd) < 0)
+               i_fatal ("pipe creation failed: %m");
+
+       pid = fork ();
+       if (pid < 0)  i_fatal ("fork() failed: %m");
+
+       if (pid == 0) { /*  child   */
+           const char *helper;
+           const char **args;
+
+           close (infd[0]);
+           close (outfd[1]);
+
+           if (dup2 (outfd[0], 0) < 0 ||
+               dup2 (infd[1], 1) < 0
+           )  i_fatal ("dup2 failed after fork(): %m");
+
+           if (negotiate) {
+               helper = getenv ("WINBIND_HELPER_SPNEGO");
+               if (!helper)  helper = WINBIND_HELPER_SPNEGO;
+           } else {
+               helper = getenv ("WINBIND_HELPER_NTLM");
+               if (!helper)  helper = WINBIND_HELPER_NTLM;
+           }
+
+           args = t_strsplit_spaces (helper, " ");
+           if (!args || !args[0])
+                   i_fatal ("bad helper `%s' specified", helper);
+
+           execv (args[0], (char **) args);
+           i_fatal ("execv of %s failed: %m", args[0]);
+           _exit (127);        /*  paranoia   */
+       }
+
+       /*  parent   */
+
+       close (infd[1]);
+       close (outfd[0]);
+
+       in_pipe = i_stream_create_file (infd[0], default_pool, 
+                                    AUTH_CLIENT_MAX_LINE_LENGTH, FALSE);
+       out_pipe = o_stream_create_file (outfd[1], default_pool,
+                                               (size_t) -1, FALSE);
+
+       return;
+}
+
+
+static enum helper_result do_auth_continue (struct auth_request *auth_request,
+                               const unsigned char *data, size_t data_size) {
+       struct winbind_auth_request *request =
+                               (struct winbind_auth_request *) auth_request;
+       string_t *str;
+       char *answer;
+       const char **token;
+
+       str = t_str_new (MAX_BASE64_ENCODED_SIZE (data_size + 1) + 4);
+
+       str_printfa (str, "%s ", request->continued ? "KK" : "YR");
+       base64_encode (data, data_size, str);
+       str_append_c (str, '\n');
+
+       if (o_stream_send_str (out_pipe, str_c (str)) < 0 ||
+           o_stream_flush (out_pipe) < 0
+       ) {
+           auth_request_log_info (auth_request, "winbind",
+                                       "cannot write to helper pipe");
+           return HR_RESTART;
+       }
+
+       request->continued = FALSE;
+
+
+       answer = i_stream_read_next_line (in_pipe);
+       if (!answer) {
+           auth_request_log_info (auth_request, "winbind",
+                                       "cannot read from helper pipe");
+           return HR_RESTART;
+       }
+
+       token = t_strsplit_spaces (answer, " \n");
+       if (!token || !token[0] ||
+           (!token[1] && strcmp (token[0], "BH") != 0) ||
+           (request->negotiate && !token[2])
+       ) {
+           auth_request_log_info (auth_request, "winbind",
+                           "could not parse `%s' helper callback", answer);
+           return HR_RESTART;
+       }
+
+       /*
+        *  NTLM:
+        *  The child's reply contains 2 parts:
+        *   - The code: TT, AF or NA
+        *   - The argument:
+        *        For TT it's the blob to send to the client, coded in base64
+        *        For AF it's user or DOMAIN\user
+        *        For NA it's the NT error code
+        *
+        *  GSS-SPNEGO:
+        *  The child's reply contains 3 parts:
+        *   - The code: TT, AF or NA
+        *   - The blob to send to the client, coded in base64
+        *   - The argument:
+        *        For TT it's a dummy '*'
+        *        For AF it's DOMAIN\user
+        *        For NA it's the NT error code
+        */
+       
+       if (!strcmp (token[0], "TT")) {
+           buffer_t *buf;
+           size_t len = strlen (token[1]);
+
+           buf = buffer_create_dynamic (pool_datastack_create(),
+                                           MAX_BASE64_DECODED_SIZE (len));
+           base64_decode (token[1], len, NULL, buf);
+
+           auth_request->callback (auth_request,
+                                       AUTH_CLIENT_RESULT_CONTINUE,
+                                       buf->data, buf->used);
+           request->continued = TRUE;
+           return HR_OK;
+       }
+       else if (!strcmp (token[0], "NA")) {
+           const char *error = request->negotiate ? token[2] : token[1];
+
+           auth_request_log_info (auth_request, "winbind",
+                               "user not authenticated: %s", error);
+
+           return HR_FAIL;
+       }
+       else if (!strcmp (token[0], "AF")) {
+           const char *user, *p, *error;
+
+           user = request->negotiate ? token[2] : token[1];
+
+           p = strchr (user, '\\');
+           if (p) {
+               /*  change "DOMAIN\user" to uniform style "[EMAIL PROTECTED]"   
*/
+               user = t_strconcat (p+1, "@", t_strdup_until (user, p), NULL);
+           }
+
+           if (!auth_request_set_username (auth_request, user, &error)) {
+               auth_request_log_info (auth_request, "winbind", "%s", error);
+
+               return HR_FAIL;
+           }
+ 
+           if (request->negotiate && strcmp (token[1], "*") != 0) {
+               buffer_t *buf;
+               size_t len = strlen (token[1]);
+
+               buf = buffer_create_dynamic (pool_datastack_create(),
+                                               MAX_BASE64_DECODED_SIZE (len));
+               base64_decode (token[1], len, NULL, buf);
+
+               auth_request_success (&request->auth_request,
+                                               buf->data, buf->used);
+           } else
+               auth_request_success (&request->auth_request, NULL, 0);
+
+           return HR_OK;
+       }
+       else if (!strcmp (token[0], "BH")) {
+           auth_request_log_info (auth_request, "winbind",
+                       "ntlm_auth reports broken helper: %s",
+                       token[1] ? token[1] : "");
+           return HR_FAIL;
+       }
+       else {
+           auth_request_log_info (auth_request, "winbind",
+                           "could not parse `%s' helper callback", answer);
+           return HR_FAIL;
+       }
+
+}
+
+static void mech_winbind_auth_continue(struct auth_request *auth_request,
+                               const unsigned char *data, size_t data_size) {
+       struct winbind_auth_request *request =
+                               (struct winbind_auth_request *) auth_request;
+       enum helper_result res;
+
+       res = do_auth_continue (auth_request, data, data_size);
+
+       if (res == HR_OK)  return;
+       else if (res == HR_RESTART)
+           init_helper (request->negotiate);   /*   try to restart   */
+
+       auth_request_fail (auth_request);
+       return;
+}
+
+
+static struct auth_request *do_auth_new (bool negotiate) {
+       struct winbind_auth_request *request;
+       pool_t pool;
+
+       pool = pool_alloconly_create("winbind_auth_request", 1024);
+       request = p_new(pool, struct winbind_auth_request, 1);
+       request->pool = pool;
+
+       request->negotiate = negotiate;
+
+       request->continued = FALSE;
+
+       if (!in_pipe || !out_pipe)
+               init_helper (negotiate);
+
+       request->auth_request.pool = pool;
+       return &request->auth_request;
+}
+
+static struct auth_request *mech_winbind_ntlm_auth_new (void) {
+
+       return  do_auth_new (FALSE);
+}
+
+static struct auth_request *mech_winbind_spnego_auth_new (void) {
+
+       return  do_auth_new (TRUE);
+}
+
+
+const struct mech_module mech_winbind_ntlm = {
+       "NTLM",
+
+       MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE,
+
+       MEMBER(passdb_need_plain) FALSE,
+       MEMBER(passdb_need_credentials) FALSE,
+
+       mech_winbind_ntlm_auth_new,
+       mech_generic_auth_initial,
+       mech_winbind_auth_continue,
+       mech_generic_auth_free
+};
+
+const struct mech_module mech_winbind_spnego = {
+       "GSS-SPNEGO",
+
+       MEMBER(flags) 0,
+
+       MEMBER(passdb_need_plain) FALSE,
+       MEMBER(passdb_need_credentials) FALSE,
+
+       mech_winbind_spnego_auth_new,
+       mech_generic_auth_initial,
+       mech_winbind_auth_continue,
+       mech_generic_auth_free
+};
+
diff -Nrbu dovecot-1.0.1/src/auth/mech.c dovecot-1.0.1-OK/src/auth/mech.c
--- dovecot-1.0.1/src/auth/mech.c       2007-05-19 15:14:04.000000000 +0400
+++ dovecot-1.0.1-OK/src/auth/mech.c    2007-07-03 17:53:14.000000000 +0400
@@ -73,6 +73,8 @@
 #ifdef HAVE_GSSAPI
 extern struct mech_module mech_gssapi;
 #endif
+extern struct mech_module mech_winbind_ntlm;
+extern struct mech_module mech_winbind_spnego;
 
 void mech_init(void)
 {
@@ -81,12 +83,16 @@
        mech_register_module(&mech_apop);
        mech_register_module(&mech_cram_md5);
        mech_register_module(&mech_digest_md5);
+       if (getenv("NTLM_USE_WINBIND") != NULL)
+           mech_register_module(&mech_winbind_ntlm);
+       else
        mech_register_module(&mech_ntlm);
        mech_register_module(&mech_rpa);
        mech_register_module(&mech_anonymous);
 #ifdef HAVE_GSSAPI
        mech_register_module(&mech_gssapi);
 #endif
+       mech_register_module(&mech_winbind_spnego);
 }
 
 void mech_deinit(void)
@@ -96,10 +102,14 @@
        mech_unregister_module(&mech_apop);
        mech_unregister_module(&mech_cram_md5);
        mech_unregister_module(&mech_digest_md5);
+       if (getenv("NTLM_USE_WINBIND") != NULL)
+           mech_unregister_module(&mech_winbind_ntlm);
+       else
        mech_unregister_module(&mech_ntlm);
        mech_unregister_module(&mech_rpa);
        mech_unregister_module(&mech_anonymous);
 #ifdef HAVE_GSSAPI
        mech_unregister_module(&mech_gssapi);
 #endif
+       mech_unregister_module(&mech_winbind_spnego);
 }
diff -Nrbu dovecot-1.0.1/src/master/auth-process.c 
dovecot-1.0.1-OK/src/master/auth-process.c
--- dovecot-1.0.1/src/master/auth-process.c     2007-06-12 20:43:46.000000000 
+0400
+++ dovecot-1.0.1-OK/src/master/auth-process.c  2007-07-03 17:53:14.000000000 
+0400
@@ -474,6 +474,8 @@
                env_put("SSL_REQUIRE_CLIENT_CERT=1");
        if (set->ssl_username_from_cert)
                env_put("SSL_USERNAME_FROM_CERT=1");
+       if (set->ntlm_use_winbind)
+               env_put("NTLM_USE_WINBIND=1");
        if (*set->krb5_keytab != '\0') {
                /* Environment used by Kerberos 5 library directly */
                env_put(t_strconcat("KRB5_KTNAME=", set->krb5_keytab, NULL));
@@ -482,6 +484,14 @@
                env_put(t_strconcat("GSSAPI_HOSTNAME=",
                                    set->gssapi_hostname, NULL));
        }
+       if (*set->winbind_helper_ntlm != '\0') {
+               env_put(t_strconcat("WINBIND_HELPER_NTLM=",
+                                   set->winbind_helper_ntlm, NULL));
+       }
+       if (*set->winbind_helper_spnego != '\0') {
+               env_put(t_strconcat("WINBIND_HELPER_SPNEGO=",
+                                   set->winbind_helper_spnego, NULL));
+       }
 
        restrict_process_size(set->process_size, (unsigned int)-1);
 }
diff -Nrbu dovecot-1.0.1/src/master/master-settings.c 
dovecot-1.0.1-OK/src/master/master-settings.c
--- dovecot-1.0.1/src/master/master-settings.c  2007-06-12 20:43:06.000000000 
+0400
+++ dovecot-1.0.1-OK/src/master/master-settings.c       2007-07-03 
17:53:14.000000000 +0400
@@ -72,12 +72,15 @@
        DEF(SET_STR, anonymous_username),
        DEF(SET_STR, krb5_keytab),
        DEF(SET_STR, gssapi_hostname),
+       DEF(SET_STR, winbind_helper_ntlm),
+       DEF(SET_STR, winbind_helper_spnego),
 
        DEF(SET_BOOL, verbose),
        DEF(SET_BOOL, debug),
        DEF(SET_BOOL, debug_passwords),
        DEF(SET_BOOL, ssl_require_client_cert),
        DEF(SET_BOOL, ssl_username_from_cert),
+       DEF(SET_BOOL, ntlm_use_winbind),
 
        DEF(SET_INT, count),
        DEF(SET_INT, worker_max_count),
@@ -291,12 +294,15 @@
        MEMBER(anonymous_username) "anonymous",
        MEMBER(krb5_keytab) "",
        MEMBER(gssapi_hostname) "",
+       MEMBER(winbind_helper_ntlm) "",
+       MEMBER(winbind_helper_spnego) "",
 
        MEMBER(verbose) FALSE,
        MEMBER(debug) FALSE,
        MEMBER(debug_passwords) FALSE,
        MEMBER(ssl_require_client_cert) FALSE,
        MEMBER(ssl_username_from_cert) FALSE,
+       MEMBER(ntlm_use_winbind) FALSE,
 
        MEMBER(count) 1,
        MEMBER(worker_max_count) 30,
diff -Nrbu dovecot-1.0.1/src/master/master-settings.h 
dovecot-1.0.1-OK/src/master/master-settings.h
--- dovecot-1.0.1/src/master/master-settings.h  2007-06-12 20:42:52.000000000 
+0400
+++ dovecot-1.0.1-OK/src/master/master-settings.h       2007-07-03 
17:53:14.000000000 +0400
@@ -191,10 +191,13 @@
        const char *anonymous_username;
        const char *krb5_keytab;
        const char *gssapi_hostname;
+       const char *winbind_helper_ntlm;
+       const char *winbind_helper_spnego;
 
        bool verbose, debug, debug_passwords;
        bool ssl_require_client_cert;
        bool ssl_username_from_cert;
+       bool ntlm_use_winbind;
 
        unsigned int count;
        unsigned int worker_max_count;

Reply via email to