Apache 1.3 # Apache 2.0
/* Copyright (c) 2000 Ingo Luetkebohle, All rights reserved. 1 /* Copyright (c) 2000 Ingo Luetkebohle, All rights reserved.
 * 2  *
 * Redistribution and use in source and binary forms, with or without 3  * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions 4  * modification, are permitted provided that the following conditions
 * are met: 5  * are met:
 * 6  *
 * 1. Redistributions of source code must retain the above copyright 7  * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 8  *    notice, this list of conditions and the following disclaimer.
 * 9  *
 * 2. Redistributions in binary form must reproduce the above copyright 10  * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in 11  *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the 12  *    the documentation and/or other materials provided with the
 *    distribution. 13  *    distribution.
 * 14  *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CODE CONTRIBUTORS 18  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CODE CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 19  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 20  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE. 26  * SUCH DAMAGE.
 */ 27  */
28
/* 29 /* $Id$
 * v 1.1 from 07. July 2002 30  * mod_auth_pam for apache 2.0
31  *
32  * Based on: v 1.1 from 07. July 2002 (for apache 1.3)
33  *
 * 34  *
 * mod_auth_pam: 35  * mod_auth_pam:
 *  basic authentication against pluggable authentication module lib 36  *  basic authentication against pluggable authentication module lib
 * 37  *
 * The authoritative homepage for this module is at 38  * The authoritative homepage for this module is at
 *    http://pam.sourceforge.net/ 39  *    http://pam.sourceforge.net/
 * 40  *
 * For assistance and support, contact the pam-list 41  * For assistance and support, contact the pam-list
 *    https://listman.redhat.com/mailman/listinfo/pam-list 42  *    https://listman.redhat.com/mailman/listinfo/pam-list
 * or the Help forum on SourceForge. 43  * or the Help forum on SourceForge.
 * 44  *
 * Written by Ingo Luetkebohle, based upon mod_auth.c, with contributions 45  * Written by Ingo Luetkebohle, based upon mod_auth.c, with contributions
 * from Fredrik Ohrn (group performance), Dirk-Willem van Gulik (licensing 46  * from Fredrik Ohrn (group performance), Dirk-Willem van Gulik (licensing
 * consultation) and Michael Johnson (example group implementation). 47  * consultation) and Michael Johnson (example group implementation).
 * 48  *
 * Thanks to Andrew Morgan and the rest of the Linux-PAM developers who 49  * Thanks to Andrew Morgan and the rest of the Linux-PAM developers who
 * provided invaluable development help and ideas. 50  * provided invaluable development help and ideas.
 * 51  *
 * Changes: 52  * Changes:
53  *   09-Jul-02: Ported to apache 2.02 (Dirk-Willem van Gulik,
54  *              <dirkx@covalent.net>).
 *   07-Jul-02: License changed to allow redistribution 55  *   07-Jul-02: License changed to allow redistribution
 *        Performance improvement for group lookup 56  *        Performance improvement for group lookup
        (many thanks to Fredrik Ohrn for the patch) 57         (many thanks to Fredrik Ohrn for the patch)
 *   06-Dec-99: Special casing for Solaris 2.6 added 58  *   06-Dec-99: Special casing for Solaris 2.6 added
 *              Added versioning message to headers 59  *              Added versioning message to headers
 *   14-Feb-99: Cleaned up the configuration directives and named them 60  *   14-Feb-99: Cleaned up the configuration directives and named them
 *        in a more straightforward way 61  *        in a more straightforward way
 * 62  *
 *        incorporated getugroups patch from Klaus Wissmann <kw@aw.net> 63  *        incorporated getugroups patch from Klaus Wissmann <kw@aw.net>
 *        to look up supplementary groups from /etc/group 64  *        to look up supplementary groups from /etc/group
 * 65  *
 *   22-Jan-99: incorporated changes from Pavel Kankovsky <kan@dcit.cz> 66  *   22-Jan-99: incorporated changes from Pavel Kankovsky <kan@dcit.cz>
 *        Enabler configuration directive now called AuthPAM_Enabled 67  *        Enabler configuration directive now called AuthPAM_Enabled
 *        Updated for Apache 1.3.x 68  *        Updated for Apache 1.3.x
 *   25-Sep-98: replaced pwdb groups routine with standard C 69  *   25-Sep-98: replaced pwdb groups routine with standard C
 *   19-Oct-97: made module fall through (if configured to do so), 70  *   19-Oct-97: made module fall through (if configured to do so),
 *         even when a user of the given name is found in the PAM 71  *         even when a user of the given name is found in the PAM
 *        databases 72  *        databases
 *   17-Apr-97: fixed segfault that occured when Apache couldn't look 73  *   17-Apr-97: fixed segfault that occured when Apache couldn't look
 *              up the remote host name 74  *              up the remote host name
 *              removed annoying compiler warnings 75  *              removed annoying compiler warnings
 * 76  *
 *   05-Apr-97: made fall-through configurable with AuthPAM_Authorative 77  *   05-Apr-97: made fall-through configurable with AuthPAM_Authorative
 * 78  *
 *   25-Mar-97: added support for transparent fall-through to other auth 79  *   25-Mar-97: added support for transparent fall-through to other auth
 *              modules with configurable fail delays 80  *              modules with configurable fail delays
 *              added acct_mgmt hook 81  *              added acct_mgmt hook
 *              added group support (through libpwdb) 82  *              added group support (through libpwdb)
 * 83  *
 * usage information: 84  * usage information:
 * 85  *
 * new-style (DSO) 86  * new-style (DSO and apache 2.0)
 * 87  *
 *    compile with 88  *    compile with
 *        apxs -c -lpam mod_auth_pam.c 89  *        apxs -c -lpam mod_auth_pam.c
 * 90  *
 *     install with 91  *        apxs -I/usr/local/include -L/usr/local/lib -c -lpam mod_auth_pam.c
 *        apxs -i -a mod_auth_pam.so 92  *
 * 93  *     install with
 *    configure PAM by adding 94  *        apxs -i -a mod_auth_pam.so
 *        /etc/pam.d/httpd 95  *
 *    with the appropriate pam modules (for starters, just copy over ftp) 96  *    configure PAM by adding
 * 97  *        /etc/pam.d/httpd
 * old-style (from Apache 1.2.x) 98  *
 * 99  *    with the appropriate pam modules (for starters, just copy over ftp)
 *     1. Configuration: 100  *
 *        Module pam_auth_module mod_auth_pam.o 101  * old-style (from Apache 1.2.x)
 *         EXTRA_LIBS+= -lpam -ldl 102  *
 * 103  *     1. Configuration:
 *     2. Add an auth and an account entry for service type "httpd" 104  *        Module pam_auth_module mod_auth_pam.o
 *          to your PAM configuration 105  *         EXTRA_LIBS+= -lpam -ldl
 * 106  *
 * 107  *     2. Add an auth and an account entry for service type "httpd"
 * configuration directives: 108  *          to your PAM configuration
 * AuthFailDelay <msecs> 109  *
 *                              number of mili-seconds to wait after a 110  *
 *                              failed authentication attempt. this is 111  * configuration directives:
 *                              a minimum value and may have been 112  * AuthFailDelay <msecs>
 *                              increased by other pam apps. 113  *                              number of mili-seconds to wait after a
 *                              defaults to 0 114  *                              failed authentication attempt. this is
 *                REQUIRES lib_pam SUPPORT 115  *                              a minimum value and may have been
 * 116  *                              increased by other pam apps.
 * AuthPAM_Enabled on|off 117  *                              defaults to 0
 *                              If on, mod_auth_pam will try to authenticate 118  *                REQUIRES lib_pam SUPPORT
 *                the user. 119  *
 *                If off, mod_auth_pam will DECLINE immediately 120  * AuthPAM_Enabled on|off
 *                instead of trying to authenticate the user. 121  *                              If on, mod_auth_pam will try to authenticate
 *                This will make Apache try other modules. 122  *                the user.
 *                Defaults to on 123  *                If off, mod_auth_pam will DECLINE immediately
 * 124  *                instead of trying to authenticate the user.
 * AuthPAM_FallThrough 125  *                This will make Apache try other modules.
 *                If on, makes mod_auth_pam DECLINE if it can't 126  *                Defaults to on
 *                the username, giving other modules a chance. 127  *
 *                Please note that, if it DOES find the username, 128  * AuthPAM_FallThrough
 *                and the password doesn't match, it will NOT 129  *                If on, makes mod_auth_pam DECLINE if it can't
 *                fall through but return "access denied" instead 130  *                the username, giving other modules a chance.
 *                Defaults to off 131  *                Please note that, if it DOES find the username,
 * 132  *                and the password doesn't match, it will NOT
 * AuthPAM_Authorative on|off   DEPRECATED 133  *                fall through but return "access denied" instead
 */ 134  *                Defaults to off
