home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 8 / CDACTUAL8.iso / share / os2 / varios / apache / mod_auth.c < prev    next >
Encoding:
Text File  |  1996-07-08  |  34.4 KB  |  980 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1995 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission.
  24.  *
  25.  * 5. Redistributions of any form whatsoever must retain the following
  26.  *    acknowledgment:
  27.  *    "This product includes software developed by the Apache Group
  28.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  29.  *
  30.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  31.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  32.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  33.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  34.  * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  38.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  41.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  42.  * ====================================================================
  43.  *
  44.  * This software consists of voluntary contributions made by many
  45.  * individuals on behalf of the Apache Group and was originally based
  46.  * on public domain software written at the National Center for
  47.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  48.  * For more information on the Apache Group and the Apache HTTP server
  49.  * project, please see <http://www.apache.org/>.
  50.  *
  51.  */
  52.  
  53.  
  54. /*
  55.  * mod_auth_msql: authentication
  56.  *
  57.  * Rob McCool & Brian Behlendorf.
  58.  *
  59.  * Adapted to Shambhala by rst.
  60.  *
  61.  * Addapted for use with the mSQL database
  62.  * (see ftp:/ftp.bond.edu.au/pub/Minerva/mSQL)
  63.  *
  64.  * Version 1.0 May 1996 - Blame: Dirk.vanGulik@jrc.it.
  65.  *
  66.  * A (sometimes more up to date) version of the documentation
  67.  * can be found at the http://www.apache.org site or at 
  68.  * http://me-www.jrc.it/~dirkx/mod_auth_msql.html.
  69.  * 
  70.  * Outline:
  71.  *
  72.  * This module allows access control using the public domain
  73.  * mSQL database; a fast but limted SQL engine which can be
  74.  * contacted over an internal unix domain protocol as well as
  75.  * over normal inter-machine tcp/ip socket communication.
  76.  *
  77.  * An example table could be:
  78.  *
  79.  * create table user_records (
  80.  *       User_id  char(32) primary key,
  81.  *      Cpasswd  char(32),
  82.  *    [ Xgroup   char(32) ]
  83.  *      ) \g
  84.  *
  85.  * The user_id can be as long as desired; however some of the
  86.  * popular web browsers truncate, or stop the user from entering
  87.  * names longer than 32 characters. Furthermore the 'crypt' function
  88.  * on your platform might impose further limits. Also use of
  89.  * the 'require users uid [uid..]' directive in the access.conf file,
  90.  * where the user ids are separated by spaces can possibly prohibit the
  91.  * use of spaces in your user-names. Also, not the MAX_FIELD_LEN define
  92.  * somewhere below.
  93.  *
  94.  * To use the above, the following example could be in your access.conf
  95.  * file. Also there is a more elaborate description afther this example.
  96.  *
  97.  * <directory /web/docs/private>
  98.  *
  99.  *  Auth_MSQLhost localhost
  100.  * or
  101.  *  Auth_MSQLhost datab.machine.your.org
  102.  *
  103.  *                  If this directive is ommited, or set to
  104.  *            localhost, the machine on which apache
  105.  *            runs is assumed, and the faster /dev/msql
  106.  *            communication channel will be used. Otherwise
  107.  *            it is the machine to contact by tcp/ip.
  108.  *
  109.  * Auth_MSQLdatabase    www
  110.  *
  111.  *                      The name of the database on the above machine,
  112.  *            which contains *both* the tables for group and
  113.  *            for user/passwords. Currently it is not possible
  114.  *            to have these split over two databases. Make
  115.  *            sure that the msql.acl (access control file) of
  116.  *            mSQL does indeed allow the effective uid of the
  117.  *            web server read access to this database. Check the
  118.  *            httpd.conf file for this uid.
  119.  *
  120.  * Auth_MSQLpwd_table   user_records
  121.  *
  122.  *                      Here the table which contain the uid/password combination
  123.  *            is specified.
  124.  *
  125.  * Auth_MSQLuid_field    User_id
  126.  * Auth_MSQLpwd_field   Cpasswd
  127.  *
  128.  *            These two directive specify the field names in the 'user_record'
  129.  *            table. If this module is compiled with the BACKWARD_VITEK
  130.  *            compatibility switch, the defaults 'user' and 'password' are
  131.  *            assumed if you do not specify them. Currently the user_id field
  132.  *            *MUST* be a primary key or one must ensure that each user only
  133.  *            occurs *once* in the table. If a UID occurs twice access is
  134.  *            denied by default.
  135.  *
  136.  * Auth_MSQLgrp_table   user_records
  137.  * Auth_MSQLgrp_field    Xgroup
  138.  *
  139.  *                      Optionaly one can also specify a table which contains the
  140.  *            user/group combinations. This can be the same table which
  141.  *            also contains the username/password combinations. However
  142.  *            if a user belongs to two or more groups, one will have to
  143.  *                  use a differt table with multiple entries.
  144.  *
  145.  * Auth_MSQL_nopasswd            off
  146.  * Auth_MSQL_Authorative        on
  147.  * Auth_MSQL_EncryptedPasswords on
  148.  *
  149.  *                      These three optional fields (all set to the sensible defaults,
  150.  *            so you really do not have to enter them) are described in more
  151.  *            detail below. If you choose to set these to any other values than
  152.  *            the above be very sure you understand the security implications and
  153.  *            do verify that apache does what you exect it to do.
  154.  *
  155.  * AuthName         example mSQL realm
  156.  * AuthType        basic
  157.  *
  158.  *                      Normal apache/ncsa tokens for access control
  159.  *
  160.  * <limit get post head>
  161.  *   order deny,allow
  162.  *   allow from all
  163.  *
  164.  *   require valid-user
  165.  *                        'valid-user'; allow in any user which has a valid uid/passwd
  166.  *                        pair in the above pwd_table.
  167.  * or
  168.  *   require user smith jones
  169.  *                      Limit access to users who have a valid uid/passwd pair in the
  170.  *              above pwd_table AND whose uid is 'smith' or 'jones'. Do note that
  171.  *              the uid's are separated by 'spaces' for historic (ncsa) reasons.
  172.  *              So allowing uids with spaces might cause problems.
  173.  *
  174.  *   require group has_paid
  175.  *                      Optionally also ensure that the uid has the value 'has_paid' in the group
  176.  *              field in the group table.
  177.  *   </limit>
  178.  * </directory>
  179.  *
  180.  * End of the example
  181.  *
  182.  * - full description of all tokens: -
  183.  *
  184.  * Directives:
  185.  *
  186.  * Auth_MSQLhost       Hostname of the machine running
  187.  *               the mSQL demon. The effective uid
  188.  *               of the server should be allowed
  189.  *               access. If not given, or if it is
  190.  *               the magic name 'localhost', it is
  191.  *               passed to the mSQL libary as a null
  192.  *               pointer. This effectively forces it
  193.  *               to use /dev/msql rather than the
  194.  *               (slower) socket communication.
  195.  *
  196.  * Auth_MSQLdatabase    Name of the database in which the following
  197.  *            table(s) are contained.
  198.  *
  199.  * Auth_MSQLpwd_table    Contains at least the fields with the
  200.  *            username and the (encrypted) password. Each
  201.  *            uid should only occur once in this table and
  202.  *            for performance reasons should be a primary key.
  203.  *            Normally this table is compulsory, but it is
  204.  *            possible to use a fall-through to other methods
  205.  *            and use the mSQL module for group control only;
  206.  *            see the Authorative directive below.
  207.  *
  208.  * Auth_MSQLgrp_table    Contains at least the fields with the
  209.  *            username and the groupname. A user which
  210.  *            is in multiple groups has therefore
  211.  *            multiple entries; this might be some per-
  212.  *            formance problems associated with this; and one
  213.  *            might consider to have separate tables for each
  214.  *            group (rather than all groups in one table) if
  215.  *            your directory structure allows for it.
  216.  *            One only needs to specify this table when doing
  217.  *            group control.
  218.  *
  219.  * Auth_MSQLuid_field    Name of the field containing the username
  220.  * Auth_MSQLpwd_field   Fieldname for the passwords
  221.  * Auth_MSQLgrp_field    Fieldname for the groupname
  222.  *
  223.  *                      Only the fields used need to be specified. When this
  224.  *            module is compiled with the BACKWARD_VITEK option the
  225.  *            uid and pwd field names default to 'user' and 'password'.
  226.  *
  227.  *
  228.  * Auth_MSQL_nopasswd    <on|off>
  229.  *            skip password comparison if passwd field is
  230.  *            empty; i.e. allow 'any' password. This is off
  231.  *            by default; thus to ensure that an empty field
  232.  *            in the mSQL table does not allow people in by
  233.  *            default with a random password.
  234.  *
  235.  * Auth_MSQL_Authorative <on|off>
  236.  *            default is 'on'. When set on, there is no
  237.  *                 fall through to other authorization methods. So if a
  238.  *            user is not in the mSQL dbase table (and perhaps
  239.  *                not in the right group) or has the password wrong, then
  240.  *                      he or she is denied access. When this directive is set to
  241.  *            'off' control is passed on to any other authorization
  242.  *            modules, such as the basic auth module wih the htpasswd
  243.  *            file and or the unix-(g)dbm modules.
  244.  *            The default is 'ON' to avoid nasty 'fall-through' sur-
  245.  *            prizes. Do be sure you know what you decide to switch
  246.  *            it off.
  247.  *
  248.  * Auth_MSQL_EncryptedPasswords <on|off>
  249.  *             default is on. When set on, the values in the
  250.  *            pwd_field are assumed to be crypted using *your*
  251.  *                machines 'crypt' function; and the incoming password
  252.  *                is 'crypt'ed before comparison. When this function is
  253.  *            off, the comparison is done directly with the plaintext
  254.  *            entered password. (Yes; http-basic-auth does send the
  255.  *            password as plaintext over the wire :-( ). The default
  256.  *            is a sensible 'on', and I personally thing that it is
  257.  *            a *very-bad-idea* to change this. However a multi
  258.  *            vendor or international environment (which sometimes
  259.  *            leads to different crypts functions) might force you to.
  260.  *
  261.  * Dirk.vanGulik@jrc.it; http://ewse.ceo.org; http://me-www.jrc.it/~dirkx
  262.  * 23 Nov 1995, 24 Feb 1996, 16 May 1996.
  263.  *
  264.  * Version 0.0  First release
  265.  *         0.1  Update to apache 1.00
  266.  *         0.2  added lines which got missing god knows when
  267.  *              and which did the valid-user authentification
  268.  *              no good at all !
  269.  *       0.3  Added 'Auth_MSQL_nopasswd' option
  270.  *       0.4  Cleaned out the error messages mess.
  271.  *       0.6  Inconsistency with gid/grp in comment/token/source
  272.  *           Make sure you really use 'Auth_MSQLgrp_field' as
  273.  *        indicated above.
  274.  *       0.7  *host to host fixed. Credits go to Rob Stout,
  275.  *          <stout@lava.et.tudelft.nl> for spotting this one.
  276.  *       0.8  Authorative directive added. See above.
  277.  *       0.9  palloc return code check(s), should be backward compatible with
  278.  *           1.11 version of Vivek Khera <khera@kciLink.com> msql module,
  279.  *        fixed broken err msg in group control, changed command table
  280.  *        messages to make more sense when displayed in that new module
  281.  *        management tool. Added EncryptedPassword on/off functionality.
  282.  *        msqlClose() statements added upon error. Support for persistent
  283.  *        connections with the mSQL database (riscy). Escaping of ' and \.
  284.  *        Replaced some MAX_STRING_LENGTH claims. 
  285.  *       1.0  removed some error check as they where already done elsehwere
  286.  *            NumFields -> NumRows (Thanks Vitek). More stack memory.
  287.  *       1.1    no logging of empty password strings.
  288.  *        1.2  Problem with the Backward vitek which cause it to check
  289.  *        even if msql_auth was not configured; Also more carefull
  290.  *        with the authorative stuff; caught by thomas@marvin.calvacom.fr.
  291.  *       1.3  Even more changes to get it right; that BACKWARD thing was a bad
  292.  *        idea. 
  293.  */
  294.  
  295.  
  296. #define ONLY_ONCE 1
  297. /*
  298.  * If the mSQL table containing the uid/passwd combination does
  299.  * not have the uid field as a primary key, it is possible for the
  300.  * uid to occur more than once in the table with possibly different
  301.  * passwords. When this module is compiled with the ONLY_ONCE directive
  302.  * set, access is denied if the uid occures more than once in the
  303.  * uid/passwd table. If you choose not to set it, the software takes
  304.  * the first pair returned and ignores any further pairs. The SQL
  305.  * statement used for this is
  306.  *
  307.  *       "select password form pwd_table where user='uid'"
  308.  *
  309.  * this might lead to unpredictable results. For this reason as well
  310.  * as for performance reasons you are strongly adviced to make the
  311.  * uid field a primary key. Use at your own peril :-)
  312.  */
  313.  
  314. #undef KEEP_MSQL_CONNECTION_OPEN
  315. /*
  316.  * Normally the (tcp/ip) connection with the database is opened and
  317.  * closed for each SQL query. When the httpd-server and the database
  318.  * are on the same machine, and /dev/msql is used this does not
  319.  * cause a serious overhead. However when your platform does not
  320.  * support this (see the mSQL documentation) or when the web server
  321.  * and the database are on different machines the overhead can be
  322.  * considerable. When the above is set defined the server leaves the
  323.  * connection open; i.e. no call to msqlClose(). If an error occures
  324.  * an attempt is made to re-open the connection for the next http-rq.
  325.  *
  326.  * This has a number of very serious drawbacks
  327.  *  - It costs 2 already rare filedescriptors for each child.
  328.  *  - It costs msql-connections, typically one per child. The (compiled in)
  329.  *    number of connections mSQL can handle is low, typically 6 or 12.
  330.  *    which might prohibit access to the mSQL database for later
  331.  *    processes.
  332.  *  - when a child dies, it might not free that connection properly
  333.  *    or quick enough.
  334.  *  - When errors start to occur, connection/file-descr resources might
  335.  *    become exausted very quickly.
  336.  *
  337.  * In short; use this at your own peril and only in a highly controled and
  338.  * monitored environment
  339.  */
  340.  
  341. #define BACKWARD_VITEK
  342. #define VITEX_uid_name "user"
  343. #define VITEX_gid_name "passwd"
  344. /* A second mSQL auth module for apache has also been developed by
  345.  * Vivek Khera <khera@kciLink.com> and was subsequently distributed
  346.  * with some early versions of Apache. It can be optained from
  347.  * ftp://ftp.kcilink.com/pub/mod_auth_msql.c*. Older 'vitek' versions had
  348.  * the field/table names compiled in; newer versions, v.1.11 have
  349.  * more access.conf configuration options; however these where
  350.  * choosen not to be in line the 'ewse' version of this module. Also,
  351.  * the 'vitek' module does not give group control or 'empty' password
  352.  * control.
  353.  *
  354.  * To get things slightly more in line this version (0.9) should
  355.  * be backward compatible with the vitek module by:
  356.  *
  357.  *   - adding support for the EncryptedPassword on/off functionality
  358.  *
  359.  *   - adding support for the different spelling fo the 4 configuration
  360.  *     tokens for user-table-name, user/password-field-name and dbase-name.
  361.  *
  362.  *   - setting some field names to a default which used to be hard
  363.  *     coded in in older vitek modules.
  364.  *
  365.  * If this troubles you; remove the 'BACKWARD_VITEX' define.
  366.  */
  367.  
  368. /* get some sensible values; rather than that big MAX_STRING_LEN,
  369.  */
  370.  
  371. /* Max field value length limit; well above the limit of some browsers :-)
  372.  */
  373. #define MAX_FIELD_LEN (64)
  374. /* the next two values can be pulled from msql_priv.c, which is *NOT* copied to your
  375.  * /usr/local/include as part of the normal install procedure which comes with
  376.  * mSQL.
  377.  */
  378. #define MSQL_FIELD_NAME_LEN (19)
  379. #define MSQL_TABLE_NAME_LEN (19)
  380. /* We only do the following two queries:
  381.  *
  382.  * - for the user/passwd combination
  383.  *      select PWDFIELD from PWDTABEL where USERFIELD='UID'
  384.  *
  385.  * - optionally for the user/group combination:
  386.  *       select GROUPFIELD from GROUPTABLE where USERFIELD='UID' and GROUPFIELD='GID'
  387.  *
  388.  * This leads to the following limits: (we are ignoring escaping a wee bit bit here
  389.  * assuming not more than 24 escapes.)
  390.  */
  391.  
  392. #define MAX_QUERY_LEN (32+24+MAX_FIELD_LEN*2+3*MSQL_FIELD_NAME_LEN+1*MSQL_TABLE_NAME_LEN)
  393.  
  394.  
  395. #include "httpd.h"
  396. #include "http_config.h"
  397. #include "http_core.h"
  398. #include "http_log.h"
  399. #include "http_protocol.h"
  400. #include <msql.h>
  401. #ifdef HAVE_CRYPT_H
  402. #include <crypt.h>
  403. #endif
  404.  
  405. typedef struct  {
  406.  
  407.     char *auth_msql_host;
  408.     char *auth_msql_database;
  409.  
  410.     char *auth_msql_pwd_table;
  411.     char *auth_msql_grp_table;
  412.  
  413.     char *auth_msql_pwd_field;
  414.     char *auth_msql_uname_field;
  415.     char *auth_msql_grp_field;
  416.  
  417.     int auth_msql_nopasswd;
  418.     int auth_msql_authorative;
  419.     int auth_msql_encrypted;
  420.  
  421. } msql_auth_config_rec;
  422.  
  423. void *create_msql_auth_dir_config (pool *p, char *d)
  424. {
  425.     msql_auth_config_rec * sec= (msql_auth_config_rec *) pcalloc (p, sizeof(msql_auth_config_rec));
  426.  
  427.     sec->auth_msql_host        = NULL; /* just to enforce the default 'localhost' behaviour */
  428.  
  429.     /* just in case, to be nice... */
  430.     sec->auth_msql_database    = NULL;
  431.     sec->auth_msql_pwd_table   = NULL;
  432.     sec->auth_msql_grp_table   = NULL;
  433.     sec->auth_msql_pwd_field   = NULL;
  434.     sec->auth_msql_uname_field = NULL;
  435.     sec->auth_msql_grp_field   = NULL;
  436.  
  437.  
  438.     sec->auth_msql_authorative = 1; /* set some defaults, just in case... */
  439.     sec->auth_msql_encrypted   = 1;
  440.     sec->auth_msql_nopasswd    = 0;
  441.  
  442. #ifdef BACKWARD_VITEK
  443.     /* these are for backward compatibility with the Vivek
  444.      * msql module, as it used to have compile-time defaults.
  445.      */
  446.     sec->auth_msql_uname_field = VITEX_uid_name;
  447.     sec->auth_msql_pwd_field   = VITEX_gid_name;
  448. #endif
  449.  
  450.     return sec;
  451. }
  452.  
  453. char *set_passwd_flag (cmd_parms *cmd, msql_auth_config_rec *sec, int arg) {
  454.     sec->auth_msql_nopasswd=arg;
  455.     return NULL;
  456. }
  457.  
  458. char *set_authorative_flag (cmd_parms *cmd, msql_auth_config_rec *sec, int arg) {
  459.     sec->auth_msql_authorative=arg;
  460.     return NULL;
  461. }
  462.  
  463. char *set_crypted_password_flag (cmd_parms *cmd, msql_auth_config_rec *sec , int arg) {
  464.     sec->auth_msql_encrypted = arg;
  465.     return NULL;
  466. }
  467.  
  468. char *msql_set_string_slot (cmd_parms *cmd, char *struct_ptr, char *arg) {
  469.     int offset = (int)cmd->info;
  470.     *(char **)(struct_ptr + offset) = pstrdup (cmd->pool, arg);
  471.     return NULL;
  472. }
  473.  
  474.  
  475. command_rec msql_auth_cmds[] = {
  476. { "Auth_MSQLhost", msql_set_string_slot,
  477.     (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_host),
  478.     OR_AUTHCFG, TAKE1, "Host on which the mSQL database engine resides (defaults to localhost)" },
  479.  
  480. { "Auth_MSQLdatabase", msql_set_string_slot,
  481.     (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_database),
  482.     OR_AUTHCFG, TAKE1, "Name of the mSQL database which contains the password (and possibly the group) tables. " },
  483.  
  484. { "Auth_MSQLpwd_table", msql_set_string_slot,
  485.     (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_table),
  486.     OR_AUTHCFG, TAKE1, "Name of the mSQL table containing the password/user-name combination" },
  487.  
  488. { "Auth_MSQLgrp_table", msql_set_string_slot,
  489.     (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_table),
  490.     OR_AUTHCFG, TAKE1, "Name of the mSQL table containing the group-name/user-name combination; can be the same as the password-table." },
  491.  
  492. { "Auth_MSQLpwd_field", msql_set_string_slot,
  493.     (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_field),
  494.     OR_AUTHCFG, TAKE1, "The name of the field in the mSQL password table" },
  495.  
  496. { "Auth_MSQLuid_field", msql_set_string_slot,
  497.     (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_uname_field),
  498.     OR_AUTHCFG, TAKE1, "The name of the user-name field in the mSQL password (and possibly group) table(s)." },
  499.  
  500. { "Auth_MSQLgrp_field", msql_set_string_slot,
  501.     (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_field),
  502.     OR_AUTHCFG, TAKE1,
  503.     "The name of the group field in the mSQL group table; must be set if you want to use groups." },
  504.  
  505. { "Auth_MSQL_nopasswd", set_passwd_flag, NULL, OR_AUTHCFG, FLAG,
  506.     "Enable (on) or disable (off) empty password strings; in which case any user password is accepted." },
  507.  
  508. { "Auth_MSQL_Authorative", set_authorative_flag, NULL, OR_AUTHCFG, FLAG,
  509.     "When 'on' the mSQL database is taken to be authorative and access control is not passed along to other db or access modules." },
  510.  
  511. { "Auth_MSQL_EncryptedPasswords", set_crypted_password_flag, NULL, OR_AUTHCFG, FLAG,
  512.     "When 'on' the password in the password table are taken to be crypt()ed using your machines crypt() function." },
  513.  
  514. #ifdef BACKWARD_VITEK
  515. /* These 'altenative' tokens should ensure backward compatibility
  516.  * with viteks mSQL module. The only difference is the spelling.
  517.  * Note that these tokens do not allow group configuration.
  518.  */
  519. { "AuthMSQLHost", set_string_slot,
  520.     (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_host),
  521.     OR_AUTHCFG, TAKE1, "mSQL server hostname" },
  522. { "AuthMSQLDB", set_string_slot,
  523.     (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_database),
  524.     OR_AUTHCFG, TAKE1, "mSQL database name" },
  525. { "AuthMSQLUserTable", set_string_slot,
  526.     (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_table),
  527.     OR_AUTHCFG, TAKE1, "mSQL user table name" },
  528. { "AuthMSQLGroupTable", set_string_slot,
  529.     (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_table),
  530.     OR_AUTHCFG, TAKE1, "mSQL group table name" },
  531. { "AuthMSQLNameField", set_string_slot,
  532.     (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_uname_field),
  533.     OR_AUTHCFG, TAKE1, "mSQL User ID field name within table" },
  534. { "AuthMSQLGroupField", set_string_slot,
  535.     (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_field),
  536.     OR_AUTHCFG, TAKE1, "mSQL Group field name within table" },
  537. { "AuthMSQLPasswordField", set_string_slot,
  538.     (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_field),
  539.     OR_AUTHCFG, TAKE1, "mSQL Password field name within table" },
  540. { "AuthMSQLCryptedPasswords", set_crypted_password_flag, NULL,
  541.     OR_AUTHCFG, FLAG, "mSQL passwords are stored encrypted if On" },
  542.  
  543. #endif
  544.  
  545. { NULL }
  546. };
  547.  
  548. module msql_auth_module;
  549.  
  550. /* boring little routine which escapes the ' and \ in the
  551.  * SQL query. See the mSQL FAQ for more information :-) on
  552.  * this very popular subject in the msql-mailing list.
  553.  */
  554. char *msql_escape(char *out, char *in, char *msql_errstr) {
  555.  
  556.   register int i=0,j=0;
  557.  
  558.   do {
  559.     /* do we need to escape */
  560.     if ( (in[i] == '\'') || (in[i] == '\\')) {
  561.  
  562.       /* does this fit ? */
  563.       if (j >= (MAX_FIELD_LEN-1)) {
  564.     sprintf(msql_errstr,"Could not escape '%s', longer than %d",in,MAX_FIELD_LEN);
  565.     return NULL;
  566.     };
  567.  
  568.       out[j++] = '\\'; /* insert that escaping slash for good measure */
  569.     };
  570.  
  571.     /* Do things still fit ? */
  572.     if (j >= MAX_FIELD_LEN) return NULL;
  573.  
  574.   } while ( ( out[j++] = in[i++]) != '\0' );
  575.  
  576.   return out;
  577. }
  578.  
  579. /* get the password for uname=user, and copy it
  580.  * into r. Assume that user is a string and stored
  581.  * as such in the mSQL database
  582.  */
  583. char *do_msql_query(request_rec *r, char *query, msql_auth_config_rec *sec, int once , char *msql_errstr) {
  584.  
  585.         static int     sock=-1;
  586.         int        hit;
  587.         m_result     *results;
  588.         m_row         currow;
  589.  
  590.      char         *result=NULL;
  591.     char        *host=sec->auth_msql_host;
  592.  
  593. #ifndef KEEP_MSQL_CONNECTION_OPEN
  594.         sock=-1;
  595. #endif
  596.  
  597.     /* force fast access over /dev/msql */
  598.  
  599.     if ((host) && (!(strcasecmp(host,"localhost"))))
  600.         host=NULL;
  601.  
  602.     /* (re) open if nessecary
  603.      */
  604.         if (sock==-1) if ((sock=msqlConnect(host)) == -1) {
  605.         sprintf (msql_errstr,
  606.             "mSQL: Could not connect to Msql DB %s (%s)",
  607.             (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"),
  608.             msqlErrMsg);
  609.         return NULL;
  610.             }
  611.  
  612.     /* we always do this, as it avoids book-keeping
  613.      * and is quite cheap anyway
  614.      */
  615.         if (msqlSelectDB(sock,sec->auth_msql_database) == -1 ) {
  616.         sprintf (msql_errstr,"mSQL: Could not select Msql Table \'%s\' on host \'%s\'(%s)",
  617.             (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"),
  618.                 (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"),
  619.             msqlErrMsg);
  620.         msqlClose(sock);
  621.         sock=-1;
  622.         return NULL;
  623.         }
  624.  
  625.         if (msqlQuery(sock,query) == -1 ) {
  626.         sprintf (msql_errstr,"mSQL: Could not Query database '%s' on host '%s' (%s) with query [%s]",
  627.             (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"),
  628.                 (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"),
  629.                 msqlErrMsg,
  630.             ( query ? query : "\'unset!\'") );
  631.         msqlClose(sock);
  632.         sock=-1;
  633.         return NULL;
  634.         }
  635.  
  636.     if (!(results=msqlStoreResult())) {
  637.         sprintf (msql_errstr,"mSQL: Could not get the results from mSQL database \'%s\' on \'%s\' (%s) with query [%s]",
  638.             (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"),
  639.                 (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"),
  640.             msqlErrMsg,
  641.             ( query ? query : "\'unset!\'") );
  642.         msqlClose(sock);
  643.         sock=-1;
  644.         return NULL;
  645.         };
  646.  
  647.     hit=msqlNumRows(results);
  648.  
  649.     if (( once ) && ( hit >1 )) {
  650.           /* complain if there are to many
  651.            * matches.
  652.            */
  653.           sprintf (msql_errstr,"mSQL: More than %d matches (%d) whith query [%s]",
  654.                  once,hit,( query ? query : "\'unset!\'") );
  655.     } else
  656.     /* if we have a it, try to get it
  657.     */
  658.         if ( hit )  {
  659.         if ( (currow=msqlFetchRow(results)) != NULL) {
  660.             /* copy the first matching field value */
  661.             if (!(result=palloc(r->pool,strlen(currow[0])+1))) {
  662.                 sprintf (msql_errstr,"mSQL: Could not get memory for mSQL %s (%s) with [%s]",
  663.                     (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"),
  664.                     msqlErrMsg,
  665.                     ( query ? query : "\'unset!\'") );
  666.                 /* do not return right away, to ensure Free/Close.
  667.                  */
  668.                 } else {
  669.                     strcpy(result,currow[0]);
  670.                     };
  671.         }
  672.     };
  673.  
  674.     /* ignore errors, functions are voids anyway. */
  675.     msqlFreeResult(results);
  676.  
  677. #ifndef KEEP_MSQL_CONNECTION_OPEN
  678.     /* close the connection, unless explicitly told not to. Do note that
  679.      * we do not have a decent closing option of child termination due
  680.      * the lack of hooks in the API (or my understanding thereof)
  681.      */
  682.     msqlClose(sock);
  683.     sock=-1;
  684. #endif
  685.  
  686.     return result;
  687. }
  688.  
  689. char *get_msql_pw(request_rec *r, char *user, msql_auth_config_rec *sec ,char *msql_errstr) {
  690.       char         query[MAX_QUERY_LEN];
  691.     char         esc_user[MAX_FIELD_LEN];
  692.  
  693.     /* do we have enough information to build a query */
  694.     if (
  695.         (!sec->auth_msql_pwd_table) ||
  696.         (!sec->auth_msql_pwd_field) ||
  697.         (!sec->auth_msql_uname_field)
  698.        ) {
  699.         sprintf(msql_errstr,
  700.             "mSQL: Missing parameters for password lookup: %s%s%s",
  701.             (sec->auth_msql_pwd_table ? "" : "Password table "),
  702.             (sec->auth_msql_pwd_field ? "" : "Password field name "),
  703.             (sec->auth_msql_uname_field ? "" : "UserID field name ")
  704.             );
  705.         return NULL;
  706.         };
  707.  
  708.         if (!(msql_escape(esc_user, user, msql_errstr))) {
  709.         sprintf(msql_errstr,
  710.             "mSQL: Could not cope/escape the '%s' user_id value; ",user);
  711.         return NULL;
  712.         };
  713.         sprintf(query,"select %s from %s where %s='%s'",
  714.         sec->auth_msql_pwd_field,
  715.         sec->auth_msql_pwd_table,
  716.         sec->auth_msql_uname_field,
  717.         esc_user
  718.         );
  719.  
  720.     return do_msql_query(r,query,sec,ONLY_ONCE,msql_errstr);
  721. }
  722.  
  723. char *get_msql_grp(request_rec *r, char *group,char *user, msql_auth_config_rec *sec, char *msql_errstr) {
  724.       char         query[MAX_QUERY_LEN];
  725.  
  726.     char         esc_user[MAX_FIELD_LEN];
  727.     char         esc_group[MAX_FIELD_LEN];
  728.  
  729.     /* do we have enough information to build a query */
  730.     if (
  731.         (!sec->auth_msql_grp_table) ||
  732.         (!sec->auth_msql_grp_field) ||
  733.         (!sec->auth_msql_uname_field)
  734.        ) {
  735.         sprintf(msql_errstr,
  736.             "mSQL: Missing parameters for group lookup: %s%s%s",
  737.             (sec->auth_msql_grp_table ? "" : "Group table "),
  738.             (sec->auth_msql_grp_field ? "" : "GroupID field name "),
  739.             (sec->auth_msql_uname_field ? "" : "UserID field name ")
  740.             );
  741.         return NULL;
  742.         };
  743.  
  744.         if (!(msql_escape(esc_user, user,msql_errstr))) {
  745.         sprintf(msql_errstr,
  746.             "mSQL: Could not cope/escape the '%s' user_id value",user);
  747.  
  748.         return NULL;
  749.         };
  750.         if (!(msql_escape(esc_group, group,msql_errstr))) {
  751.         sprintf(msql_errstr,
  752.             "mSQL: Could not cope/escape the '%s' group_id value",group);
  753.  
  754.         return NULL;
  755.         };
  756.  
  757.         sprintf(query,"select %s from %s where %s='%s' and %s='%s'",
  758.         sec->auth_msql_grp_field,
  759.         sec->auth_msql_grp_table,
  760.         sec->auth_msql_uname_field,esc_user,
  761.         sec->auth_msql_grp_field,  esc_group
  762.         );
  763.  
  764.     return do_msql_query(r,query,sec,0,msql_errstr);
  765. }
  766.  
  767.  
  768. int msql_authenticate_basic_user (request_rec *r)
  769. {
  770.     msql_auth_config_rec *sec =
  771.       (msql_auth_config_rec *)get_module_config (r->per_dir_config,
  772.                         &msql_auth_module);
  773.     char msql_errstr[MAX_STRING_LEN];
  774.     conn_rec *c = r->connection;
  775.     char *sent_pw, *real_pw;
  776.     int res;
  777.     msql_errstr[0]='\0';
  778.  
  779.     if ((res = get_basic_auth_pw (r, &sent_pw)))
  780.         return res;
  781.  
  782.     /* if mSQL *password* checking is configured in any way, i.e. then
  783.      * handle it, if not decline and leave it to the next in line..
  784.      * We do not check on dbase, group, userid or host name, as it is
  785.      * perfectly possible to only do group control with mSQL and leave
  786.      * user control to the next (dbm) guy in line.
  787.      * We no longer check on the user field name; to avoid problems
  788.      * with Backward VITEK.
  789.      */
  790.     if (!sec->auth_msql_pwd_table) return DECLINED;
  791.  
  792.     if(!(real_pw = get_msql_pw(r, c->user, sec,msql_errstr ))) {
  793.     if ( msql_errstr[0] ) {
  794.         res = SERVER_ERROR;
  795.         } else {
  796.         if (sec->auth_msql_authorative) {
  797.                  /* insist that the user is in the database
  798.                   */
  799.                  sprintf(msql_errstr,"mSQL: Password for user %s not found", c->user);
  800.            note_basic_auth_failure (r);
  801.            res = AUTH_REQUIRED;
  802.            } else {
  803.            /* pass control on to the next authorization module.
  804.             */
  805.            return DECLINED;
  806.            }; /* if authorative */
  807.                }; /* if no error */
  808.     log_reason (msql_errstr, r->filename, r);
  809.     return res;
  810.     }
  811.  
  812.     /* allow no password, if the flag is set and the password
  813.      * is empty. But be sure to log this.
  814.      */
  815.  
  816.     if ((sec->auth_msql_nopasswd) && (!strlen(real_pw))) {
  817. /*
  818.         sprintf(msql_errstr,"mSQL: user %s: Empty/'any' password accepted",c->user);
  819.     log_reason (msql_errstr, r->uri, r);
  820.  */
  821.     return OK;
  822.     };
  823.  
  824.     /* if the flag is off however, keep that kind of stuff at
  825.      * an arms length.
  826.      */
  827.     if ((!strlen(real_pw)) || (!strlen(sent_pw))) {
  828.         sprintf(msql_errstr,"mSQL: user %s: Empty Password(s) Rejected",c->user);
  829.     log_reason (msql_errstr, r->uri, r);
  830.     note_basic_auth_failure (r);
  831.     return AUTH_REQUIRED;
  832.     };
  833.  
  834.     if(sec->auth_msql_encrypted) {
  835.         /* anyone know where the prototype for crypt is?
  836.          *
  837.          * PLEASE NOTE:
  838.          *    The crypt function (at least under FreeBSD 2.0.5) returns
  839.          *    a ptr to a *static* array (max 120 chars) and does *not*
  840.          *    modify the string pointed at by sent_pw !
  841.          */
  842.         sent_pw=(char *)crypt(sent_pw,real_pw);
  843.         };
  844.  
  845.     if (strcmp(real_pw,sent_pw)) {
  846.         sprintf(msql_errstr,"mSQL user %s: password mismatch",c->user);
  847.     log_reason (msql_errstr, r->uri, r);
  848.     note_basic_auth_failure (r);
  849.     return AUTH_REQUIRED;
  850.     }
  851.     return OK;
  852. }
  853.  
  854. /* Checking ID */
  855.  
  856. int msql_check_auth (request_rec *r) {
  857.     int user_result=DECLINED,group_result=DECLINED;
  858.  
  859.     msql_auth_config_rec *sec =
  860.       (msql_auth_config_rec *)get_module_config (r->per_dir_config,
  861.                         &msql_auth_module);
  862.     char msql_errstr[MAX_STRING_LEN];
  863.     char *user = r->connection->user;
  864.     int m = r->method_number;
  865.     array_header *reqs_arr = requires (r);
  866.     require_line *reqs = reqs_arr ? (require_line *)reqs_arr->elts : NULL;
  867.  
  868.     register int x;
  869.     char *t, *w;
  870.     msql_errstr[0]='\0';
  871.  
  872.     /* If we are not configured, ignore */
  873.     if (!sec->auth_msql_pwd_table) return DECLINED;
  874.  
  875.     if (!reqs_arr) {
  876.     if (sec->auth_msql_authorative) {
  877.             sprintf(msql_errstr,"user %s denied, no access rules specified (MSQL-Authorative) ",user);
  878.         log_reason (msql_errstr, r->uri, r);
  879.             note_basic_auth_failure(r);
  880.         return AUTH_REQUIRED;
  881.         };
  882.     return DECLINED;
  883.      };
  884.  
  885.     for(x=0; (x < reqs_arr->nelts) ; x++) {
  886.  
  887.     if (! (reqs[x].method_mask & (1 << m))) continue;
  888.  
  889.         t = reqs[x].requirement;
  890.         w = getword(r->pool, &t, ' ');
  891.  
  892.         if ((user_result != OK) && (!strcmp(w,"user"))) {
  893.         user_result=AUTH_REQUIRED;
  894.             while(t[0]) {
  895.                 w = getword_conf (r->pool, &t);
  896.                 if (!strcmp(user,w)) {
  897.                     user_result= OK;
  898.             break;
  899.         };
  900.             }
  901.         if ((sec->auth_msql_authorative) && ( user_result != OK)) {
  902.                sprintf(msql_errstr,"User %s not found (MSQL-Auhtorative)",user);
  903.         log_reason (msql_errstr, r->uri, r);
  904.                note_basic_auth_failure(r);
  905.         return AUTH_REQUIRED;
  906.         };
  907.         }
  908.  
  909.         if ( (group_result != OK) && 
  910.          (!strcmp(w,"group")) &&  
  911.              (sec->auth_msql_grp_table) && 
  912.              (sec->auth_msql_grp_field)
  913.            ) {
  914.        /* look up the membership for each of the groups in the table
  915.             */
  916.        group_result=AUTH_REQUIRED;
  917.            while ( (t[0]) && (group_result != OK) && (!msql_errstr[0]) ) {
  918.                 if (get_msql_grp(r,getword(r->pool, &t, ' '),user,sec,msql_errstr)) {
  919.             group_result= OK;
  920.             break;
  921.             };
  922.                };
  923.  
  924.        if (msql_errstr[0]) {
  925.            log_reason (msql_errstr, r->filename, r);
  926.         return SERVER_ERROR;
  927.         };
  928.  
  929.        if ( (sec->auth_msql_authorative) && (group_result != OK) ) {
  930.                sprintf(msql_errstr,"user %s not in right groups (MSQL-Authorative) ",user);
  931.         log_reason (msql_errstr, r->uri, r);
  932.                note_basic_auth_failure(r);
  933.         return AUTH_REQUIRED;
  934.         };
  935.            };
  936.  
  937.         if(!strcmp(w,"valid-user")) {
  938.             user_result= OK;
  939.         };
  940.         }
  941.  
  942.     /* Get serious if we are authorative, previous
  943.      * returns are only if msql yielded a correct result. 
  944.      * This really is not needed.
  945.      */
  946.     if (((group_result == AUTH_REQUIRED) || (user_result == AUTH_REQUIRED)) && (sec->auth_msql_authorative) ) {
  947.         sprintf(msql_errstr,"mSQL-Authorative: Access denied on %s %s rule(s) ", 
  948.         (group_result == AUTH_REQUIRED) ? "USER" : "", 
  949.         (user_result == AUTH_REQUIRED) ? "GROUP" : ""
  950.         );
  951.     log_reason (msql_errstr, r->uri, r);
  952.     return AUTH_REQUIRED;
  953.     };
  954.  
  955.     if ( (user_result == OK) || (group_result == OK))
  956.     return OK;
  957.  
  958.     return DECLINED;
  959. }
  960.  
  961.  
  962. module msql_auth_module = {
  963.    STANDARD_MODULE_STUFF,
  964.    NULL,            /* initializer */
  965.    create_msql_auth_dir_config,    /* dir config creater */
  966.    NULL,            /* dir merger --- default is to override */
  967.    NULL,            /* server config */
  968.    NULL,            /* merge server config */
  969.    msql_auth_cmds,        /* command table */
  970.    NULL,            /* handlers */
  971.    NULL,            /* filename translation */
  972.    msql_authenticate_basic_user,/* check_user_id */
  973.    msql_check_auth,        /* check auth */
  974.    NULL,            /* check access */
  975.    NULL,            /* type_checker */
  976.    NULL,            /* pre-run fixups */
  977.    NULL                /* logger */
  978. };
  979.  
  980.