r685: Add missing auth_ prefix.
[jerry/mod_auth_ntlm_winbind.git] / mod_auth_ntlm_winbind.c
1 /*
2  *  mod_auth_ntlm_winbind.c -- Apache auth_ntlm_winbind module
3  *
4  *  [Originally autogenerated via ``apxs -n ntlm_winbind -g'']
5  *
6  * Based on:
7  *     mod_ntlm.c for Win32 by Tim Costello <tim.costello@bigfoot.com>
8  *     pam_smb by Dave Airlie <Dave.Airlie@ul.ie>
9  *
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
15  *
16  * THIS SOFTWARE IS PROVIDED ``AS IS`` AND ANY EXPRESSED OR IMPLIED
17  * WARRANTIES ARE DISCLAIMED.
18  *
19  * This code may be freely distributed, as long the above notices are
20  * reproduced.
21  */
22 /* (CGI fork code)
23  * Copyright 1999-2004 The Apache Software Foundation
24  *
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
28  *
29  *     http://www.apache.org/licenses/LICENSE-2.0
30  *
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.
36  */
37
38
39 /*
40  * The sections OVERVIEW, INSTALLATION, and CONFIGURATION are available from
41  * the README file in this directory.
42  */
43
44 #include "httpd.h"
45 #include "http_config.h"
46 #include "http_protocol.h"
47 #include "http_log.h"
48 #include "http_core.h"
49 #include "ap_config.h"
50 #include "util_script.h" /* for ap_call_exec */
51 #include <assert.h>
52
53 #ifdef APACHE2
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"
60
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
64 #else
65
66 /* compat stuff */
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)
76
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 )
79 #define CLEANUP(x) x
80 #endif
81
82 /* The name of the NTLM authentication scheme.  This appears in the
83    'WWW-Authenticate' header in the initial HTTP request. */
84
85 #define NTLM_AUTH_NAME "NTLM"
86 #define NEGOTIATE_AUTH_NAME "Negotiate"
87
88 /* A structure to hold information about the configuration for the
89    mod_auth_ntlm_winbind apache module. */
90
91 typedef struct _ntlm_config_struct {
92     unsigned int ntlm_on;
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;
100 } ntlm_config_rec;
101
102 /* A structure to hold per-connection information about authentications
103    that are in progress. */
104
105 struct _ntlm_auth_helper {
106     int sent_challenge;
107     int helper_pid;
108 #ifdef APACHE2
109     apr_proc_t *proc;
110 #else
111     BUFF *out_to_helper, *in_from_helper;
112 #endif
113     apr_pool_t *pool;
114 };
115
116 struct _connected_user_authenticated {
117     char *user;
118     char *auth_type;
119     apr_pool_t *pool;
120     int keepalives; /* used to detect redirected auths */
121 };
122
123 struct _ntlm_child_stuff {
124     request_rec *r;
125     char *argv0;
126 };
127
128 typedef struct _conn_context {
129     struct _connected_user_authenticated *connected_user_authenticated;
130 } ntlm_connection_context_t;
131
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;
136 } ntlm_context_t;
137
138 #ifdef APACHE2
139 module AP_MODULE_DECLARE_DATA auth_ntlm_winbind_module;
140 #else
141 module MODULE_VAR_EXPORT auth_ntlm_winbind_module;
142 #endif
143
144 #define NTLM_DEBUG (APLOG_DEBUG | APLOG_NOERRNO)
145
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
150    problem. */
151
152 /* Extra apache configuration directives defined for this module */
153 static const command_rec ntlm_winbind_cmds[] = {
154 #ifdef APACHE2
155     /* NTLM authentication commands */
156     AP_INIT_FLAG( "NTLMAuth", ap_set_flag_slot,
157                   (void *) APR_OFFSETOF( ntlm_config_rec, ntlm_on ),
158                   OR_AUTHCFG,
159                   "set to 'on' to activate NTLM authentication" ),
160
161     AP_INIT_FLAG( "NegotiateAuth", ap_set_flag_slot,
162                   (void *) APR_OFFSETOF(ntlm_config_rec, negotiate_on),
163                   OR_AUTHCFG,
164                   "set to 'on' to activate Negotiate authentication" ),
165
166     AP_INIT_FLAG( "NTLMBasicAuthoritative", ap_set_flag_slot,
167                   (void *) APR_OFFSETOF(ntlm_config_rec, authoritative),
168                   OR_AUTHCFG,
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),
174                    OR_AUTHCFG,
175                    "location and arguments to the Samba ntlm_auth utility" ),
176
177     AP_INIT_TAKE1( "NegotiateAuthHelper", ap_set_string_slot,
178                    (void *) APR_OFFSETOF(ntlm_config_rec, negotiate_ntlm_auth_helper),
179                    OR_AUTHCFG,
180                    "location and arguments to the Samba ntlm_auth utility" ),
181
182     AP_INIT_TAKE1( "PlaintextAuthHelper", ap_set_string_slot,
183                    (void *) APR_OFFSETOF(ntlm_config_rec, ntlm_plaintext_helper ),
184                    OR_AUTHCFG,
185                    "location and arguments to the Samba ntlm_auth utility" ),
186
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),
190                   OR_AUTHCFG,
191                   "set to 'on' to allow Basic authentication too" ),
192
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" ),
196
197 #else
198     /* NTLM authentication commands */
199
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" },
203
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" },
207
208     { "NTLMBasicAuthoritative", ap_set_flag_slot,
209       (void *) XtOffsetOf(ntlm_config_rec, authoritative),
210       OR_AUTHCFG, FLAG,
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" },
213
214     /* ntlm_auth location */
215
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"},
219
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"},
223
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"},
227
228     /* Basic Authentcation transport for non-IE browsers */
229
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" },
233
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" },
237 #endif
238
239     { NULL }
240 };
241
242 /* both apache1 and apache2 use this to maintain per-child context */
243 static ntlm_context_t global_ntlm_context;
244
245 #ifndef APACHE2
246 /* apache 1 doesn't seem to have a connection context I can use */
247 static ntlm_connection_context_t global_connection_context;
248
249 static void cleanup_ntlm_auth_helper(void *ntlm_conn_v)
250 {
251     struct _ntlm_auth_helper *ntlm_conn = ntlm_conn_v;
252
253     ap_log_error( APLOG_MARK, NTLM_DEBUG, NULL, "freeing ntlm_helper" );
254
255     ap_bclose(ntlm_conn->out_to_helper);
256     ap_bclose(ntlm_conn->in_from_helper);
257
258     global_ntlm_context.ntlm_auth_helper = NULL;
259 }
260
261 static void cleanup_negotiate_ntlm_auth_helper(void *ntlm_conn_v)
262 {
263     struct _ntlm_auth_helper *ntlm_conn = ntlm_conn_v;
264
265     ap_log_error( APLOG_MARK, NTLM_DEBUG, NULL, "freeing negotiate_helper" );
266
267     ap_bclose(ntlm_conn->out_to_helper);
268     ap_bclose(ntlm_conn->in_from_helper);
269
270     global_ntlm_context.negotiate_ntlm_auth_helper = NULL;
271 }
272
273 static void cleanup_ntlm_plaintext_helper( void *ntlm_conn_v ) {
274     struct _ntlm_auth_helper *ntlm_conn = ntlm_conn_v;
275
276     ap_log_error( APLOG_MARK, NTLM_DEBUG, NULL, "freeing plaintext_helper" );
277
278     ap_bclose( ntlm_conn->out_to_helper );
279     ap_bclose( ntlm_conn->in_from_helper );
280
281     global_ntlm_context.ntlm_plaintext_helper = NULL;
282 }
283
284 /* Dispose of a connected user */
285
286 static void cleanup_connected_user_authenticated(void *unused)
287 {
288     ap_log_error( APLOG_MARK, NTLM_DEBUG, NULL, "freeing user" );
289     global_connection_context.connected_user_authenticated = NULL;
290 }
291 #endif
292
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;
296
297 #ifdef APACHE2
298     retval = (ntlm_connection_context_t *)ap_get_module_config( connection->conn_config,
299                                                                 &auth_ntlm_winbind_module );
300 #else
301     retval = &global_connection_context;
302 #endif
303     return retval;
304 }
305
306 /* Authorisation has failed - we set some headers so the client can
307    get the hint and prompt for a password from the user. */
308
309 static int
310 note_auth_failure(request_rec * r, const char *negotiate_auth_line)
311 {
312     ntlm_config_rec *crec
313         = (ntlm_config_rec *) ap_get_module_config(r->per_dir_config,
314                                                    &auth_ntlm_winbind_module);
315     char *line;
316     ntlm_connection_context_t *ctxt = get_connection_context( r->connection );
317
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",
324                       line);
325     }
326
327     /* Set header for NTLM authentication */
328
329     if (crec->ntlm_on) {
330         apr_table_add(r->err_headers_out,
331                       r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
332                       NTLM_AUTH_NAME);
333     }
334
335     /* Set header for basic authentication so client can use this if
336        supported. */
337
338     if (crec->ntlm_basic_on) {
339         line = apr_pstrcat(r->pool,
340                            "Basic realm=\"", crec->ntlm_basic_realm, "\"",
341                            NULL);
342         apr_table_add(r->err_headers_out,
343                       r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
344                       line);
345     }
346
347     if ( ctxt->connected_user_authenticated &&
348          ctxt->connected_user_authenticated->pool ) {
349         apr_pool_destroy( ctxt->connected_user_authenticated->pool );
350     }
351
352     return HTTP_UNAUTHORIZED;
353 }
354
355
356 const char *
357 get_auth_header(request_rec * r, ntlm_config_rec * crec, const char *auth_scheme)
358 {
359     const char *auth_line = apr_table_get(r->headers_in,
360                                           r->proxyreq ? "Proxy-Authorization"
361                                           : "Authorization");
362
363     if (!auth_line) {
364         RERROR( APR_EINIT, "no auth line present" );
365         return NULL;
366     }
367     if (strcmp(ap_getword_white(r->pool, &auth_line), auth_scheme)) {
368         RERROR( APR_EINIT, "%s auth name not present", auth_scheme );
369         return NULL;
370     }
371     return auth_line;
372 }
373
374 #ifndef APACHE2
375 static int helper_child(void *child_stuff, child_info *pinfo)
376 {
377     struct _ntlm_child_stuff *cld = (struct _ntlm_child_stuff *) child_stuff;
378     request_rec *r = cld->r;
379     char *argv0 = cld->argv0;
380     int child_pid;
381
382     RAISE_SIGSTOP(CGI_CHILD);
383
384     /* Transmute outselves into the helper
385      */
386
387     ap_cleanup_for_exec();
388
389     child_pid = ap_call_exec(r, pinfo, argv0, NULL, 1);
390
391     /* Uh oh.  Still here.  Where's the kaboom?  There was supposed to be an
392      * EARTH-shattering kaboom!
393      *
394      * Oh, well.  Muddle through as best we can...
395      *
396      * Note that only stderr is available at this point, so don't pass in
397      * a server to aplog_error.
398      */
399
400     RERROR( errno, "exec of %s failed", r->filename);
401     exit(0);
402     /* NOT REACHED */
403     return (0);
404 }
405 #endif
406
407
408 static int
409 send_auth_reply(request_rec * r, const char *auth_scheme, const char *reply)
410 {
411     RDEBUG( "sending back %s", reply );
412     /* Read negotiate from ntlm_auth */
413
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));
417
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.
422      */
423     if(r->connection->keepalives >= r->server->keep_alive_max)
424     {
425         RDEBUG("Decrement the connection request count to keep it alive");
426         r->connection->keepalives -= 1;
427     }
428
429     return HTTP_UNAUTHORIZED;
430 }
431
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 *)) {
434 #ifdef APACHE2
435     apr_procattr_t *attr;
436 #endif
437
438     if ( auth_helper == NULL ) {
439         struct _ntlm_child_stuff cld;
440         apr_pool_t *pool;
441 #ifdef APACHE2
442         char **argv_out;
443         apr_pool_create_ex( &pool, NULL, NULL, NULL ); /* xxx return code */
444 #else
445         pool = ap_make_sub_pool( NULL );
446 #endif
447         auth_helper = apr_pcalloc( pool, sizeof( struct _ntlm_auth_helper ));
448         auth_helper->pool = pool;
449         auth_helper->helper_pid = 0;
450
451 #ifdef APACHE2
452         apr_tokenize_to_argv( cmd, &argv_out, pool );
453 #else
454         ap_register_cleanup( pool, auth_helper, cleanup, ap_null_cleanup );
455 #endif
456         cld.argv0 = cmd;
457         cld.r = r;
458
459 #ifdef APACHE2
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]);
466             return NULL;
467         }
468         auth_helper->helper_pid = auth_helper->proc->pid;
469 #else
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,
474                                                   NULL);
475
476         if (auth_helper->helper_pid == -1) {
477             RERROR( errno, "couldn't spawn child ntlm helper process: %s", cld.argv0);
478             return NULL;
479         }
480 #endif
481
482         RDEBUG( "Launched ntlm_helper, pid %d", auth_helper->helper_pid );
483     } else {
484         RDEBUG( "Using existing auth helper %d", auth_helper->helper_pid );
485     }
486
487     return auth_helper;
488 }
489
490 /* Call winbind to authenticate a (user, password)
491    pair */
492 static int winbind_authenticate_plaintext( request_rec *r, ntlm_config_rec * crec, char *user, char *pass)
493 {
494     ntlm_connection_context_t *ctxt = get_connection_context( r->connection );
495     char *newline;
496     char args_to_helper[HUGE_STRING_LEN];
497     char args_from_helper[HUGE_STRING_LEN];
498     unsigned int bytes_written;
499     int bytes_read;
500
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;
503     }
504
505     if ( ctxt->connected_user_authenticated == NULL ) {
506         apr_pool_t *pool;
507
508         RDEBUG( "creating auth user" );
509
510 #ifdef APACHE2
511         apr_pool_create_ex( &pool, r->connection->pool, NULL, NULL );
512 #else
513         pool = ap_make_sub_pool(r->connection->pool);
514 #endif
515
516         ctxt->connected_user_authenticated =
517             apr_pcalloc(pool, sizeof( struct _connected_user_authenticated));
518
519 #ifndef APACHE2
520         ap_register_cleanup(pool,ctxt->connected_user_authenticated,
521                             cleanup_connected_user_authenticated, ap_null_cleanup );
522 #endif
523
524         ctxt->connected_user_authenticated->pool = pool;
525         ctxt->connected_user_authenticated->user = NULL;
526         ctxt->connected_user_authenticated->auth_type = NULL;
527     } else {
528         /* what, we're already authenticated? */
529         return OK;
530     }
531
532     snprintf( args_to_helper, HUGE_STRING_LEN, "%s %s\n", user, pass );
533
534 #ifdef APACHE2
535     bytes_written = strlen( args_to_helper );
536     apr_file_write( global_ntlm_context.ntlm_plaintext_helper->proc->in, args_to_helper, &bytes_written );
537 #else
538     bytes_written = ap_bwrite( global_ntlm_context.ntlm_plaintext_helper->out_to_helper, args_to_helper, strlen( args_to_helper ));
539 #endif
540
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;
546     }
547
548 #ifdef APACHE2
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 );
552     } else {
553         bytes_read = 0;
554     }
555 #else
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 );
558 #endif
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);
563
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);
569
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);
575
576         return HTTP_INTERNAL_SERVER_ERROR;
577     }
578
579     newline = strchr(args_from_helper, '\n');
580     if (newline != NULL) {
581         *newline = '\0';
582     }
583
584     RDEBUG( "got response: %s", args_from_helper );
585
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;
590 #ifdef APACHE2
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 );*/
596 #else
597         r->connection->user = ctxt->connected_user_authenticated->user;
598         r->connection->ap_auth_type = ap_pstrdup(r->connection->pool, "Basic");
599 #endif
600         RDEBUG( "authenticated %s", ctxt->connected_user_authenticated->user );
601         return  OK;
602     } else {
603         if ( strncmp( args_from_helper, "ERR", 3 ) == 0 ) {
604             RDEBUG( "username/password incorrect" );
605             return note_auth_failure( r, NULL );
606         } else {
607             RDEBUG( "unknown helper response %s", args_from_helper );
608             return HTTP_INTERNAL_SERVER_ERROR;
609         }
610     }
611 }
612
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
615    (type 3). */
616
617 static int
618 process_msg(request_rec * r, ntlm_config_rec * crec, const char *auth_type)
619 {
620     const char *client_msg;
621     const char *message_type;
622     char *childarg;
623     char *newline;
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;
628     int bytes_read;
629     struct _ntlm_auth_helper *auth_helper;
630
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 */
634
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;
641     } else {
642         auth_helper = NULL;
643     }
644
645     if ( auth_helper == NULL ) {
646         return HTTP_INTERNAL_SERVER_ERROR;
647     }
648
649     if ( ctxt->connected_user_authenticated == NULL ) {
650         apr_pool_t *pool;
651
652         RDEBUG( "creating auth user" );
653
654 #ifdef APACHE2
655         apr_pool_create_ex( &pool, r->connection->pool, NULL, NULL );
656 #else
657         pool = ap_make_sub_pool(r->connection->pool);
658 #endif
659
660         ctxt->connected_user_authenticated =
661             apr_pcalloc(pool, sizeof( struct _connected_user_authenticated));
662
663 #ifndef APACHE2
664         ap_register_cleanup(pool,ctxt->connected_user_authenticated,
665                             cleanup_connected_user_authenticated, ap_null_cleanup );
666 #endif
667
668         ctxt->connected_user_authenticated->pool = pool;
669         ctxt->connected_user_authenticated->user = NULL;
670         ctxt->connected_user_authenticated->auth_type = NULL;
671
672         message_type = "YR";
673     } else {
674         message_type = "KK";
675     }
676
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);
681     }
682
683     /* Pipe to helper */
684     snprintf(args_to_helper, HUGE_STRING_LEN, "%s %s\n", message_type, client_msg);
685
686 #ifdef APACHE2
687     bytes_written = strlen( args_to_helper );
688     apr_file_write( auth_helper->proc->in, args_to_helper, &bytes_written );
689 #else
690     bytes_written = ap_bwrite(auth_helper->out_to_helper, args_to_helper, strlen(args_to_helper));
691 #endif
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);
696
697         return HTTP_INTERNAL_SERVER_ERROR;
698     }
699
700 #ifdef APACHE2
701     apr_file_flush( auth_helper->proc->in );
702
703     RDEBUG( "parsing reply from helper to %s", args_to_helper );
704
705     if ( apr_file_gets(args_from_helper, HUGE_STRING_LEN, auth_helper->proc->out ) == APR_SUCCESS ) {
706         bytes_read = strlen( args_from_helper );
707     } else {
708         bytes_read = 0;
709     }
710 #else
711     ap_bflush(auth_helper->out_to_helper);
712
713     bytes_read = ap_bgets(args_from_helper, HUGE_STRING_LEN, auth_helper->in_from_helper);
714 #endif
715
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);
720
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);
726
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);
732
733         return HTTP_INTERNAL_SERVER_ERROR;
734     }
735
736     newline = strchr(args_from_helper, '\n');
737     if (newline != NULL) {
738         *newline = '\0';
739     }
740
741     RDEBUG( "got response: %s", args_from_helper );
742
743     /* inspect message type */
744
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);
750
751         return HTTP_INTERNAL_SERVER_ERROR;
752     }
753     childarg++;
754
755     if (strcasecmp(auth_type, NTLM_AUTH_NAME) == 0) {
756         /* if TT, send to client */
757
758         if (strncmp(args_from_helper, "TT ", 3) == 0) {
759             return send_auth_reply(r, auth_type, childarg);
760         }
761
762         /* if NA, not authenticated */
763
764         if (strncmp(args_from_helper, "NA ", 3) == 0) {
765             RDEBUG("user not authenticated: %s", childarg);
766             return note_auth_failure(r, NULL);
767         }
768
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,
773                             childarg);
774             ctxt->connected_user_authenticated->keepalives =
775                 r->connection->keepalives;
776 #ifdef APACHE2
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 );*/
782 #else
783             r->connection->user = ctxt->connected_user_authenticated->user;
784             r->connection->ap_auth_type = ap_pstrdup(r->connection->pool, auth_type);
785 #endif
786             RDEBUG( "authenticated %s",
787                     ctxt->connected_user_authenticated->user );
788             return OK;
789         }
790     } else if (strcasecmp(auth_type, NEGOTIATE_AUTH_NAME) == 0) {
791
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
795        - The argument:
796              For TT it's a dummy '*'
797          For AF it's domain\\user
798          For NA it's the NT error code
799     */
800
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);
806
807             return HTTP_INTERNAL_SERVER_ERROR;
808         }
809         *childarg3 = '\0';
810         childarg3++;
811
812         /* if TT, send to client */
813
814         if (strncmp(args_from_helper, "TT ", 3) == 0) {
815             return send_auth_reply(r, auth_type, childarg);
816         }
817
818         /* if NA, not authenticated */
819
820         if (strncmp(args_from_helper, "NA ", 3) == 0) {
821             RDEBUG("user not authenticated: %s", childarg3);
822             return note_auth_failure(r, childarg);
823         }
824
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,
829                             childarg3);
830 #ifdef APACHE2
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;
835 #else
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;
839 #endif
840
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));
846             }
847
848             RDEBUG( "wow, we're all happy here" );
849
850             return OK;
851         }
852     }
853
854     /* Helper failed */
855
856     /* if BH, helper is busted */
857
858     if (strncmp(args_from_helper, "BH ", 3) == 0) {
859         RERROR( APR_EGENERAL, "ntlm_auth reports Broken Helper: %s", args_from_helper);
860     } else {
861         RERROR( APR_EGENERAL, "could not parse %s helper callback: %s", auth_type, args_from_helper);
862     }
863
864     apr_pool_destroy(auth_helper->pool);
865     apr_pool_destroy(ctxt->connected_user_authenticated->pool);
866
867     return HTTP_INTERNAL_SERVER_ERROR;
868 }
869
870 /* Called to create a configuration structure for each <Directory> section
871    that uses the ntlm auth module. */
872
873 static void *
874 ntlm_winbind_dir_config(apr_pool_t * p, char *d)
875 {
876     ntlm_config_rec *crec
877         = (ntlm_config_rec *) apr_pcalloc(p, sizeof(ntlm_config_rec));
878
879     /* Set the defaults. */
880
881     crec->authoritative = 1;
882     crec->ntlm_on = 0;
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";
889
890     return crec;
891 }
892
893 /* Authenticate a user using basic authentication */
894 static int
895 authenticate_basic_user(request_rec * r, ntlm_config_rec * crec,
896                         const char *auth_line_after_Basic)
897 {
898     char *sent_user = NULL, *sent_pw;
899     int result = HTTP_UNAUTHORIZED;
900
901     while (*auth_line_after_Basic == ' ' || *auth_line_after_Basic == '\t')
902         auth_line_after_Basic++;
903
904 #ifdef APACHE2
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 );
907 #else
908     sent_user = ap_pbase64decode(r->pool, auth_line_after_Basic);
909 #endif
910
911     if (sent_user != NULL) {
912         char *s;
913
914         if ((sent_pw = strchr(sent_user, ':')) != NULL) {
915             *sent_pw = '\0';
916             ++sent_pw;
917         } else
918             sent_pw = "";
919         if ((s = strchr(sent_user, '\\')) != NULL
920             || (s = strchr(sent_user, '/')) != NULL) {
921
922             /* Authenticate this user as a domain user */
923
924             result = winbind_authenticate_plaintext( r, crec, sent_user, sent_pw);
925
926             RDEBUG("authenticate domain user %s: %s", sent_user,
927                    (result == OK) ? "OK" : "FAILED");
928             goto done;
929         }
930     } else {
931         RDEBUG("can't extract user from %s", auth_line_after_Basic );
932         sent_user = sent_pw = "";
933     }
934
935     RDEBUG("authenticate local user %s: %s", sent_user,
936            (result == OK) ? "OK" : "FAILED");
937
938  done:
939     return result;
940 }
941
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"
950                                           : "Authorization");
951     const char *auth_line2;
952
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 );
963 #ifdef APACHE2
964             r->user = ctxt->connected_user_authenticated->user;
965             r->ap_auth_type = ctxt->connected_user_authenticated->auth_type;
966 #else
967             r->connection->user = ctxt->connected_user_authenticated->user;
968             r->connection->ap_auth_type = ctxt->connected_user_authenticated->auth_type;
969 #endif
970             return OK;
971         } else {
972             RDEBUG( "reauth" );
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;
977             }
978         }
979     }
980
981     /* No authentication line given.  Return a 401 and a WWW-Authenticate
982        header so authentication can commence. */
983
984     if (!auth_line) {
985         note_auth_failure(r, NULL);
986         return HTTP_UNAUTHORIZED;
987     }
988
989     /* If basic authentication is requested and enabled, try to
990        authenticate the user with basic */
991
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 */
997     }
998
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");
1004             return DECLINED;
1005         } else {
1006             return process_msg(r, crec, NEGOTIATE_AUTH_NAME);
1007         }
1008     }
1009
1010     /* Process a NTLM over http message */
1011
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");
1016             return DECLINED;
1017         } else {
1018             RDEBUG( "doing ntlm auth dance" );
1019             return process_msg(r, crec, NTLM_AUTH_NAME);
1020         }
1021     }
1022
1023     if (ctxt->connected_user_authenticated && ctxt->connected_user_authenticated->pool ) {
1024         apr_pool_destroy(ctxt->connected_user_authenticated->pool);
1025     }
1026
1027     RDEBUG( "declined" );
1028
1029     return DECLINED;
1030 }
1031
1032 /* Dispatch list for API hooks */
1033 #ifdef APACHE2
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));
1036
1037     ap_set_module_config(c->conn_config, &auth_ntlm_winbind_module, ctxt);
1038
1039     return OK;
1040 }
1041
1042
1043 static void register_hooks(apr_pool_t *pool)
1044 {
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);
1047 };
1048
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 */
1057 };
1058 #else
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              */
1079 #ifdef EAPI
1080    ,NULL,                    /* EAPI: add_module                    */
1081     NULL,                    /* EAPI: remove_module                 */
1082     NULL,                    /* EAPI: rewrite_command               */
1083     NULL                     /* EAPI: new_connection                */
1084 #endif
1085 };
1086 #endif
1087
1088 /*
1089  * Local Variables:
1090  * c-basic-offset: 4
1091  * indent-tabs-mode: nil
1092  * End:
1093  */