135  *
136  * AuthPAM_Authorative on|off   DEPRECATED
137  */
138
139 #include <sys/types.h>
140 #include <pwd.h>        /* for getpwnam et.al. */
141 #include <grp.h>        /* for getpwnam et.al. */
#include <unistd.h> 142 #include <unistd.h>
143
144 #include "ap_config.h"
#include "httpd.h" 145 #include "httpd.h"
#include "http_config.h" 146 #include "http_config.h"
#include "http_core.h" 147 #include "http_core.h"
#include "http_log.h" 148 #include "http_log.h"
#include "http_protocol.h" 149 #include "http_protocol.h"
150 #include "http_request.h"
151
#include <security/pam_appl.h> 152 #include <security/pam_appl.h>
153
/* change this to 0 on RedHat 4.x */ 154 /* change this to 0 on RedHat 4.x */
#define PAM_STRE_NEEDS_PAMH 1 155 #define PAM_STRE_NEEDS_PAMH 1
#define VERSION "1.0a" 156 #define VERSION "1.0a"
157
module pam_auth_module; 158 module pam_auth_module;
159
static const char 160 static const char
    *pam_servicename = "httpd", *valid_user = "valid-user"; 161     *pam_servicename = "httpd", *valid_user = "valid-user";
162
typedef struct { 163 typedef struct {
    char *name, *pw; 164     char *name, *pw;
}      auth_pam_userinfo; 165 }      auth_pam_userinfo;
166
/* 167 /*
 * Solaris 2.6.x has a broken conversation function and needs this 168  * Solaris 2.6.x has a broken conversation function and needs this
 * as a global variable 169  * as a global variable
 * I refused to pollute code for other platforms with this, 170  * I refused to pollute code for other platforms with this,
 * so all Solaris 2.6 specific stuff is if'd like the following 171  * so all Solaris 2.6 specific stuff is if'd like the following
 */ 172  */
#if SOLARIS2 == 260 173 #if SOLARIS2 == 260
auth_pam_userinfo *global_userinfo; 174 auth_pam_userinfo *global_userinfo;
#endif 175 #endif
176
/* 177 /*
 * the pam_strerror function has different parameters in early PAM 178  * the pam_strerror function has different parameters in early PAM
 * versions 179  * versions
 */ 180  */
#ifndef PAM_STRE_NEEDS_PAMH 181 #ifndef PAM_STRE_NEEDS_PAMH
#define compat_pam_strerror(pamh, res) pam_strerror(res) 182 #define compat_pam_strerror(pamh, res) pam_strerror(res)
#else 183 #else
#define compat_pam_strerror(pamh, res) pam_strerror(pamh, res) 184 #define compat_pam_strerror(pamh, res) pam_strerror(pamh, res)
#endif 185 #endif
186
/* 187 /*
 * configuration directive handling 188  * configuration directive handling
 */ 189  */
190
typedef struct { 191 typedef struct {
    int 192     int
        fail_delay,        /* fail delay in ms -- needs library support */ 193         fail_delay,        /* fail delay in ms -- needs library support */
        fall_through,        /* 1 to DECLINE instead of AUTH_REQUIRED if 194         fall_through,        /* 1 to DECLINE instead of
                 * we can't find the username (defaults to 0) */ 195                  * HTTP_UNAUTHORIZEDif we can't find the
        enabled;        /* 1 to use mod_auth_pam, 0 otherwise 196                  * username (defaults to 0) */
                 * (defaults to 1) */ 197         enabled;        /* 1 to use mod_auth_pam, 0 otherwise
}      auth_pam_dir_config; 198                  * (defaults to 1) */
199 }      auth_pam_dir_config;
200
void auth_pam_init(server_rec * s, pool * p) 201 static
202 int auth_pam_init(
203               apr_pool_t * p,
204               apr_pool_t * plog,
205               apr_pool_t * ptemp,
206               server_rec * s
207 )
{ 208 {
    ap_add_version_component("mod_auth_pam/" VERSION); 209     ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "PAM: mod_auth_pam/" VERSION);
210
211     return OK;
} 212 }
213
static 214 static
void *create_auth_pam_dir_config(pool * p, char *dummy) 215 void *create_auth_pam_dir_config(apr_pool_t * p, char *dummy)
{ 216 {
    auth_pam_dir_config *new = 217     auth_pam_dir_config *new =
    (auth_pam_dir_config *) ap_palloc(p, sizeof(auth_pam_dir_config)); 218     (auth_pam_dir_config *) apr_palloc(p, sizeof(auth_pam_dir_config));
219
    new->fail_delay = 0;    /* 0 ms */ 220     new->fail_delay = 0;    /* 0 ms */
    new->fall_through = 0;    /* off */ 221     new->fall_through = 0;    /* off */
    new->enabled = 1;        /* on */ 222     new->enabled = 1;        /* on */
    return new; 223     return new;
} 224 }
225
static char *auth_fail_delay(cmd_parms * cmd, auth_pam_dir_config * config, char *secs) 226
{ 227
    if (secs) 228
    config->fail_delay = atoi(secs); 229
    return NULL; 230
} 231
232
static char *auth_fall_through(cmd_parms * cmd, auth_pam_dir_config * config, int arg) 233
{ 234
    config->fall_through = arg; 235
    return NULL; 236
} 237
238
static char *auth_enable(cmd_parms * cmd, auth_pam_dir_config * config, int arg) 239
{ 240
    config->enabled = arg; 241
    return NULL; 242
} 243
244
static command_rec auth_pam_cmds[] = { 245 static command_rec auth_pam_cmds[] = {
    {"AuthFailDelay", (const char *(*) ()) auth_fail_delay, 0, OR_AUTHCFG, TAKE1, 246
    "number of micro seconds to wait after failed authentication attempt. " 247     AP_INIT_TAKE1("AuthPAM_FailDelay",
        "default is 0 "}, 248     ap_set_int_slot, (void *) APR_OFFSETOF(auth_pam_dir_config, fail_delay),
    {"AuthPAM_Authorative", (const char *(*) ()) auth_fall_through, NULL, OR_AUTHCFG, FLAG, 249     OR_AUTHCFG,
    "no longer in use -- see AuthPAM_FallThrough instead"}, 250     "number of micro seconds to wait after failed authentication "
    {"AuthPAM_FallThrough", (const char *(*) ()) auth_fall_through, NULL, OR_AUTHCFG, FLAG, 251     "attempt. (default is 0.)"),
    "on|off - determines if other authentication methods are attempted if this " 252
        "one fails; default is off "}, 253     AP_INIT_FLAG("AuthPAM_FallThrough",
    {"AuthPAM_Enabled", (const char *(*) ()) auth_enable, NULL, OR_AUTHCFG, FLAG, 254      ap_set_flag_slot, (void *) APR_OFFSETOF(auth_pam_dir_config, fall_through),
    "on|off - determines if PAM authentication is enabled; default is on" 255      OR_AUTHCFG,
    }, 256     "on|off - determines if other authentication methods are attempted "
257      "if this one fails; (default is off.)"),
258
259     AP_INIT_FLAG("AuthPAM_Enabled",
260      ap_set_flag_slot, (void *) APR_OFFSETOF(auth_pam_dir_config, enabled),
261      OR_AUTHCFG,
262      "on|off - determines if PAM authentication is enabled. "
263      "(default is on.)"),
264
    {NULL} 265     {NULL}
}; 266 };
267
/* 268 /*
 * auth_pam_talker: supply authentication information to PAM when asked 269  * auth_pam_talker: supply authentication information to PAM when asked
 * 270  *
 * Assumptions: 271  * Assumptions:
 *   A password is asked for by requesting input without echoing 272  *   A password is asked for by requesting input without echoing
 *   A username is asked for by requesting input _with_ echoing 273  *   A username is asked for by requesting input _with_ echoing
 * 274  *
 */ 275  */
static 276 static
int auth_pam_talker(int num_msg, 277 int auth_pam_talker(int num_msg,
                const struct pam_message ** msg, 278                 const struct pam_message ** msg,
                struct pam_response ** resp, 279                 struct pam_response ** resp,
                void *appdata_ptr) 280                 void *appdata_ptr)
{ 281 {
    unsigned short i = 0; 282     unsigned short i = 0;
    auth_pam_userinfo *userinfo = (auth_pam_userinfo *) appdata_ptr; 283     auth_pam_userinfo *userinfo = (auth_pam_userinfo *) appdata_ptr;
    struct pam_response *response = 0; 284     struct pam_response *response = 0;
285
#if SOLARIS2 == 260 286 #if SOLARIS2 == 260
    if (!userinfo) 287     if (!userinfo)
    userinfo = global_userinfo; 288     userinfo = global_userinfo;
    /* fprintf(stderr,"%s : %s", userinfo->name, userinfo->pw); */ 289     /* fprintf(stderr,"%s : %s", userinfo->name, userinfo->pw); */
#endif 290 #endif
291
    /* parameter sanity checking */ 292     /* parameter sanity checking */
    if (!resp || !msg || !userinfo) 293     if (!resp || !msg || !userinfo)
    return PAM_CONV_ERR; 294     return PAM_CONV_ERR;
295
    /* allocate memory to store response */ 296     /* allocate memory to store response */
    response = malloc(num_msg * sizeof(struct pam_response)); 297     response = malloc(num_msg * sizeof(struct pam_response));
    if (!response) 298     if (!response)
    return PAM_CONV_ERR; 299     return PAM_CONV_ERR;
300
    /* copy values */ 301     /* copy values */
    for (i = 0; i < num_msg; i++) { 302     for (i = 0; i < num_msg; i++) {
    /* initialize to safe values */ 303     /* initialize to safe values */
    response[i].resp_retcode = 0; 304     response[i].resp_retcode = 0;
    response[i].resp = 0; 305     response[i].resp = 0;
306
    /* select response based on requested output style */ 307     /* select response based on requested output style */
    switch (msg[i]->msg_style) { 308     switch (msg[i]->msg_style) {
    case PAM_PROMPT_ECHO_ON: 309     case PAM_PROMPT_ECHO_ON:
        /* on memory allocation failure, auth fails */ 310         /* on memory allocation failure, auth fails */
        response[i].resp = strdup(userinfo->name); 311         response[i].resp = strdup(userinfo->name);
        break; 312         break;
    case PAM_PROMPT_ECHO_OFF: 313     case PAM_PROMPT_ECHO_OFF:
        response[i].resp = strdup(userinfo->pw); 314         response[i].resp = strdup(userinfo->pw);
        break; 315         break;
    default: 316     default:
        if (response) 317         if (response)
        free(response); 318         free(response);
        return PAM_CONV_ERR; 319         return PAM_CONV_ERR;
    } 320     }
    } 321     }
    /* everything okay, set PAM response values */ 322     /* everything okay, set PAM response values */
    *resp = response; 323     *resp = response;
    return PAM_SUCCESS; 324     return PAM_SUCCESS;
} 325 }
326
/* 327 /*
 * These functions return 0 if client is OK, and proper error status 328  * These functions return 0 if client is OK, and proper error status
 * if not... either AUTH_REQUIRED, if we made a check, and it failed, or 329  * if not... either AUTH_REQUIRED, if we made a check, and it failed, or
 * SERVER_ERROR, if things are so totally confused that we couldn't 330  * SERVER_ERROR, if things are so totally confused that we couldn't
 * figure out how to tell if the client is authorized or not. 331  * figure out how to tell if the client is authorized or not.
 * 332  *
 * If they return DECLINED, and all other modules also decline, that's 333  * If they return DECLINED, and all other modules also decline, that's
 * treated by the server core as a configuration error, logged and 334  * treated by the server core as a configuration error, logged and
 * reported as such. 335  * reported as such.
 */ 336  */
