r677: Name it auth_ntlm_winbind and not ntlm_auth_winbind.
authorLars Müller <lmuelle@samba.org>
Tue, 14 Nov 2006 18:14:25 +0000 (18:14 +0000)
committerLars Müller <lmuelle@samba.org>
Tue, 14 Nov 2006 18:14:25 +0000 (18:14 +0000)
mod_auth_ntlm_winbind.c [new file with mode: 0644]

diff --git a/mod_auth_ntlm_winbind.c b/mod_auth_ntlm_winbind.c
new file mode 100644 (file)
index 0000000..29fe4f8
--- /dev/null
@@ -0,0 +1,1137 @@
+/*
+ *  mod_auth_ntlm_winbind.c -- Apache auth_ntlm_winbind module
+ *
+ *  [Originally autogenerated via ``apxs -n ntlm_winbind -g'']
+ *
+ * Based on:
+ *     mod_ntlm.c for Win32 by Tim Costello <tim.costello@bigfoot.com>
+ *     pam_smb by Dave Airlie <Dave.Airlie@ul.ie>
+ *
+ * Copyright Andreas Gal <agal@uwsp.edu>,           2000
+ * Copyright Sverre H. Huseby <sverrehu@online.no>, 2000
+ * Copyright Tim Potter <tpot@samba.org>,           2001
+ * Copyright Andrew Bartlett <abartlet@samba.org>,           2004
+ * Apache2 code by Waider <waider@waider.ie>,       2006
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS`` AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES ARE DISCLAIMED.
+ *
+ * This code may be freely distributed, as long the above notices are
+ * reproduced.
+ */
+/* (CGI fork code)
+ * Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+/*
+ * OVERVIEW:
+ *
+ * The mod_auth_ntlm_winbind module allows authentication and authorisation
+ * over the web against a Windows NT/2000/XP domain controller using the
+ * winbind daemon running on the same machine apache is running on.
+ *
+ * Used only by IE and newer versions of Mozilla, the NTLM over HTTP protocol is completed undocumented
+ * by Microsoft but  has been reverse engineered and described at the
+ * following URL:
+ *
+ * http://davenport.sf.net/ntlm.html
+ *
+ * CONFIGURATION:
+ *
+ * The following httpd.conf configuration describes an example
+ * configuration for this module:
+ *
+ *   <Directory "/var/www/auth">
+ *     AuthName "NTLM Authentication thingy"
+ *     NTLMAuth on
+ *     NTLMAuthHelper "/usr/bin/ntlm_auth --helper-protocol=squid-2.5-ntlmssp"
+ *     NTLMBasicAuthoritative on
+ *     AuthType NTLM
+ *     require valid-user
+ *   </Directory>
+ *
+ * or, to enable 'Negotiate' authentication too:
+ *
+ *   <Directory "/var/www/auth">
+ *     AuthName "NTLM Authentication thingy"
+ *     NTLMAuth on
+ *     NegotiateAuth on
+ *     NTLMAuthHelper "/usr/bin/ntlm_auth --helper-protocol=squid-2.5-ntlmssp"
+ *     NegotiateAuthHelper "/usr/bin/ntlm_auth --helper-protocol=gss-spnego"
+ *     NTLMBasicAuthoritative on
+ *     AuthType NTLM
+ *     AuthType Negotiate
+ *     require valid-user
+ *   </Directory>
+ *
+ * To debug what is going on, add the following line to your httpd.conf
+ * to enable debug messages to be written to the apache error log file:
+ *
+ * LogLevel debug
+ *
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_core.h"
+#include "ap_config.h"
+#include "util_script.h" /* for ap_call_exec */
+#include <assert.h>
+
+#ifdef APACHE2
+#include "http_request.h"
+#include "http_connection.h"
+#include "apr_strings.h"
+#include "apr_pools.h"
+#include "apr_tables.h"
+#include "apr_base64.h"
+
+#define RDEBUG( x... ) ap_log_rerror( APLOG_MARK, NTLM_DEBUG, APR_SUCCESS, r, x )
+#define RERROR( c, x... ) ap_log_rerror( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, c, r, x )
+#define CLEANUP(x) NULL
+#else
+
+/* compat stuff */
+#define apr_pool_t ap_pool
+#define apr_pstrcat(x...) ap_pstrcat(x)
+#define apr_pstrdup(x...) ap_pstrdup(x)
+#define apr_psprintf(x...) ap_psprintf(x)
+#define apr_table_add(x...) ap_table_add(x)
+#define apr_table_get(x...) ap_table_get(x)
+#define apr_table_setn(x...) ap_table_setn(x)
+#define apr_pcalloc(x...) ap_pcalloc(x)
+#define apr_pool_destroy(x...) ap_destroy_pool(x)
+
+#define RDEBUG( x... ) ap_log_rerror( APLOG_MARK, LOG_DEBUG, r, x )
+#define RERROR( c, x... ) ap_log_rerror( APLOG_MARK, NTLM_DEBUG|APLOG_NOERRNO, r, x )
+#define CLEANUP(x) x
+#endif
+
+/* The name of the NTLM authentication scheme.  This appears in the
+   'WWW-Authenticate' header in the initial HTTP request. */
+
+#define NTLM_AUTH_NAME "NTLM"
+#define NEGOTIATE_AUTH_NAME "Negotiate"
+
+/* A structure to hold information about the configuration for the
+   mod_auth_ntlm_winbind apache module. */
+
+typedef struct _ntlm_config_struct {
+    unsigned int ntlm_on;
+    unsigned int negotiate_on;
+    unsigned int ntlm_basic_on;
+    char *ntlm_basic_realm;
+    unsigned int authoritative;
+    char *ntlm_auth_helper;
+    char *negotiate_ntlm_auth_helper;
+    char *ntlm_plaintext_helper;
+} ntlm_config_rec;
+
+/* A structure to hold per-connection information about authentications
+   that are in progress. */
+
+struct _ntlm_auth_helper {
+    int sent_challenge;
+    int helper_pid;
+#ifdef APACHE2
+    apr_proc_t *proc;
+#else
+    BUFF *out_to_helper, *in_from_helper;
+#endif
+    apr_pool_t *pool;
+};
+
+struct _connected_user_authenticated {
+    char *user;
+    char *auth_type;
+    apr_pool_t *pool;
+    int keepalives; /* used to detect redirected auths */
+};
+
+struct _ntlm_child_stuff {
+    request_rec *r;
+    char *argv0;
+};
+
+typedef struct _conn_context {
+    struct _connected_user_authenticated *connected_user_authenticated;
+} ntlm_connection_context_t;
+
+typedef struct _ntlm_context {
+    struct _ntlm_auth_helper *ntlm_auth_helper;
+    struct _ntlm_auth_helper *negotiate_ntlm_auth_helper;
+    struct _ntlm_auth_helper *ntlm_plaintext_helper;
+} ntlm_context_t;
+
+#ifdef APACHE2
+module AP_MODULE_DECLARE_DATA auth_ntlm_winbind_module;
+#else
+module MODULE_VAR_EXPORT auth_ntlm_winbind_module;
+#endif
+
+#define NTLM_DEBUG (APLOG_DEBUG | APLOG_NOERRNO)
+
+/* If we have already authenticated then allow all subsequence accesses.
+   This appears to be what IE and IIS do when talking to each other.  I
+   don't think there are any security problems with this, unless someone
+   hijacks the connection, but then MS is susceptible to exactly the same
+   problem. */
+
+/* Extra apache configuration directives defined for this module */
+static const command_rec ntlm_winbind_cmds[] = {
+#ifdef APACHE2
+    /* NTLM authentication commands */
+    AP_INIT_FLAG( "NTLMAuth", ap_set_flag_slot,
+                  (void *) APR_OFFSETOF( ntlm_config_rec, ntlm_on ),
+                  OR_AUTHCFG,
+                  "set to 'on' to activate NTLM authentication" ),
+
+    AP_INIT_FLAG( "NegotiateAuth", ap_set_flag_slot,
+                  (void *) APR_OFFSETOF(ntlm_config_rec, negotiate_on),
+                  OR_AUTHCFG,
+                  "set to 'on' to activate Negotiate authentication" ),
+
+    AP_INIT_FLAG( "NTLMBasicAuthoritative", ap_set_flag_slot,
+                  (void *) APR_OFFSETOF(ntlm_config_rec, authoritative),
+                  OR_AUTHCFG,
+                  "set to 'off' to allow access control to be passed along to lower "
+                  "modules if the UserID is not known to this module" ),
+    /* ntlm_auth location */
+    AP_INIT_TAKE1( "NTLMAuthHelper", ap_set_string_slot,
+                   (void *) APR_OFFSETOF(ntlm_config_rec, ntlm_auth_helper),
+                   OR_AUTHCFG,
+                   "location and arguments to the Samba ntlm_auth utility" ),
+
+    AP_INIT_TAKE1( "NegotiateAuthHelper", ap_set_string_slot,
+                   (void *) APR_OFFSETOF(ntlm_config_rec, negotiate_ntlm_auth_helper),
+                   OR_AUTHCFG,
+                   "location and arguments to the Samba ntlm_auth utility" ),
+
+    AP_INIT_TAKE1( "PlaintextAuthHelper", ap_set_string_slot,
+                   (void *) APR_OFFSETOF(ntlm_config_rec, ntlm_plaintext_helper ),
+                   OR_AUTHCFG,
+                   "location and arguments to the Samba ntlm_auth utility" ),
+
+    /* Basic Authentication transport for non-IE browsers */
+    AP_INIT_FLAG( "NTLMBasicAuth", ap_set_flag_slot,
+                  (void *) APR_OFFSETOF(ntlm_config_rec, ntlm_basic_on),
+                  OR_AUTHCFG,
+                  "set to 'on' to allow Basic authentication too" ),
+
+    AP_INIT_TAKE1( "NTLMBasicRealm", ap_set_string_slot,
+                   (void *) APR_OFFSETOF(ntlm_config_rec, ntlm_basic_realm),
+                   OR_AUTHCFG, "realm to use for Basic authentication" ),
+
+#else
+    /* NTLM authentication commands */
+
+    { "NTLMAuth", ap_set_flag_slot,
+      (void *) XtOffsetOf(ntlm_config_rec, ntlm_on),
+      OR_AUTHCFG, FLAG, "set to 'on' to activate NTLM authentication" },
+
+    { "NegotiateAuth", ap_set_flag_slot,
+      (void *) XtOffsetOf(ntlm_config_rec, negotiate_on),
+      OR_AUTHCFG, FLAG, "set to 'on' to activate Negotiate authentication" },
+
+    { "NTLMBasicAuthoritative", ap_set_flag_slot,
+      (void *) XtOffsetOf(ntlm_config_rec, authoritative),
+      OR_AUTHCFG, FLAG,
+      "set to 'off' to allow access control to be passed along to lower "
+      "modules if the UserID is not known to this module" },
+
+    /* ntlm_auth location */
+
+    { "NTLMAuthHelper", ap_set_string_slot,
+      (void *) XtOffsetOf(ntlm_config_rec, ntlm_auth_helper), OR_AUTHCFG,
+      TAKE1, "location and arguments to the Samba ntlm_auth utility"},
+
+    { "NegotiateAuthHelper", ap_set_string_slot,
+      (void *) XtOffsetOf(ntlm_config_rec, negotiate_ntlm_auth_helper), OR_AUTHCFG,
+      TAKE1, "location and arguments to the Samba ntlm_auth utility"},
+
+    { "PlaintextAuthHelper", ap_set_string_slot,
+      (void *) XtOffsetOf(ntlm_config_rec, ntlm_plaintext_helper), OR_AUTHCFG,
+      TAKE1, "location and arguments to the Samba ntlm_auth utility"},
+
+    /* Basic Authentcation transport for non-IE browsers */
+
+    { "NTLMBasicAuth", ap_set_flag_slot,
+      (void *) XtOffsetOf(ntlm_config_rec, ntlm_basic_on),
+      OR_AUTHCFG, FLAG, "set to 'on' to allow Basic authentication too" },
+
+    { "NTLMBasicRealm", ap_set_string_slot,
+      (void *) XtOffsetOf(ntlm_config_rec, ntlm_basic_realm),
+      OR_AUTHCFG, TAKE1, "realm to use for Basic authentication" },
+#endif
+
+    { NULL }
+};
+
+/* both apache1 and apache2 use this to maintain per-child context */
+static ntlm_context_t global_ntlm_context;
+
+#ifndef APACHE2
+/* apache 1 doesn't seem to have a connection context I can use */
+static ntlm_connection_context_t global_connection_context;
+
+static void cleanup_ntlm_auth_helper(void *ntlm_conn_v)
+{
+    struct _ntlm_auth_helper *ntlm_conn = ntlm_conn_v;
+
+    ap_log_error( APLOG_MARK, NTLM_DEBUG, NULL, "freeing ntlm_helper" );
+
+    ap_bclose(ntlm_conn->out_to_helper);
+    ap_bclose(ntlm_conn->in_from_helper);
+
+    global_ntlm_context.ntlm_auth_helper = NULL;
+}
+
+static void cleanup_negotiate_ntlm_auth_helper(void *ntlm_conn_v)
+{
+    struct _ntlm_auth_helper *ntlm_conn = ntlm_conn_v;
+
+    ap_log_error( APLOG_MARK, NTLM_DEBUG, NULL, "freeing negotiate_helper" );
+
+    ap_bclose(ntlm_conn->out_to_helper);
+    ap_bclose(ntlm_conn->in_from_helper);
+
+    global_ntlm_context.negotiate_ntlm_auth_helper = NULL;
+}
+
+static void cleanup_ntlm_plaintext_helper( void *ntlm_conn_v ) {
+    struct _ntlm_auth_helper *ntlm_conn = ntlm_conn_v;
+
+    ap_log_error( APLOG_MARK, NTLM_DEBUG, NULL, "freeing plaintext_helper" );
+
+    ap_bclose( ntlm_conn->out_to_helper );
+    ap_bclose( ntlm_conn->in_from_helper );
+
+    global_ntlm_context.ntlm_plaintext_helper = NULL;
+}
+
+/* Dispose of a connected user */
+
+static void cleanup_connected_user_authenticated(void *unused)
+{
+    ap_log_error( APLOG_MARK, NTLM_DEBUG, NULL, "freeing user" );
+    global_connection_context.connected_user_authenticated = NULL;
+}
+#endif
+
+/* helper function to pull out the context data */
+static ntlm_connection_context_t *get_connection_context( struct conn_rec *connection ) {
+    ntlm_connection_context_t *retval = NULL;
+
+#ifdef APACHE2
+    retval = (ntlm_connection_context_t *)ap_get_module_config( connection->conn_config,
+                                                                &auth_ntlm_winbind_module );
+#else
+    retval = &global_connection_context;
+#endif
+    return retval;
+}
+
+/* Authorisation has failed - we set some headers so the client can
+   get the hint and prompt for a password from the user. */
+
+static int
+note_auth_failure(request_rec * r, const char *negotiate_auth_line)
+{
+    ntlm_config_rec *crec
+        = (ntlm_config_rec *) ap_get_module_config(r->per_dir_config,
+                                                   &auth_ntlm_winbind_module);
+    char *line;
+    ntlm_connection_context_t *ctxt = get_connection_context( r->connection );
+
+    /* MSIE will simply reply to the first, not strongest, protocol listed */
+    if (crec->negotiate_on) {
+        line = apr_pstrcat(r->pool, NEGOTIATE_AUTH_NAME, " ",
+                           negotiate_auth_line, NULL);
+        apr_table_add(r->err_headers_out,
+                      r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
+                      line);
+    }
+
+    /* Set header for NTLM authentication */
+
+    if (crec->ntlm_on) {
+        apr_table_add(r->err_headers_out,
+                      r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
+                      NTLM_AUTH_NAME);
+    }
+
+    /* Set header for basic authentication so client can use this if
+       supported. */
+
+    if (crec->ntlm_basic_on) {
+        line = apr_pstrcat(r->pool,
+                           "Basic realm=\"", crec->ntlm_basic_realm, "\"",
+                           NULL);
+        apr_table_add(r->err_headers_out,
+                      r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
+                      line);
+    }
+
+    if ( ctxt->connected_user_authenticated &&
+         ctxt->connected_user_authenticated->pool ) {
+        apr_pool_destroy( ctxt->connected_user_authenticated->pool );
+    }
+
+    return HTTP_UNAUTHORIZED;
+}
+
+
+const char *
+get_auth_header(request_rec * r, ntlm_config_rec * crec, const char *auth_scheme)
+{
+    const char *auth_line = apr_table_get(r->headers_in,
+                                          r->proxyreq ? "Proxy-Authorization"
+                                          : "Authorization");
+
+    if (!auth_line) {
+        RERROR( APR_EINIT, "no auth line present" );
+        return NULL;
+    }
+    if (strcmp(ap_getword_white(r->pool, &auth_line), auth_scheme)) {
+        RERROR( APR_EINIT, "%s auth name not present", auth_scheme );
+        return NULL;
+    }
+    return auth_line;
+}
+
+#ifndef APACHE2
+static int helper_child(void *child_stuff, child_info *pinfo)
+{
+    struct _ntlm_child_stuff *cld = (struct _ntlm_child_stuff *) child_stuff;
+    request_rec *r = cld->r;
+    char *argv0 = cld->argv0;
+    int child_pid;
+
+    RAISE_SIGSTOP(CGI_CHILD);
+
+    /* Transmute outselves into the helper
+     */
+
+    ap_cleanup_for_exec();
+
+    child_pid = ap_call_exec(r, pinfo, argv0, NULL, 1);
+
+    /* Uh oh.  Still here.  Where's the kaboom?  There was supposed to be an
+     * EARTH-shattering kaboom!
+     *
+     * Oh, well.  Muddle through as best we can...
+     *
+     * Note that only stderr is available at this point, so don't pass in
+     * a server to aplog_error.
+     */
+
+    RERROR( errno, "exec of %s failed", r->filename);
+    exit(0);
+    /* NOT REACHED */
+    return (0);
+}
+#endif
+
+
+static int
+send_auth_reply(request_rec * r, const char *auth_scheme, const char *reply)
+{
+    RDEBUG( "sending back %s", reply );
+    /* Read negotiate from ntlm_auth */
+
+    apr_table_setn(r->err_headers_out,
+                  r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
+                  apr_psprintf(r->pool, "%s %s", auth_scheme, reply));
+
+    /* This is to make sure that when receiving later messages
+     * that the r->connection is still alive after sending the
+     * nonce to the client.  This code is written, and the problem
+     * was pointed out by Michael Cai.
+     */
+    if(r->connection->keepalives >= r->server->keep_alive_max)
+    {
+        RDEBUG("Decrement the connection request count to keep it alive");
+        r->connection->keepalives -= 1;
+    }
+
+    return HTTP_UNAUTHORIZED;
+}
+
+/* get the current request's auth helper or fork one */
+static struct _ntlm_auth_helper *get_auth_helper( request_rec *r, struct _ntlm_auth_helper *auth_helper, char *cmd, void (*cleanup)(void *)) {
+#ifdef APACHE2
+    apr_procattr_t *attr;
+#endif
+
+    if ( auth_helper == NULL ) {
+        struct _ntlm_child_stuff cld;
+        apr_pool_t *pool;
+#ifdef APACHE2
+        char **argv_out;
+        apr_pool_create_ex( &pool, NULL, NULL, NULL ); /* xxx return code */
+#else
+        pool = ap_make_sub_pool( NULL );
+#endif
+        auth_helper = apr_pcalloc( pool, sizeof( struct _ntlm_auth_helper ));
+        auth_helper->pool = pool;
+        auth_helper->helper_pid = 0;
+
+#ifdef APACHE2
+        apr_tokenize_to_argv( cmd, &argv_out, pool );
+#else
+        ap_register_cleanup( pool, auth_helper, cleanup, ap_null_cleanup );
+#endif
+        cld.argv0 = cmd;
+        cld.r = r;
+
+#ifdef APACHE2
+        apr_procattr_create( &attr, pool );
+        apr_procattr_io_set( attr, APR_FULL_BLOCK, APR_FULL_BLOCK, APR_NO_PIPE );
+        apr_procattr_error_check_set( attr, 1 );
+        auth_helper->proc = (apr_proc_t *)apr_pcalloc(pool, sizeof(apr_proc_t)) ;
+        if ( apr_proc_create( auth_helper->proc, argv_out[0], (const char * const *)argv_out, NULL, attr, pool ) != APR_SUCCESS ) {
+            RERROR( errno, "couldn't spawn child ntlm helper process: %s", argv_out[0]);
+            return NULL;
+        }
+        auth_helper->helper_pid = auth_helper->proc->pid;
+#else
+        auth_helper->helper_pid = ap_bspawn_child(pool, helper_child,
+                                                  (void *) &cld, just_wait,
+                                                  &auth_helper->out_to_helper,
+                                                  &auth_helper->in_from_helper,
+                                                  NULL);
+
+        if (auth_helper->helper_pid == -1) {
+            RERROR( errno, "couldn't spawn child ntlm helper process: %s", cld.argv0);
+            return NULL;
+        }
+#endif
+
+        RDEBUG( "Launched ntlm_helper, pid %d", auth_helper->helper_pid );
+    } else {
+        RDEBUG( "Using existing auth helper %d", auth_helper->helper_pid );
+    }
+
+    return auth_helper;
+}
+
+/* Call winbind to authenticate a (user, password)
+   pair */
+static int winbind_authenticate_plaintext( request_rec *r, ntlm_config_rec * crec, char *user, char *pass)
+{
+    ntlm_connection_context_t *ctxt = get_connection_context( r->connection );
+    char *newline;
+    char args_to_helper[HUGE_STRING_LEN];
+    char args_from_helper[HUGE_STRING_LEN];
+    unsigned int bytes_written;
+    int bytes_read;
+
+    if (( global_ntlm_context.ntlm_plaintext_helper = get_auth_helper( r, global_ntlm_context.ntlm_plaintext_helper, crec->ntlm_plaintext_helper, CLEANUP(cleanup_ntlm_plaintext_helper))) == NULL ) {
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    if ( ctxt->connected_user_authenticated == NULL ) {
+        apr_pool_t *pool;
+
+        RDEBUG( "creating auth user" );
+
+#ifdef APACHE2
+        apr_pool_create_ex( &pool, r->connection->pool, NULL, NULL );
+#else
+        pool = ap_make_sub_pool(r->connection->pool);
+#endif
+
+        ctxt->connected_user_authenticated =
+            apr_pcalloc(pool, sizeof( struct _connected_user_authenticated));
+
+#ifndef APACHE2
+        ap_register_cleanup(pool,ctxt->connected_user_authenticated,
+                            cleanup_connected_user_authenticated, ap_null_cleanup );
+#endif
+
+        ctxt->connected_user_authenticated->pool = pool;
+        ctxt->connected_user_authenticated->user = NULL;
+        ctxt->connected_user_authenticated->auth_type = NULL;
+    } else {
+        /* what, we're already authenticated? */
+        return OK;
+    }
+
+    snprintf( args_to_helper, HUGE_STRING_LEN, "%s %s\n", user, pass );
+
+#ifdef APACHE2
+    bytes_written = strlen( args_to_helper );
+    apr_file_write( global_ntlm_context.ntlm_plaintext_helper->proc->in, args_to_helper, &bytes_written );
+#else
+    bytes_written = ap_bwrite( global_ntlm_context.ntlm_plaintext_helper->out_to_helper, args_to_helper, strlen( args_to_helper ));
+#endif
+
+    if ( bytes_written < strlen( args_to_helper )) {
+        RDEBUG( "failed to write user/pass to helper - wrote %d bytes", bytes_written );
+        apr_pool_destroy( global_ntlm_context.ntlm_plaintext_helper->pool );
+        apr_pool_destroy( ctxt->connected_user_authenticated->pool );
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+#ifdef APACHE2
+    apr_file_flush( global_ntlm_context.ntlm_plaintext_helper->proc->in );
+    if ( apr_file_gets( args_from_helper, HUGE_STRING_LEN, global_ntlm_context.ntlm_plaintext_helper->proc->out ) == APR_SUCCESS ) {
+        bytes_read = strlen( args_from_helper );
+    } else {
+        bytes_read = 0;
+    }
+#else
+    ap_bflush( global_ntlm_context.ntlm_plaintext_helper->out_to_helper );
+    bytes_read = ap_bgets( args_from_helper, HUGE_STRING_LEN, global_ntlm_context.ntlm_plaintext_helper->in_from_helper );
+#endif
+    if ( bytes_read == 0 ) {
+        RERROR( errno, "early EOF from helper" );
+        apr_pool_destroy(global_ntlm_context.ntlm_plaintext_helper->pool);
+        apr_pool_destroy(ctxt->connected_user_authenticated->pool);
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    } else if (bytes_read == -1) {
+        RERROR( errno, "helper died!" );
+        apr_pool_destroy(global_ntlm_context.ntlm_plaintext_helper->pool);
+        apr_pool_destroy(ctxt->connected_user_authenticated->pool);
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    } else if (bytes_read < 2) {
+        RERROR( errno, "failed to read NTLMSSP string from helper - only got %d bytes", bytes_read);
+        apr_pool_destroy(global_ntlm_context.ntlm_plaintext_helper->pool);
+        apr_pool_destroy(ctxt->connected_user_authenticated->pool);
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    newline = strchr(args_from_helper, '\n');
+    if (newline != NULL) {
+        *newline = '\0';
+    }
+
+    RDEBUG( "got response: %s", args_from_helper );
+
+    if ( strncmp( args_from_helper, "OK", 2 ) == 0 ) {
+        RDEBUG( "authentication succeeded!" );
+        ctxt->connected_user_authenticated->user = apr_pstrdup(ctxt->connected_user_authenticated->pool, user);
+        ctxt->connected_user_authenticated->keepalives = r->connection->keepalives;
+#ifdef APACHE2
+        r->user = ctxt->connected_user_authenticated->user;
+        r->ap_auth_type = apr_pstrdup(r->connection->pool, "Basic");
+        /* disconnect the child process */
+        /*        apr_proc_kill( global_ntlm_context.ntlm_plaintext_helper->proc, 9 );
+                  apr_proc_wait( global_ntlm_context.ntlm_plaintext_helper->proc, &exit, &why, APR_WAIT );*/
+#else
+        r->connection->user = ctxt->connected_user_authenticated->user;
+        r->connection->ap_auth_type = ap_pstrdup(r->connection->pool, "Basic");
+#endif
+        RDEBUG( "authenticated %s", ctxt->connected_user_authenticated->user );
+        return  OK;
+    } else {
+        if ( strncmp( args_from_helper, "ERR", 3 ) == 0 ) {
+            RDEBUG( "username/password incorrect" );
+            return note_auth_failure( r, NULL );
+        } else {
+            RDEBUG( "unknown helper response %s", args_from_helper );
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+    }
+}
+
+/* Process a message received from the client.  This can be a request for a
+   challenge (type 1) or a request to authenticate a challenge/response
+   (type 3). */
+
+static int
+process_msg(request_rec * r, ntlm_config_rec * crec, const char *auth_type)
+{
+    const char *client_msg;
+    const char *message_type;
+    char *childarg;
+    char *newline;
+    char args_to_helper[HUGE_STRING_LEN];
+    char args_from_helper[HUGE_STRING_LEN];
+    ntlm_connection_context_t *ctxt = get_connection_context( r->connection );
+    unsigned int bytes_written;
+    int bytes_read;
+    struct _ntlm_auth_helper *auth_helper;
+
+    /* If this is the first request with this connection, then create
+     * a ntlm_auth_helper entry for it. It will be cleaned up when the
+     * connection is dropped */
+
+    if (strcmp(auth_type, NEGOTIATE_AUTH_NAME) == 0) {
+        auth_helper = get_auth_helper( r, global_ntlm_context.negotiate_ntlm_auth_helper, crec->negotiate_ntlm_auth_helper, CLEANUP(cleanup_negotiate_ntlm_auth_helper));
+        global_ntlm_context.negotiate_ntlm_auth_helper = auth_helper;
+    } else if (strcmp(auth_type, NTLM_AUTH_NAME) == 0) {
+        auth_helper = get_auth_helper( r, global_ntlm_context.ntlm_auth_helper, crec->ntlm_auth_helper, CLEANUP(cleanup_ntlm_auth_helper));
+        global_ntlm_context.ntlm_auth_helper = auth_helper;
+    } else {
+        auth_helper = NULL;
+    }
+
+    if ( auth_helper == NULL ) {
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    if ( ctxt->connected_user_authenticated == NULL ) {
+        apr_pool_t *pool;
+
+        RDEBUG( "creating auth user" );
+
+#ifdef APACHE2
+        apr_pool_create_ex( &pool, r->connection->pool, NULL, NULL );
+#else
+        pool = ap_make_sub_pool(r->connection->pool);
+#endif
+
+        ctxt->connected_user_authenticated =
+            apr_pcalloc(pool, sizeof( struct _connected_user_authenticated));
+
+#ifndef APACHE2
+        ap_register_cleanup(pool,ctxt->connected_user_authenticated,
+                            cleanup_connected_user_authenticated, ap_null_cleanup );
+#endif
+
+        ctxt->connected_user_authenticated->pool = pool;
+        ctxt->connected_user_authenticated->user = NULL;
+        ctxt->connected_user_authenticated->auth_type = NULL;
+
+        message_type = "YR";
+    } else {
+        message_type = "KK";
+    }
+
+    /* Decode the information the WWW-Authenticate header */
+    if ((client_msg = get_auth_header(r, crec, auth_type)) == NULL) {
+        RDEBUG( "client did not return NTLM authentication header");
+        return note_auth_failure(r, NULL);
+    }
+
+    /* Pipe to helper */
+    snprintf(args_to_helper, HUGE_STRING_LEN, "%s %s\n", message_type, client_msg);
+
+#ifdef APACHE2
+    bytes_written = strlen( args_to_helper );
+    apr_file_write( auth_helper->proc->in, args_to_helper, &bytes_written );
+#else
+    bytes_written = ap_bwrite(auth_helper->out_to_helper, args_to_helper, strlen(args_to_helper));
+#endif
+    if (bytes_written < strlen(args_to_helper)) {
+        RDEBUG("failed to write NTLMSSP string to helper - wrote %d bytes", bytes_written);
+        apr_pool_destroy(auth_helper->pool);
+        apr_pool_destroy(ctxt->connected_user_authenticated->pool);
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+#ifdef APACHE2
+    apr_file_flush( auth_helper->proc->in );
+
+    RDEBUG( "parsing reply from helper to %s", args_to_helper );
+
+    if ( apr_file_gets(args_from_helper, HUGE_STRING_LEN, auth_helper->proc->out ) == APR_SUCCESS ) {
+        bytes_read = strlen( args_from_helper );
+    } else {
+        bytes_read = 0;
+    }
+#else
+    ap_bflush(auth_helper->out_to_helper);
+
+    bytes_read = ap_bgets(args_from_helper, HUGE_STRING_LEN, auth_helper->in_from_helper);
+#endif
+
+    if (bytes_read == 0) {
+        RERROR( errno, "early EOF from helper" );
+        apr_pool_destroy(auth_helper->pool);
+        apr_pool_destroy(ctxt->connected_user_authenticated->pool);
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    } else if (bytes_read == -1) {
+        RERROR( errno, "helper died!");
+        apr_pool_destroy(auth_helper->pool);
+        apr_pool_destroy(ctxt->connected_user_authenticated->pool);
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    } else if (bytes_read < 2) {
+        RERROR( errno, "failed to read NTLMSSP string from helper - only got %d bytes", bytes_read );
+        apr_pool_destroy(auth_helper->pool);
+        apr_pool_destroy(ctxt->connected_user_authenticated->pool);
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    newline = strchr(args_from_helper, '\n');
+    if (newline != NULL) {
+        *newline = '\0';
+    }
+
+    RDEBUG( "got response: %s", args_from_helper );
+
+    /* inspect message type */
+
+    childarg = strchr(args_from_helper, ' ');
+    if (childarg == NULL) {
+        RERROR( errno, "failed to parse response from helper");
+        apr_pool_destroy(auth_helper->pool);
+        apr_pool_destroy(ctxt->connected_user_authenticated->pool);
+
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+    childarg++;
+
+    if (strcasecmp(auth_type, NTLM_AUTH_NAME) == 0) {
+        /* if TT, send to client */
+
+        if (strncmp(args_from_helper, "TT ", 3) == 0) {
+            return send_auth_reply(r, auth_type, childarg);
+        }
+
+        /* if NA, not authenticated */
+
+        if (strncmp(args_from_helper, "NA ", 3) == 0) {
+            RDEBUG("user not authenticated: %s", childarg);
+            return note_auth_failure(r, NULL);
+        }
+
+        /* if AF, record username */
+        if (strncmp(args_from_helper, "AF ", 3) == 0) {
+            ctxt->connected_user_authenticated->user =
+                apr_pstrdup(ctxt->connected_user_authenticated->pool,
+                            childarg);
+            ctxt->connected_user_authenticated->keepalives =
+                r->connection->keepalives;
+#ifdef APACHE2
+            r->user = ctxt->connected_user_authenticated->user;
+            r->ap_auth_type = apr_pstrdup(r->connection->pool, auth_type);
+            /* disconnect the child process */
+            /*            apr_proc_kill( auth_helper->proc, 9 );
+                          apr_proc_wait( auth_helper->proc, &exit, &why, APR_WAIT );*/
+#else
+            r->connection->user = ctxt->connected_user_authenticated->user;
+            r->connection->ap_auth_type = ap_pstrdup(r->connection->pool, auth_type);
+#endif
+            RDEBUG( "authenticated %s",
+                    ctxt->connected_user_authenticated->user );
+            return OK;
+        }
+    } else if (strcasecmp(auth_type, NEGOTIATE_AUTH_NAME) == 0) {
+
+    /* 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
+    */
+
+        char *childarg3 = strchr(childarg, ' ');
+        if (childarg3 == NULL) {
+            RERROR( errno, "failed to parse response from helper");
+            apr_pool_destroy(auth_helper->pool);
+            apr_pool_destroy(ctxt->connected_user_authenticated->pool);
+
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+        *childarg3 = '\0';
+        childarg3++;
+
+        /* if TT, send to client */
+
+        if (strncmp(args_from_helper, "TT ", 3) == 0) {
+            return send_auth_reply(r, auth_type, childarg);
+        }
+
+        /* if NA, not authenticated */
+
+        if (strncmp(args_from_helper, "NA ", 3) == 0) {
+            RDEBUG("user not authenticated: %s", childarg3);
+            return note_auth_failure(r, childarg);
+        }
+
+        /* if AF, record username */
+        if (strncmp(args_from_helper, "AF ", 3) == 0) {
+            ctxt->connected_user_authenticated->user =
+                apr_pstrdup(ctxt->connected_user_authenticated->pool,
+                            childarg3);
+#ifdef APACHE2
+            r->user = ctxt->connected_user_authenticated->user;
+            ctxt->connected_user_authenticated->auth_type =
+                apr_pstrdup(r->connection->pool, auth_type);
+            r->ap_auth_type = ctxt->connected_user_authenticated->auth_type;
+#else
+            r->connection->user = ctxt->connected_user_authenticated->user;
+            ctxt->connected_user_authenticated->auth_type = ap_pstrdup(r->connection->pool, auth_type);
+            r->connection->ap_auth_type = ctxt->connected_user_authenticated->auth_type;
+#endif
+
+            if (!strcmp("*", childarg)) {
+                /* Send last leg (possible mutual authentication token) */
+                apr_table_setn(r->headers_out,
+                              r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
+                              apr_psprintf(r->pool, "%s %s", auth_type, childarg));
+            }
+
+            RDEBUG( "wow, we're all happy here" );
+
+            return OK;
+        }
+    }
+
+    /* Helper failed */
+
+    /* if BH, helper is busted */
+
+    if (strncmp(args_from_helper, "BH ", 3) == 0) {
+        RERROR( APR_EGENERAL, "ntlm_auth reports Broken Helper: %s", args_from_helper);
+    } else {
+        RERROR( APR_EGENERAL, "could not parse %s helper callback: %s", auth_type, args_from_helper);
+    }
+
+    apr_pool_destroy(auth_helper->pool);
+    apr_pool_destroy(ctxt->connected_user_authenticated->pool);
+
+    return HTTP_INTERNAL_SERVER_ERROR;
+}
+
+/* Called to create a configuration structure for each <Directory> section
+   that uses the ntlm auth module. */
+
+static void *
+ntlm_winbind_dir_config(apr_pool_t * p, char *d)
+{
+    ntlm_config_rec *crec
+        = (ntlm_config_rec *) apr_pcalloc(p, sizeof(ntlm_config_rec));
+
+    /* Set the defaults. */
+
+    crec->authoritative = 1;
+    crec->ntlm_on = 0;
+    crec->negotiate_on = 0;
+    crec->ntlm_basic_on = 0;
+    crec->ntlm_basic_realm = "REALM";
+    crec->ntlm_auth_helper = "ntlm_auth --helper-protocol=squid-2.5-ntlmssp";
+    crec->negotiate_ntlm_auth_helper = "ntlm_auth --helper-protocol=gss-spnego";
+    crec->ntlm_plaintext_helper = "ntlm_auth --helper-protocol=squid-2.5-basic";
+
+    return crec;
+}
+
+/* Authenticate a user using basic authentication */
+static int
+authenticate_basic_user(request_rec * r, ntlm_config_rec * crec,
+                        const char *auth_line_after_Basic)
+{
+    char *sent_user = NULL, *sent_pw;
+    int result = HTTP_UNAUTHORIZED;
+
+    while (*auth_line_after_Basic == ' ' || *auth_line_after_Basic == '\t')
+        auth_line_after_Basic++;
+
+#ifdef APACHE2
+    sent_user = apr_pcalloc( r->pool, apr_base64_decode_len( auth_line_after_Basic ));
+    apr_base64_decode( sent_user, auth_line_after_Basic );
+#else
+    sent_user = ap_pbase64decode(r->pool, auth_line_after_Basic);
+#endif
+
+    if (sent_user != NULL) {
+        char *s;
+
+        if ((sent_pw = strchr(sent_user, ':')) != NULL) {
+            *sent_pw = '\0';
+            ++sent_pw;
+        } else
+            sent_pw = "";
+        if ((s = strchr(sent_user, '\\')) != NULL
+            || (s = strchr(sent_user, '/')) != NULL) {
+
+            /* Authenticate this user as a domain user */
+
+            result = winbind_authenticate_plaintext( r, crec, sent_user, sent_pw);
+
+            RDEBUG("authenticate domain user %s: %s", sent_user,
+                   (result == OK) ? "OK" : "FAILED");
+            goto done;
+        }
+    } else {
+        RDEBUG("can't extract user from %s", auth_line_after_Basic );
+        sent_user = sent_pw = "";
+    }
+
+    RDEBUG("authenticate local user %s: %s", sent_user,
+           (result == OK) ? "OK" : "FAILED");
+
+ done:
+    return result;
+}
+
+/* Check the user id from a http request */
+static int check_user_id(request_rec * r) {
+    ntlm_config_rec *crec =
+        (ntlm_config_rec *) ap_get_module_config(r->per_dir_config,
+                                                 &auth_ntlm_winbind_module);
+    ntlm_connection_context_t *ctxt = get_connection_context( r->connection );
+    const char *auth_line = apr_table_get(r->headers_in,
+                                          r->proxyreq ? "Proxy-Authorization"
+                                          : "Authorization");
+    const char *auth_line2;
+
+    /* Trust the authentication on an existing connection */
+    if (ctxt->connected_user_authenticated && ctxt->connected_user_authenticated->user) {
+        /* internal redirects cause this to get called more than once
+           per request on Apache 1.x. This compensates by checking if
+           the connection is the same as the one we authed against */
+        if ( !auth_line || ( ctxt->connected_user_authenticated->keepalives == r->connection->keepalives )) {
+            /* silently accept login with same credentials */
+            RDEBUG( "retaining user %s",
+                    ctxt->connected_user_authenticated->user );
+            RDEBUG( "keepalives: %d", r->connection->keepalives );
+#ifdef APACHE2
+            r->user = ctxt->connected_user_authenticated->user;
+            r->ap_auth_type = ctxt->connected_user_authenticated->auth_type;
+#else
+            r->connection->user = ctxt->connected_user_authenticated->user;
+            r->connection->ap_auth_type = ctxt->connected_user_authenticated->auth_type;
+#endif
+            return OK;
+        } else {
+            RDEBUG( "reauth" );
+            /* client wishes to re-authenticate this TCP socket */
+            if ( ctxt->connected_user_authenticated->pool ) {
+                apr_pool_destroy(ctxt->connected_user_authenticated->pool);
+                ctxt->connected_user_authenticated = NULL;
+            }
+        }
+    }
+
+    /* No authentication line given.  Return a 401 and a WWW-Authenticate
+       header so authentication can commence. */
+
+    if (!auth_line) {
+        note_auth_failure(r, NULL);
+        return HTTP_UNAUTHORIZED;
+    }
+
+    /* If basic authentication is requested and enabled, try to
+       authenticate the user with basic */
+
+    auth_line2 = auth_line;
+    if (crec->ntlm_basic_on
+        && strcasecmp(ap_getword(r->pool, &auth_line2, ' '), "Basic") == 0) {
+        RDEBUG( "trying basic auth" );
+        return authenticate_basic_user(r, crec, &auth_line[5]); /* seems clunky */
+    }
+
+    /* Process a 'Negotiate' SPNEGO over http message */
+    auth_line2 = auth_line;
+    if (strcasecmp(ap_getword(r->pool, &auth_line2, ' '), NEGOTIATE_AUTH_NAME) == 0) {
+        if (!crec->negotiate_on) {
+            RDEBUG("Negotiate authentication is not enabled");
+            return DECLINED;
+        } else {
+            return process_msg(r, crec, NEGOTIATE_AUTH_NAME);
+        }
+    }
+
+    /* Process a NTLM over http message */
+
+    auth_line2 = auth_line;
+    if (strcasecmp(ap_getword(r->pool, &auth_line2, ' '), NTLM_AUTH_NAME) == 0) {
+        if (!crec->ntlm_on) {
+            RDEBUG("NTLM authentication is not enabled");
+            return DECLINED;
+        } else {
+            RDEBUG( "doing ntlm auth dance" );
+            return process_msg(r, crec, NTLM_AUTH_NAME);
+        }
+    }
+
+    if (ctxt->connected_user_authenticated && ctxt->connected_user_authenticated->pool ) {
+        apr_pool_destroy(ctxt->connected_user_authenticated->pool);
+    }
+
+    RDEBUG( "declined" );
+
+    return DECLINED;
+}
+
+/* Dispatch list for API hooks */
+#ifdef APACHE2
+static int ntlm_pre_conn(conn_rec *c, void *csd) {
+    ntlm_connection_context_t *ctxt = apr_pcalloc(c->pool, sizeof(ntlm_connection_context_t));
+
+    ap_set_module_config(c->conn_config, &auth_ntlm_winbind_module, ctxt);
+
+    return OK;
+}
+
+
+static void register_hooks(apr_pool_t *pool)
+{
+    ap_hook_pre_connection(ntlm_pre_conn,NULL,NULL,APR_HOOK_MIDDLE);
+    ap_hook_check_user_id(check_user_id,NULL,NULL,APR_HOOK_MIDDLE);
+};
+
+module AP_MODULE_DECLARE_DATA auth_ntlm_winbind_module = {
+    STANDARD20_MODULE_STUFF,
+    ntlm_winbind_dir_config, /* create per-dir    config structures */
+    NULL,                    /* merge  per-dir    config structures */
+    NULL,                    /* create per-server config structures */
+    NULL,                    /* merge  per-server config structures */
+    ntlm_winbind_cmds,       /* table of config file commands       */
+    register_hooks,          /* register hooks */
+};
+#else
+module MODULE_VAR_EXPORT auth_ntlm_winbind_module = {
+    STANDARD_MODULE_STUFF,
+    NULL,                    /* module initializer                  */
+    ntlm_winbind_dir_config, /* create per-dir    config structures */
+    NULL,                    /* merge  per-dir    config structures */
+    NULL,                    /* create per-server config structures */
+    NULL,                    /* merge  per-server config structures */
+    ntlm_winbind_cmds,       /* table of config file commands       */
+    NULL,                    /* [#8] MIME-typed-dispatched handlers */
+    NULL,                    /* [#1] URI to filename translation    */
+    check_user_id,           /* [#4] validate user id from request  */
+    NULL,                    /* [#5] check if the user is ok _here_ */
+    NULL,                    /* [#3] check access by host address   */
+    NULL,                    /* [#6] determine MIME type            */
+    NULL,                    /* [#7] pre-run fixups                 */
+    NULL,                    /* [#9] log a transaction              */
+    NULL,                    /* [#2] header parser                  */
+    NULL,                    /* child_init                          */
+    NULL,                    /* child_exit                          */
+    NULL                     /* [#0] post read-request              */
+#ifdef EAPI
+   ,NULL,                    /* EAPI: add_module                    */
+    NULL,                    /* EAPI: remove_module                 */
+    NULL,                    /* EAPI: rewrite_command               */
+    NULL                     /* EAPI: new_connection                */
+#endif
+};
+#endif
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */