2 * mod_auth_ntlm_winbind.c -- Apache auth_ntlm_winbind module
4 * [Originally autogenerated via ``apxs -n ntlm_winbind -g'']
7 * mod_ntlm.c for Win32 by Tim Costello <tim.costello@bigfoot.com>
8 * pam_smb by Dave Airlie <Dave.Airlie@ul.ie>
10 * Copyright Andreas Gal <agal@uwsp.edu>, 2000
11 * Copyright Sverre H. Huseby <sverrehu@online.no>, 2000
12 * Copyright Tim Potter <tpot@samba.org>, 2001
13 * Copyright Andrew Bartlett <abartlet@samba.org>, 2004
14 * Apache2 code by Waider <waider@waider.ie>, 2006
16 * THIS SOFTWARE IS PROVIDED ``AS IS`` AND ANY EXPRESSED OR IMPLIED
17 * WARRANTIES ARE DISCLAIMED.
19 * This code may be freely distributed, as long the above notices are
23 * Copyright 1999-2004 The Apache Software Foundation
25 * Licensed under the Apache License, Version 2.0 (the "License");
26 * you may not use this file except in compliance with the License.
27 * You may obtain a copy of the License at
29 * http://www.apache.org/licenses/LICENSE-2.0
31 * Unless required by applicable law or agreed to in writing, software
32 * distributed under the License is distributed on an "AS IS" BASIS,
33 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
34 * See the License for the specific language governing permissions and
35 * limitations under the License.
40 * The sections OVERVIEW, INSTALLATION, and CONFIGURATION are available from
41 * the README file in this directory.
45 #include "http_config.h"
46 #include "http_protocol.h"
48 #include "http_core.h"
49 #include "ap_config.h"
50 #include "util_script.h" /* for ap_call_exec */
54 #include "http_request.h"
55 #include "http_connection.h"
56 #include "apr_strings.h"
57 #include "apr_pools.h"
58 #include "apr_tables.h"
59 #include "apr_base64.h"
61 #define RDEBUG( x... ) ap_log_rerror( APLOG_MARK, NTLM_DEBUG, APR_SUCCESS, r, x )
62 #define RERROR( c, x... ) ap_log_rerror( APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, c, r, x )
63 #define CLEANUP(x) NULL
67 #define apr_pool_t ap_pool
68 #define apr_pstrcat(x...) ap_pstrcat(x)
69 #define apr_pstrdup(x...) ap_pstrdup(x)
70 #define apr_psprintf(x...) ap_psprintf(x)
71 #define apr_table_add(x...) ap_table_add(x)
72 #define apr_table_get(x...) ap_table_get(x)
73 #define apr_table_setn(x...) ap_table_setn(x)
74 #define apr_pcalloc(x...) ap_pcalloc(x)
75 #define apr_pool_destroy(x...) ap_destroy_pool(x)
77 #define RDEBUG( x... ) ap_log_rerror( APLOG_MARK, LOG_DEBUG, r, x )
78 #define RERROR( c, x... ) ap_log_rerror( APLOG_MARK, NTLM_DEBUG|APLOG_NOERRNO, r, x )
82 /* The name of the NTLM authentication scheme. This appears in the
83 'WWW-Authenticate' header in the initial HTTP request. */
85 #define NTLM_AUTH_NAME "NTLM"
86 #define NEGOTIATE_AUTH_NAME "Negotiate"
88 /* A structure to hold information about the configuration for the
89 mod_auth_ntlm_winbind apache module. */
91 typedef struct _ntlm_config_struct {
93 unsigned int negotiate_on;
94 unsigned int ntlm_basic_on;
95 char *ntlm_basic_realm;
96 unsigned int authoritative;
97 char *ntlm_auth_helper;
98 char *negotiate_ntlm_auth_helper;
99 char *ntlm_plaintext_helper;
102 /* A structure to hold per-connection information about authentications
103 that are in progress. */
105 struct _ntlm_auth_helper {
111 BUFF *out_to_helper, *in_from_helper;
116 struct _connected_user_authenticated {
120 int keepalives; /* used to detect redirected auths */
123 struct _ntlm_child_stuff {
128 typedef struct _conn_context {
129 struct _connected_user_authenticated *connected_user_authenticated;
130 } ntlm_connection_context_t;
132 typedef struct _ntlm_context {
133 struct _ntlm_auth_helper *ntlm_auth_helper;
134 struct _ntlm_auth_helper *negotiate_ntlm_auth_helper;
135 struct _ntlm_auth_helper *ntlm_plaintext_helper;
139 module AP_MODULE_DECLARE_DATA auth_ntlm_winbind_module;
141 module MODULE_VAR_EXPORT auth_ntlm_winbind_module;
144 #define NTLM_DEBUG (APLOG_DEBUG | APLOG_NOERRNO)
146 /* If we have already authenticated then allow all subsequence accesses.
147 This appears to be what IE and IIS do when talking to each other. I
148 don't think there are any security problems with this, unless someone
149 hijacks the connection, but then MS is susceptible to exactly the same
152 /* Extra apache configuration directives defined for this module */
153 static const command_rec ntlm_winbind_cmds[] = {
155 /* NTLM authentication commands */
156 AP_INIT_FLAG( "NTLMAuth", ap_set_flag_slot,
157 (void *) APR_OFFSETOF( ntlm_config_rec, ntlm_on ),
159 "set to 'on' to activate NTLM authentication" ),
161 AP_INIT_FLAG( "NegotiateAuth", ap_set_flag_slot,
162 (void *) APR_OFFSETOF(ntlm_config_rec, negotiate_on),
164 "set to 'on' to activate Negotiate authentication" ),
166 AP_INIT_FLAG( "NTLMBasicAuthoritative", ap_set_flag_slot,
167 (void *) APR_OFFSETOF(ntlm_config_rec, authoritative),
169 "set to 'off' to allow access control to be passed along to lower "
170 "modules if the UserID is not known to this module" ),
171 /* ntlm_auth location */
172 AP_INIT_TAKE1( "NTLMAuthHelper", ap_set_string_slot,
173 (void *) APR_OFFSETOF(ntlm_config_rec, ntlm_auth_helper),
175 "location and arguments to the Samba ntlm_auth utility" ),
177 AP_INIT_TAKE1( "NegotiateAuthHelper", ap_set_string_slot,
178 (void *) APR_OFFSETOF(ntlm_config_rec, negotiate_ntlm_auth_helper),
180 "location and arguments to the Samba ntlm_auth utility" ),
182 AP_INIT_TAKE1( "PlaintextAuthHelper", ap_set_string_slot,
183 (void *) APR_OFFSETOF(ntlm_config_rec, ntlm_plaintext_helper ),
185 "location and arguments to the Samba ntlm_auth utility" ),
187 /* Basic Authentication transport for non-IE browsers */
188 AP_INIT_FLAG( "NTLMBasicAuth", ap_set_flag_slot,
189 (void *) APR_OFFSETOF(ntlm_config_rec, ntlm_basic_on),
191 "set to 'on' to allow Basic authentication too" ),
193 AP_INIT_TAKE1( "NTLMBasicRealm", ap_set_string_slot,
194 (void *) APR_OFFSETOF(ntlm_config_rec, ntlm_basic_realm),
195 OR_AUTHCFG, "realm to use for Basic authentication" ),
198 /* NTLM authentication commands */
200 { "NTLMAuth", ap_set_flag_slot,
201 (void *) XtOffsetOf(ntlm_config_rec, ntlm_on),
202 OR_AUTHCFG, FLAG, "set to 'on' to activate NTLM authentication" },
204 { "NegotiateAuth", ap_set_flag_slot,
205 (void *) XtOffsetOf(ntlm_config_rec, negotiate_on),
206 OR_AUTHCFG, FLAG, "set to 'on' to activate Negotiate authentication" },
208 { "NTLMBasicAuthoritative", ap_set_flag_slot,
209 (void *) XtOffsetOf(ntlm_config_rec, authoritative),
211 "set to 'off' to allow access control to be passed along to lower "
212 "modules if the UserID is not known to this module" },
214 /* ntlm_auth location */
216 { "NTLMAuthHelper", ap_set_string_slot,
217 (void *) XtOffsetOf(ntlm_config_rec, ntlm_auth_helper), OR_AUTHCFG,
218 TAKE1, "location and arguments to the Samba ntlm_auth utility"},
220 { "NegotiateAuthHelper", ap_set_string_slot,
221 (void *) XtOffsetOf(ntlm_config_rec, negotiate_ntlm_auth_helper), OR_AUTHCFG,
222 TAKE1, "location and arguments to the Samba ntlm_auth utility"},
224 { "PlaintextAuthHelper", ap_set_string_slot,
225 (void *) XtOffsetOf(ntlm_config_rec, ntlm_plaintext_helper), OR_AUTHCFG,
226 TAKE1, "location and arguments to the Samba ntlm_auth utility"},
228 /* Basic Authentcation transport for non-IE browsers */
230 { "NTLMBasicAuth", ap_set_flag_slot,
231 (void *) XtOffsetOf(ntlm_config_rec, ntlm_basic_on),
232 OR_AUTHCFG, FLAG, "set to 'on' to allow Basic authentication too" },
234 { "NTLMBasicRealm", ap_set_string_slot,
235 (void *) XtOffsetOf(ntlm_config_rec, ntlm_basic_realm),
236 OR_AUTHCFG, TAKE1, "realm to use for Basic authentication" },
242 /* both apache1 and apache2 use this to maintain per-child context */
243 static ntlm_context_t global_ntlm_context;
246 /* apache 1 doesn't seem to have a connection context I can use */
247 static ntlm_connection_context_t global_connection_context;
249 static void cleanup_ntlm_auth_helper(void *ntlm_conn_v)
251 struct _ntlm_auth_helper *ntlm_conn = ntlm_conn_v;
253 ap_log_error( APLOG_MARK, NTLM_DEBUG, NULL, "freeing ntlm_helper" );
255 ap_bclose(ntlm_conn->out_to_helper);
256 ap_bclose(ntlm_conn->in_from_helper);
258 global_ntlm_context.ntlm_auth_helper = NULL;
261 static void cleanup_negotiate_ntlm_auth_helper(void *ntlm_conn_v)
263 struct _ntlm_auth_helper *ntlm_conn = ntlm_conn_v;
265 ap_log_error( APLOG_MARK, NTLM_DEBUG, NULL, "freeing negotiate_helper" );
267 ap_bclose(ntlm_conn->out_to_helper);
268 ap_bclose(ntlm_conn->in_from_helper);
270 global_ntlm_context.negotiate_ntlm_auth_helper = NULL;
273 static void cleanup_ntlm_plaintext_helper( void *ntlm_conn_v ) {
274 struct _ntlm_auth_helper *ntlm_conn = ntlm_conn_v;
276 ap_log_error( APLOG_MARK, NTLM_DEBUG, NULL, "freeing plaintext_helper" );
278 ap_bclose( ntlm_conn->out_to_helper );
279 ap_bclose( ntlm_conn->in_from_helper );
281 global_ntlm_context.ntlm_plaintext_helper = NULL;
284 /* Dispose of a connected user */
286 static void cleanup_connected_user_authenticated(void *unused)
288 ap_log_error( APLOG_MARK, NTLM_DEBUG, NULL, "freeing user" );
289 global_connection_context.connected_user_authenticated = NULL;
293 /* helper function to pull out the context data */
294 static ntlm_connection_context_t *get_connection_context( struct conn_rec *connection ) {
295 ntlm_connection_context_t *retval = NULL;
298 retval = (ntlm_connection_context_t *)ap_get_module_config( connection->conn_config,
299 &auth_ntlm_winbind_module );
301 retval = &global_connection_context;
306 /* Authorisation has failed - we set some headers so the client can
307 get the hint and prompt for a password from the user. */
310 note_auth_failure(request_rec * r, const char *negotiate_auth_line)
312 ntlm_config_rec *crec
313 = (ntlm_config_rec *) ap_get_module_config(r->per_dir_config,
314 &auth_ntlm_winbind_module);
316 ntlm_connection_context_t *ctxt = get_connection_context( r->connection );
318 /* MSIE will simply reply to the first, not strongest, protocol listed */
319 if (crec->negotiate_on) {
320 line = apr_pstrcat(r->pool, NEGOTIATE_AUTH_NAME, " ",
321 negotiate_auth_line, NULL);
322 apr_table_add(r->err_headers_out,
323 r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
327 /* Set header for NTLM authentication */
330 apr_table_add(r->err_headers_out,
331 r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
335 /* Set header for basic authentication so client can use this if
338 if (crec->ntlm_basic_on) {
339 line = apr_pstrcat(r->pool,
340 "Basic realm=\"", crec->ntlm_basic_realm, "\"",
342 apr_table_add(r->err_headers_out,
343 r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
347 if ( ctxt->connected_user_authenticated &&
348 ctxt->connected_user_authenticated->pool ) {
349 apr_pool_destroy( ctxt->connected_user_authenticated->pool );
352 return HTTP_UNAUTHORIZED;
357 get_auth_header(request_rec * r, ntlm_config_rec * crec, const char *auth_scheme)
359 const char *auth_line = apr_table_get(r->headers_in,
360 r->proxyreq ? "Proxy-Authorization"
364 RERROR( APR_EINIT, "no auth line present" );
367 if (strcmp(ap_getword_white(r->pool, &auth_line), auth_scheme)) {
368 RERROR( APR_EINIT, "%s auth name not present", auth_scheme );
375 static int helper_child(void *child_stuff, child_info *pinfo)
377 struct _ntlm_child_stuff *cld = (struct _ntlm_child_stuff *) child_stuff;
378 request_rec *r = cld->r;
379 char *argv0 = cld->argv0;
382 RAISE_SIGSTOP(CGI_CHILD);
384 /* Transmute outselves into the helper
387 ap_cleanup_for_exec();
389 child_pid = ap_call_exec(r, pinfo, argv0, NULL, 1);
391 /* Uh oh. Still here. Where's the kaboom? There was supposed to be an
392 * EARTH-shattering kaboom!
394 * Oh, well. Muddle through as best we can...
396 * Note that only stderr is available at this point, so don't pass in
397 * a server to aplog_error.
400 RERROR( errno, "exec of %s failed", r->filename);
409 send_auth_reply(request_rec * r, const char *auth_scheme, const char *reply)
411 RDEBUG( "sending back %s", reply );
412 /* Read negotiate from ntlm_auth */
414 apr_table_setn(r->err_headers_out,
415 r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
416 apr_psprintf(r->pool, "%s %s", auth_scheme, reply));
418 /* This is to make sure that when receiving later messages
419 * that the r->connection is still alive after sending the
420 * nonce to the client. This code is written, and the problem
421 * was pointed out by Michael Cai.
423 if(r->connection->keepalives >= r->server->keep_alive_max)
425 RDEBUG("Decrement the connection request count to keep it alive");
426 r->connection->keepalives -= 1;
429 return HTTP_UNAUTHORIZED;
432 /* get the current request's auth helper or fork one */
433 static struct _ntlm_auth_helper *get_auth_helper( request_rec *r, struct _ntlm_auth_helper *auth_helper, char *cmd, void (*cleanup)(void *)) {
435 apr_procattr_t *attr;
438 if ( auth_helper == NULL ) {
439 struct _ntlm_child_stuff cld;
443 apr_pool_create_ex( &pool, NULL, NULL, NULL ); /* xxx return code */
445 pool = ap_make_sub_pool( NULL );
447 auth_helper = apr_pcalloc( pool, sizeof( struct _ntlm_auth_helper ));
448 auth_helper->pool = pool;
449 auth_helper->helper_pid = 0;
452 apr_tokenize_to_argv( cmd, &argv_out, pool );
454 ap_register_cleanup( pool, auth_helper, cleanup, ap_null_cleanup );
460 apr_procattr_create( &attr, pool );
461 apr_procattr_io_set( attr, APR_FULL_BLOCK, APR_FULL_BLOCK, APR_NO_PIPE );
462 apr_procattr_error_check_set( attr, 1 );
463 auth_helper->proc = (apr_proc_t *)apr_pcalloc(pool, sizeof(apr_proc_t)) ;
464 if ( apr_proc_create( auth_helper->proc, argv_out[0], (const char * const *)argv_out, NULL, attr, pool ) != APR_SUCCESS ) {
465 RERROR( errno, "couldn't spawn child ntlm helper process: %s", argv_out[0]);
468 auth_helper->helper_pid = auth_helper->proc->pid;
470 auth_helper->helper_pid = ap_bspawn_child(pool, helper_child,
471 (void *) &cld, just_wait,
472 &auth_helper->out_to_helper,
473 &auth_helper->in_from_helper,
476 if (auth_helper->helper_pid == -1) {
477 RERROR( errno, "couldn't spawn child ntlm helper process: %s", cld.argv0);
482 RDEBUG( "Launched ntlm_helper, pid %d", auth_helper->helper_pid );
484 RDEBUG( "Using existing auth helper %d", auth_helper->helper_pid );
490 /* Call winbind to authenticate a (user, password)
492 static int winbind_authenticate_plaintext( request_rec *r, ntlm_config_rec * crec, char *user, char *pass)
494 ntlm_connection_context_t *ctxt = get_connection_context( r->connection );
496 char args_to_helper[HUGE_STRING_LEN];
497 char args_from_helper[HUGE_STRING_LEN];
498 unsigned int bytes_written;
501 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 ) {
502 return HTTP_INTERNAL_SERVER_ERROR;
505 if ( ctxt->connected_user_authenticated == NULL ) {
508 RDEBUG( "creating auth user" );
511 apr_pool_create_ex( &pool, r->connection->pool, NULL, NULL );
513 pool = ap_make_sub_pool(r->connection->pool);
516 ctxt->connected_user_authenticated =
517 apr_pcalloc(pool, sizeof( struct _connected_user_authenticated));
520 ap_register_cleanup(pool,ctxt->connected_user_authenticated,
521 cleanup_connected_user_authenticated, ap_null_cleanup );
524 ctxt->connected_user_authenticated->pool = pool;
525 ctxt->connected_user_authenticated->user = NULL;
526 ctxt->connected_user_authenticated->auth_type = NULL;
528 /* what, we're already authenticated? */
532 snprintf( args_to_helper, HUGE_STRING_LEN, "%s %s\n", user, pass );
535 bytes_written = strlen( args_to_helper );
536 apr_file_write( global_ntlm_context.ntlm_plaintext_helper->proc->in, args_to_helper, &bytes_written );
538 bytes_written = ap_bwrite( global_ntlm_context.ntlm_plaintext_helper->out_to_helper, args_to_helper, strlen( args_to_helper ));
541 if ( bytes_written < strlen( args_to_helper )) {
542 RDEBUG( "failed to write user/pass to helper - wrote %d bytes", bytes_written );
543 apr_pool_destroy( global_ntlm_context.ntlm_plaintext_helper->pool );
544 apr_pool_destroy( ctxt->connected_user_authenticated->pool );
545 return HTTP_INTERNAL_SERVER_ERROR;
549 apr_file_flush( global_ntlm_context.ntlm_plaintext_helper->proc->in );
550 if ( apr_file_gets( args_from_helper, HUGE_STRING_LEN, global_ntlm_context.ntlm_plaintext_helper->proc->out ) == APR_SUCCESS ) {
551 bytes_read = strlen( args_from_helper );
556 ap_bflush( global_ntlm_context.ntlm_plaintext_helper->out_to_helper );
557 bytes_read = ap_bgets( args_from_helper, HUGE_STRING_LEN, global_ntlm_context.ntlm_plaintext_helper->in_from_helper );
559 if ( bytes_read == 0 ) {
560 RERROR( errno, "early EOF from helper" );
561 apr_pool_destroy(global_ntlm_context.ntlm_plaintext_helper->pool);
562 apr_pool_destroy(ctxt->connected_user_authenticated->pool);
564 return HTTP_INTERNAL_SERVER_ERROR;
565 } else if (bytes_read == -1) {
566 RERROR( errno, "helper died!" );
567 apr_pool_destroy(global_ntlm_context.ntlm_plaintext_helper->pool);
568 apr_pool_destroy(ctxt->connected_user_authenticated->pool);
570 return HTTP_INTERNAL_SERVER_ERROR;
571 } else if (bytes_read < 2) {
572 RERROR( errno, "failed to read NTLMSSP string from helper - only got %d bytes", bytes_read);
573 apr_pool_destroy(global_ntlm_context.ntlm_plaintext_helper->pool);
574 apr_pool_destroy(ctxt->connected_user_authenticated->pool);
576 return HTTP_INTERNAL_SERVER_ERROR;
579 newline = strchr(args_from_helper, '\n');
580 if (newline != NULL) {
584 RDEBUG( "got response: %s", args_from_helper );
586 if ( strncmp( args_from_helper, "OK", 2 ) == 0 ) {
587 RDEBUG( "authentication succeeded!" );
588 ctxt->connected_user_authenticated->user = apr_pstrdup(ctxt->connected_user_authenticated->pool, user);
589 ctxt->connected_user_authenticated->keepalives = r->connection->keepalives;
591 r->user = ctxt->connected_user_authenticated->user;
592 r->ap_auth_type = apr_pstrdup(r->connection->pool, "Basic");
593 /* disconnect the child process */
594 /* apr_proc_kill( global_ntlm_context.ntlm_plaintext_helper->proc, 9 );
595 apr_proc_wait( global_ntlm_context.ntlm_plaintext_helper->proc, &exit, &why, APR_WAIT );*/
597 r->connection->user = ctxt->connected_user_authenticated->user;
598 r->connection->ap_auth_type = ap_pstrdup(r->connection->pool, "Basic");
600 RDEBUG( "authenticated %s", ctxt->connected_user_authenticated->user );
603 if ( strncmp( args_from_helper, "ERR", 3 ) == 0 ) {
604 RDEBUG( "username/password incorrect" );
605 return note_auth_failure( r, NULL );
607 RDEBUG( "unknown helper response %s", args_from_helper );
608 return HTTP_INTERNAL_SERVER_ERROR;
613 /* Process a message received from the client. This can be a request for a
614 challenge (type 1) or a request to authenticate a challenge/response
618 process_msg(request_rec * r, ntlm_config_rec * crec, const char *auth_type)
620 const char *client_msg;
621 const char *message_type;
624 char args_to_helper[HUGE_STRING_LEN];
625 char args_from_helper[HUGE_STRING_LEN];
626 ntlm_connection_context_t *ctxt = get_connection_context( r->connection );
627 unsigned int bytes_written;
629 struct _ntlm_auth_helper *auth_helper;
631 /* If this is the first request with this connection, then create
632 * a ntlm_auth_helper entry for it. It will be cleaned up when the
633 * connection is dropped */
635 if (strcmp(auth_type, NEGOTIATE_AUTH_NAME) == 0) {
636 auth_helper = get_auth_helper( r, global_ntlm_context.negotiate_ntlm_auth_helper, crec->negotiate_ntlm_auth_helper, CLEANUP(cleanup_negotiate_ntlm_auth_helper));
637 global_ntlm_context.negotiate_ntlm_auth_helper = auth_helper;
638 } else if (strcmp(auth_type, NTLM_AUTH_NAME) == 0) {
639 auth_helper = get_auth_helper( r, global_ntlm_context.ntlm_auth_helper, crec->ntlm_auth_helper, CLEANUP(cleanup_ntlm_auth_helper));
640 global_ntlm_context.ntlm_auth_helper = auth_helper;
645 if ( auth_helper == NULL ) {
646 return HTTP_INTERNAL_SERVER_ERROR;
649 if ( ctxt->connected_user_authenticated == NULL ) {
652 RDEBUG( "creating auth user" );
655 apr_pool_create_ex( &pool, r->connection->pool, NULL, NULL );
657 pool = ap_make_sub_pool(r->connection->pool);
660 ctxt->connected_user_authenticated =
661 apr_pcalloc(pool, sizeof( struct _connected_user_authenticated));
664 ap_register_cleanup(pool,ctxt->connected_user_authenticated,
665 cleanup_connected_user_authenticated, ap_null_cleanup );
668 ctxt->connected_user_authenticated->pool = pool;
669 ctxt->connected_user_authenticated->user = NULL;
670 ctxt->connected_user_authenticated->auth_type = NULL;
677 /* Decode the information the WWW-Authenticate header */
678 if ((client_msg = get_auth_header(r, crec, auth_type)) == NULL) {
679 RDEBUG( "client did not return NTLM authentication header");
680 return note_auth_failure(r, NULL);
684 snprintf(args_to_helper, HUGE_STRING_LEN, "%s %s\n", message_type, client_msg);
687 bytes_written = strlen( args_to_helper );
688 apr_file_write( auth_helper->proc->in, args_to_helper, &bytes_written );
690 bytes_written = ap_bwrite(auth_helper->out_to_helper, args_to_helper, strlen(args_to_helper));
692 if (bytes_written < strlen(args_to_helper)) {
693 RDEBUG("failed to write NTLMSSP string to helper - wrote %d bytes", bytes_written);
694 apr_pool_destroy(auth_helper->pool);
695 apr_pool_destroy(ctxt->connected_user_authenticated->pool);
697 return HTTP_INTERNAL_SERVER_ERROR;
701 apr_file_flush( auth_helper->proc->in );
703 RDEBUG( "parsing reply from helper to %s", args_to_helper );
705 if ( apr_file_gets(args_from_helper, HUGE_STRING_LEN, auth_helper->proc->out ) == APR_SUCCESS ) {
706 bytes_read = strlen( args_from_helper );
711 ap_bflush(auth_helper->out_to_helper);
713 bytes_read = ap_bgets(args_from_helper, HUGE_STRING_LEN, auth_helper->in_from_helper);
716 if (bytes_read == 0) {
717 RERROR( errno, "early EOF from helper" );
718 apr_pool_destroy(auth_helper->pool);
719 apr_pool_destroy(ctxt->connected_user_authenticated->pool);
721 return HTTP_INTERNAL_SERVER_ERROR;
722 } else if (bytes_read == -1) {
723 RERROR( errno, "helper died!");
724 apr_pool_destroy(auth_helper->pool);
725 apr_pool_destroy(ctxt->connected_user_authenticated->pool);
727 return HTTP_INTERNAL_SERVER_ERROR;
728 } else if (bytes_read < 2) {
729 RERROR( errno, "failed to read NTLMSSP string from helper - only got %d bytes", bytes_read );
730 apr_pool_destroy(auth_helper->pool);
731 apr_pool_destroy(ctxt->connected_user_authenticated->pool);
733 return HTTP_INTERNAL_SERVER_ERROR;
736 newline = strchr(args_from_helper, '\n');
737 if (newline != NULL) {
741 RDEBUG( "got response: %s", args_from_helper );
743 /* inspect message type */
745 childarg = strchr(args_from_helper, ' ');
746 if (childarg == NULL) {
747 RERROR( errno, "failed to parse response from helper");
748 apr_pool_destroy(auth_helper->pool);
749 apr_pool_destroy(ctxt->connected_user_authenticated->pool);
751 return HTTP_INTERNAL_SERVER_ERROR;
755 if (strcasecmp(auth_type, NTLM_AUTH_NAME) == 0) {
756 /* if TT, send to client */
758 if (strncmp(args_from_helper, "TT ", 3) == 0) {
759 return send_auth_reply(r, auth_type, childarg);
762 /* if NA, not authenticated */
764 if (strncmp(args_from_helper, "NA ", 3) == 0) {
765 RDEBUG("user not authenticated: %s", childarg);
766 return note_auth_failure(r, NULL);
769 /* if AF, record username */
770 if (strncmp(args_from_helper, "AF ", 3) == 0) {
771 ctxt->connected_user_authenticated->user =
772 apr_pstrdup(ctxt->connected_user_authenticated->pool,
774 ctxt->connected_user_authenticated->keepalives =
775 r->connection->keepalives;
777 r->user = ctxt->connected_user_authenticated->user;
778 r->ap_auth_type = apr_pstrdup(r->connection->pool, auth_type);
779 /* disconnect the child process */
780 /* apr_proc_kill( auth_helper->proc, 9 );
781 apr_proc_wait( auth_helper->proc, &exit, &why, APR_WAIT );*/
783 r->connection->user = ctxt->connected_user_authenticated->user;
784 r->connection->ap_auth_type = ap_pstrdup(r->connection->pool, auth_type);
786 RDEBUG( "authenticated %s",
787 ctxt->connected_user_authenticated->user );
790 } else if (strcasecmp(auth_type, NEGOTIATE_AUTH_NAME) == 0) {
792 /* The child's reply contains 3 parts:
793 - The code: TT, AF or NA
794 - The blob to send to the client, coded in base64
796 For TT it's a dummy '*'
797 For AF it's domain\\user
798 For NA it's the NT error code
801 char *childarg3 = strchr(childarg, ' ');
802 if (childarg3 == NULL) {
803 RERROR( errno, "failed to parse response from helper");
804 apr_pool_destroy(auth_helper->pool);
805 apr_pool_destroy(ctxt->connected_user_authenticated->pool);
807 return HTTP_INTERNAL_SERVER_ERROR;
812 /* if TT, send to client */
814 if (strncmp(args_from_helper, "TT ", 3) == 0) {
815 return send_auth_reply(r, auth_type, childarg);
818 /* if NA, not authenticated */
820 if (strncmp(args_from_helper, "NA ", 3) == 0) {
821 RDEBUG("user not authenticated: %s", childarg3);
822 return note_auth_failure(r, childarg);
825 /* if AF, record username */
826 if (strncmp(args_from_helper, "AF ", 3) == 0) {
827 ctxt->connected_user_authenticated->user =
828 apr_pstrdup(ctxt->connected_user_authenticated->pool,
831 r->user = ctxt->connected_user_authenticated->user;
832 ctxt->connected_user_authenticated->auth_type =
833 apr_pstrdup(r->connection->pool, auth_type);
834 r->ap_auth_type = ctxt->connected_user_authenticated->auth_type;
836 r->connection->user = ctxt->connected_user_authenticated->user;
837 ctxt->connected_user_authenticated->auth_type = ap_pstrdup(r->connection->pool, auth_type);
838 r->connection->ap_auth_type = ctxt->connected_user_authenticated->auth_type;
841 if (!strcmp("*", childarg)) {
842 /* Send last leg (possible mutual authentication token) */
843 apr_table_setn(r->headers_out,
844 r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
845 apr_psprintf(r->pool, "%s %s", auth_type, childarg));
848 RDEBUG( "wow, we're all happy here" );
856 /* if BH, helper is busted */
858 if (strncmp(args_from_helper, "BH ", 3) == 0) {
859 RERROR( APR_EGENERAL, "ntlm_auth reports Broken Helper: %s", args_from_helper);
861 RERROR( APR_EGENERAL, "could not parse %s helper callback: %s", auth_type, args_from_helper);
864 apr_pool_destroy(auth_helper->pool);
865 apr_pool_destroy(ctxt->connected_user_authenticated->pool);
867 return HTTP_INTERNAL_SERVER_ERROR;
870 /* Called to create a configuration structure for each <Directory> section
871 that uses the ntlm auth module. */
874 ntlm_winbind_dir_config(apr_pool_t * p, char *d)
876 ntlm_config_rec *crec
877 = (ntlm_config_rec *) apr_pcalloc(p, sizeof(ntlm_config_rec));
879 /* Set the defaults. */
881 crec->authoritative = 1;
883 crec->negotiate_on = 0;
884 crec->ntlm_basic_on = 0;
885 crec->ntlm_basic_realm = "REALM";
886 crec->ntlm_auth_helper = "ntlm_auth --helper-protocol=squid-2.5-ntlmssp";
887 crec->negotiate_ntlm_auth_helper = "ntlm_auth --helper-protocol=gss-spnego";
888 crec->ntlm_plaintext_helper = "ntlm_auth --helper-protocol=squid-2.5-basic";
893 /* Authenticate a user using basic authentication */
895 authenticate_basic_user(request_rec * r, ntlm_config_rec * crec,
896 const char *auth_line_after_Basic)
898 char *sent_user = NULL, *sent_pw;
899 int result = HTTP_UNAUTHORIZED;
901 while (*auth_line_after_Basic == ' ' || *auth_line_after_Basic == '\t')
902 auth_line_after_Basic++;
905 sent_user = apr_pcalloc( r->pool, apr_base64_decode_len( auth_line_after_Basic ));
906 apr_base64_decode( sent_user, auth_line_after_Basic );
908 sent_user = ap_pbase64decode(r->pool, auth_line_after_Basic);
911 if (sent_user != NULL) {
914 if ((sent_pw = strchr(sent_user, ':')) != NULL) {
919 if ((s = strchr(sent_user, '\\')) != NULL
920 || (s = strchr(sent_user, '/')) != NULL) {
922 /* Authenticate this user as a domain user */
924 result = winbind_authenticate_plaintext( r, crec, sent_user, sent_pw);
926 RDEBUG("authenticate domain user %s: %s", sent_user,
927 (result == OK) ? "OK" : "FAILED");
931 RDEBUG("can't extract user from %s", auth_line_after_Basic );
932 sent_user = sent_pw = "";
935 RDEBUG("authenticate local user %s: %s", sent_user,
936 (result == OK) ? "OK" : "FAILED");
942 /* Check the user id from a http request */
943 static int check_user_id(request_rec * r) {
944 ntlm_config_rec *crec =
945 (ntlm_config_rec *) ap_get_module_config(r->per_dir_config,
946 &auth_ntlm_winbind_module);
947 ntlm_connection_context_t *ctxt = get_connection_context( r->connection );
948 const char *auth_line = apr_table_get(r->headers_in,
949 r->proxyreq ? "Proxy-Authorization"
951 const char *auth_line2;
953 /* Trust the authentication on an existing connection */
954 if (ctxt->connected_user_authenticated && ctxt->connected_user_authenticated->user) {
955 /* internal redirects cause this to get called more than once
956 per request on Apache 1.x. This compensates by checking if
957 the connection is the same as the one we authed against */
958 if ( !auth_line || ( ctxt->connected_user_authenticated->keepalives == r->connection->keepalives )) {
959 /* silently accept login with same credentials */
960 RDEBUG( "retaining user %s",
961 ctxt->connected_user_authenticated->user );
962 RDEBUG( "keepalives: %d", r->connection->keepalives );
964 r->user = ctxt->connected_user_authenticated->user;
965 r->ap_auth_type = ctxt->connected_user_authenticated->auth_type;
967 r->connection->user = ctxt->connected_user_authenticated->user;
968 r->connection->ap_auth_type = ctxt->connected_user_authenticated->auth_type;
973 /* client wishes to re-authenticate this TCP socket */
974 if ( ctxt->connected_user_authenticated->pool ) {
975 apr_pool_destroy(ctxt->connected_user_authenticated->pool);
976 ctxt->connected_user_authenticated = NULL;
981 /* No authentication line given. Return a 401 and a WWW-Authenticate
982 header so authentication can commence. */
985 note_auth_failure(r, NULL);
986 return HTTP_UNAUTHORIZED;
989 /* If basic authentication is requested and enabled, try to
990 authenticate the user with basic */
992 auth_line2 = auth_line;
993 if (crec->ntlm_basic_on
994 && strcasecmp(ap_getword(r->pool, &auth_line2, ' '), "Basic") == 0) {
995 RDEBUG( "trying basic auth" );
996 return authenticate_basic_user(r, crec, &auth_line[5]); /* seems clunky */
999 /* Process a 'Negotiate' SPNEGO over http message */
1000 auth_line2 = auth_line;
1001 if (strcasecmp(ap_getword(r->pool, &auth_line2, ' '), NEGOTIATE_AUTH_NAME) == 0) {
1002 if (!crec->negotiate_on) {
1003 RDEBUG("Negotiate authentication is not enabled");
1006 return process_msg(r, crec, NEGOTIATE_AUTH_NAME);
1010 /* Process a NTLM over http message */
1012 auth_line2 = auth_line;
1013 if (strcasecmp(ap_getword(r->pool, &auth_line2, ' '), NTLM_AUTH_NAME) == 0) {
1014 if (!crec->ntlm_on) {
1015 RDEBUG("NTLM authentication is not enabled");
1018 RDEBUG( "doing ntlm auth dance" );
1019 return process_msg(r, crec, NTLM_AUTH_NAME);
1023 if (ctxt->connected_user_authenticated && ctxt->connected_user_authenticated->pool ) {
1024 apr_pool_destroy(ctxt->connected_user_authenticated->pool);
1027 RDEBUG( "declined" );
1032 /* Dispatch list for API hooks */
1034 static int ntlm_pre_conn(conn_rec *c, void *csd) {
1035 ntlm_connection_context_t *ctxt = apr_pcalloc(c->pool, sizeof(ntlm_connection_context_t));
1037 ap_set_module_config(c->conn_config, &auth_ntlm_winbind_module, ctxt);
1043 static void register_hooks(apr_pool_t *pool)
1045 ap_hook_pre_connection(ntlm_pre_conn,NULL,NULL,APR_HOOK_MIDDLE);
1046 ap_hook_check_user_id(check_user_id,NULL,NULL,APR_HOOK_MIDDLE);
1049 module AP_MODULE_DECLARE_DATA auth_ntlm_winbind_module = {
1050 STANDARD20_MODULE_STUFF,
1051 ntlm_winbind_dir_config, /* create per-dir config structures */
1052 NULL, /* merge per-dir config structures */
1053 NULL, /* create per-server config structures */
1054 NULL, /* merge per-server config structures */
1055 ntlm_winbind_cmds, /* table of config file commands */
1056 register_hooks, /* register hooks */
1059 module MODULE_VAR_EXPORT auth_ntlm_winbind_module = {
1060 STANDARD_MODULE_STUFF,
1061 NULL, /* module initializer */
1062 ntlm_winbind_dir_config, /* create per-dir config structures */
1063 NULL, /* merge per-dir config structures */
1064 NULL, /* create per-server config structures */
1065 NULL, /* merge per-server config structures */
1066 ntlm_winbind_cmds, /* table of config file commands */
1067 NULL, /* [#8] MIME-typed-dispatched handlers */
1068 NULL, /* [#1] URI to filename translation */
1069 check_user_id, /* [#4] validate user id from request */
1070 NULL, /* [#5] check if the user is ok _here_ */
1071 NULL, /* [#3] check access by host address */
1072 NULL, /* [#6] determine MIME type */
1073 NULL, /* [#7] pre-run fixups */
1074 NULL, /* [#9] log a transaction */
1075 NULL, /* [#2] header parser */
1076 NULL, /* child_init */
1077 NULL, /* child_exit */
1078 NULL /* [#0] post read-request */
1080 ,NULL, /* EAPI: add_module */
1081 NULL, /* EAPI: remove_module */
1082 NULL, /* EAPI: rewrite_command */
1083 NULL /* EAPI: new_connection */
1091 * indent-tabs-mode: nil