337
/* 338 /*
 * Determine user ID, and check if it really is that user 339  * Determine user ID, and check if it really is that user
 */ 340  */
static 341 static
int pam_auth_basic_user(request_rec * r) 342 int pam_auth_basic_user(request_rec * r)
{ 343 {
    int res = 0; 344     int res = 0;
    /* mod_auth_pam specific */ 345     /* mod_auth_pam specific */
    auth_pam_userinfo userinfo = {NULL, NULL}; 346     auth_pam_userinfo userinfo = {NULL, NULL};
    auth_pam_dir_config *conf = (auth_pam_dir_config *) 347     auth_pam_dir_config *conf = (auth_pam_dir_config *)
    ap_get_module_config(r->per_dir_config, &pam_auth_module); 348     ap_get_module_config(r->per_dir_config, &pam_auth_module);
    /* PAM specific  */ 349     /* PAM specific  */
    struct pam_conv conv_info = {&auth_pam_talker, (void *) &userinfo}; 350     struct pam_conv conv_info = {&auth_pam_talker, (void *) &userinfo};
    pam_handle_t *pamh = NULL; 351     pam_handle_t *pamh = NULL;
352
#if SOLARIS2 == 260 353 #if SOLARIS2 == 260
    global_userinfo = &userinfo; 354     global_userinfo = &userinfo;
#endif 355 #endif
356
    /* enabled? */ 357     /* enabled? */
    if (!conf->enabled) 358     if (!conf->enabled)
    return DECLINED; 359     return DECLINED;
360
    /* read sent pw */ 361     /* read sent pw */
    if ((res = ap_get_basic_auth_pw(r, (const char **) &(userinfo.pw)))) 362     if ((res = ap_get_basic_auth_pw(r, (const char **) &(userinfo.pw))))
    return res; 363     return res;
364
    /* this is only set after get_basic_auth_pw was called */ 365     /* this is only set after get_basic_auth_pw was called */
    userinfo.name = r->connection->user; 366     userinfo.name = r->user;
367
    /* initialize pam */ 368     /* initialize pam */
    if ((res = pam_start(pam_servicename, 369     if ((res = pam_start(pam_servicename,
             userinfo.name, 370              userinfo.name,
             &conv_info, 371              &conv_info,
             &pamh)) != PAM_SUCCESS) { 372              &pamh)) != PAM_SUCCESS) {
373
    ap_log_reason((char *) compat_pam_strerror(pamh, res), r->uri, r); 374     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
375               "PAM: Could not start pam service: %s",
376               compat_pam_strerror(pamh, res));
    return DECLINED; 377     return DECLINED;
    } 378     }
379
    /* set fail delay */ 380     /* set fail delay */
#ifdef PAM_FAIL_DELAY 381 #ifdef PAM_FAIL_DELAY
    pam_fail_delay(pamh, conf->fail_delay); 382     pam_fail_delay(pamh, conf->fail_delay);
#endif 383 #endif
384
    /* set remote user information */ 385     /* set remote user information */
    /* 386     /*
     * this seems to cause segfaults in lots of cases -- disabled for 387      * this seems to cause segfaults in lots of cases -- disabled for now
     * now pam_set_item(pamh, PAM_USER, userinfo.name); 388      * pam_set_item(pamh, PAM_USER, userinfo.name); pam_set_item(pamh,
     * pam_set_item(pamh, PAM_RHOST, get_remote_host(r->connection, 389      * PAM_RHOST, get_remote_host(r->connection, conf, REMOTE_NAME));
     * conf, REMOTE_NAME)); 390
     */ 391      */
392
    /* try to authenticate user, log error on failure */ 393     /* try to authenticate user, log error on failure */
    if ((res = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK)) != 394     if ((res = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK)) !=
    PAM_SUCCESS) { 395     PAM_SUCCESS) {
    ap_log_reason((char *) compat_pam_strerror(pamh, res), r->uri, r); 396     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
397               "PAM: user '%s' - not authenticated: %s", r->user, compat_pam_strerror(pamh, res));
398
    if (conf->fall_through && (res == PAM_USER_UNKNOWN)) { 399     if (conf->fall_through && (res == PAM_USER_UNKNOWN)) {
        /* 400         /* we don't know about the user, but other auth modules might do */
         * we don't know about the user, but other auth modules 401
         * might do 402
         */ 403
        pam_end(pamh, PAM_SUCCESS); 404         pam_end(pamh, PAM_SUCCESS);
        return DECLINED; 405         return DECLINED;
    } 406     }
    else { 407     else {
        pam_end(pamh, PAM_SUCCESS); 408         pam_end(pamh, PAM_SUCCESS);
        ap_note_basic_auth_failure(r); 409         ap_note_basic_auth_failure(r);
        return AUTH_REQUIRED; 410         return HTTP_UNAUTHORIZED;
    }            /* endif fall_through */ 411     }            /* endif fall_through */
    }                /* endif authenticate */ 412     }                /* endif authenticate */
413
    /* check that the account is healthy */ 414     /* check that the account is healthy */
    if ((res = pam_acct_mgmt(pamh, PAM_DISALLOW_NULL_AUTHTOK)) != PAM_SUCCESS) { 415     if ((res = pam_acct_mgmt(pamh, PAM_DISALLOW_NULL_AUTHTOK)) != PAM_SUCCESS) {
    ap_log_reason((char *) compat_pam_strerror(pamh, res), r->uri, r); 416     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
417               "PAM: user '%s'  - invalid account: %s", r->user, compat_pam_strerror(pamh, res));
    pam_end(pamh, PAM_SUCCESS); 418     pam_end(pamh, PAM_SUCCESS);
    return AUTH_REQUIRED; 419     return HTTP_UNAUTHORIZED;
    } 420     }
421
    pam_end(pamh, PAM_SUCCESS); 422     pam_end(pamh, PAM_SUCCESS);
    return OK; 423     return OK;
} 424 }
425
/* 426 static void pam_register_hooks(apr_pool_t * p)
 * Look if that user (as authenticated above) is allowed here 427
 */ 428
429
static 430
int pam_check_auth(request_rec * r) 431
{ 432 {
    register int i = 0; 433     ap_hook_post_config(auth_pam_init, NULL, NULL, APR_HOOK_MIDDLE);
    char method_restricted = 0, *line = 0, *word = 0; 434     ap_hook_check_user_id(pam_auth_basic_user, NULL, NULL, APR_HOOK_MIDDLE);
    auth_pam_dir_config *conf = (auth_pam_dir_config *) 435
    ap_get_module_config(r->per_dir_config, &pam_auth_module); 436
437
    char *p_group = NULL; 438
439
    /* check for allowed users/group */ 440
    const array_header *reqs_arr = ap_requires(r); 441
    require_line *reqs = 0; 442
443
    /* enabled? */ 444
    if (!conf->enabled) 445
    return DECLINED; 446
447
    /* if any valid user suffices return success */ 448
    if (!reqs_arr) 449
    return (OK); 450
451
    /* otherwise */ 452
    reqs = (require_line *) reqs_arr->elts; 453
454
    /* loop over requirement lines */ 455
    for (i = 0; i < reqs_arr->nelts; i++) { 456
    /* if method of this requirement matches current method */ 457
    if (reqs[i].method_mask & (1 << r->method_number)) { 458
        method_restricted = 1; 459
460
        line = reqs[i].requirement; 461
        word = ap_getword(r->pool, (const char **) &line, ' '); 462
463
        /* if any user is ok */ 464
        if (!strcmp(word, valid_user)) 465
        return OK; 466
467
        /* loop over line */ 468
        if (!strcmp(word, "user")) { 469
        while (*line) { 470
            word = ap_getword_conf(r->pool, (const char **) &line); 471
            /* if username matches remote username */ 472
            if (!strcmp(r->connection->user, word)) 473
            /* return success */ 474
            return OK; 475
        } 476
        }            /* end if user */ 477
        else if (!strcmp(word, "group")) { 478
        struct group *grent; 479
        char **members; 480
481
        if (!p_group) { 482
            struct passwd *pwent; 483
484
            if ((pwent = getpwnam(r->connection->user)) && (grent = getgrgid(pwent->pw_gid))) 485
            p_group = grent->gr_name; 486
        } 487
488
        while (*line) { 489
            word = ap_getword_conf(r->pool, (const char **) &line); 490
491
            if (p_group && !strcmp(p_group, word)) 492
            return OK; 493
494
            if ((grent = getgrnam(word)) && grent->gr_mem) { 495
            members = grent->gr_mem; 496
497
            while (*members) { 498
                if (!strcmp(*members, word)) 499
                return OK; 500
501
                members++; 502
            } 503
            } 504
        } 505
        }            /* end if group */ 506
    } 507
    } 508
509
    if (!method_restricted) 510
    return OK; 511
512
    ap_note_basic_auth_failure(r); 513
    return AUTH_REQUIRED; 514
} 515 }
516
module pam_auth_module = { 517 module AP_MODULE_DECLARE_DATA pam_auth_module = {
    STANDARD_MODULE_STUFF, 518     STANDARD20_MODULE_STUFF,
    auth_pam_init,        /* initializer */ 519
    create_auth_pam_dir_config,    /* dir config creater */ 520     create_auth_pam_dir_config,    /* dir config creater */
    NULL,            /* dir merger --- default is to override */ 521     NULL,            /* dir merger --- default is to override */
    NULL,            /* server config */ 522     NULL,            /* server config */
    NULL,            /* merge server config */ 523     NULL,            /* merge server config */
    auth_pam_cmds,        /* command table */ 524     auth_pam_cmds,        /* command table */
    NULL,            /* handlers */ 525     pam_register_hooks,        /* register hooks */
    NULL,            /* filename translation */ 526
    pam_auth_basic_user,    /* check_user_id */ 527
    pam_check_auth,        /* check auth */ 528
    NULL,            /* check access */ 529
    NULL,            /* type_checker */ 530
    NULL,            /* fixups */ 531
    NULL            /* logger */ 532
}; 533 };