/* ====================================================================
 * Copyright (c) 1995 The Apache Group.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * 4. The names "Apache Server" and "Apache Group" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission.
 *
 * 5. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
 * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Group and was originally based
 * on public domain software written at the National Center for
 * Supercomputing Applications, University of Illinois, Urbana-Champaign.
 * For more information on the Apache Group and the Apache HTTP server
 * project, please see <http://www.apache.org/>.
 *
 */


/* ===================================================================
 * mod_auth_ora8 version 2.0 - October, 30th 1999
 * Copyright (c) 1999 Ben Reser <ben@reser.org>
 *
 * Oracle8 Database Authentication Module for Apache
 *
 * I wrote this module because currently there is no module to do this.
 * You can do it with mod_perl and the DBI interfaces therein.  However,
 * copiling in a copy of Perl seems like a bit of an overkill just for
 * a simple authentication routine such as this.  
 * 
 * This Module is largely based on the mod_auth_msql module and as a result
 * all of it's copyright appears below.  I'm releasing this code under
 * the same licensing agreements as is set forth above in the orignal
 * Apache Group license.
 * 
 * For the most recent information on this module please look at:
 * http://ben.reser.org/mod_auth_ora/
 * 
 * Credit is due to the following individuals for their work on the mod_auth_msql
 * prior to my adaptation: Rob McCool, Brian Behlendorf, rst, 
 * and Dirk VanGulik <Dirk.vanGulik@jrc.it>
 *
 * Quick Compilation Instructions:
 * Make the followin modifcations to the Configuration File:
 * In EXTRA_CFLAGS add: -DORACLE_HOME=\"$(ORACLE_HOME)\" 
 * Yes the \s and "s stay
 * In EXTRA_LFLAGS add: -L$(ORACLE_HOME)/lib 
 * In EXTRA_LIBS add: -lclntsh -lnbeq8 -lnhost8 -ln8 -lncrypt8 -lnoss8 -lnidx8 -lnus8 -lnk58 -lnsslb8 -lnoname8
 * In EXTRA_INCLUDES add: -I$(ORACLE_HOME)/rdbms/demo
 * Add a line that says:  AddModule modules/extra/mod_auth_ora8.o
 * This can go just about anywhere but right around the other auth modules is a good idea.
 * Ensure that you have the ORACLE_HOME enviornment variable set properly!
 * Run the Configure script.
 * Run make 
 * 
 * No guarantee that this will work... I've only tested this personally on my machine,
 * however many of the decisions as far as where to grab things were matched with
 * DBD::Oracle of the Perl variety.  So I'm hoping this will be somewhat universal.
 * FYI: My development platform was Solaris 2.7 Sparc, Oracle 8.1.5.0, Apache 1.3.6,
 * and gcc 2.8.1...  All other platforms are at your own risk.
 *
 * Outline:
 *
 * This module allows access control using the commercial
 * Oracle8 database.
 *
 * An example table could be:
 *
 * create table user_records (
 *    user_id varchar2(32) primary key,
 *    passwd varchar2(32),
 *    grp varchar2(32)
 *  );
 *
 * The user_id can be as long as desired; however some of the
 * popular web browsers truncate, or stop the user from entering
 * names longer than 32 characters. Furthermore the 'crypt' function
 * on your platform might impose further limits. Also use of
 * the 'require users uid [uid..]' directive in the access.conf file,
 * where the user ids are separated by spaces can possibly prohibit the
 * use of spaces in your user-names. Also, not the MAX_FIELD_LEN define
 * somewhere below.
 *
 * To use the above, the following example could be in your access.conf
 * file. Also there is a more elaborate description afther this example.
 *
 * <directory /web/docs/private>
 *
 * Auth_ORA8_SID        ORCL
 *
 *                      The name of the database(SID) which  contains *both*
 *                      the tables for group and user/passwords. Currently it
 *                      is not possible to have these split over two databases.
 *                      This will be set as the ORACLE_SID environment variable.
 *                      If you want to use a network connection (non local) to
 *                      the database you'll need to use Auth_ORA8_TWO_TASK (which
 *                      is explained below).
 *
 * Auth_ORA8uid_login   wwwauth
 * Auth_ORA8pwd_login   wwwauth_pass
 *                   
 *                      These two parameters set the userid and password that
 *                      is given to Oracle to get access to the database.  
 *                      Read below for an important security note!
 *
 * Auth_ORA8pwd_table   user_records
 *
 *                      Here the table which contain the uid/password combination
 *			is specified.
 *
 * Auth_ORA8uid_field	user_id
 * Auth_ORA8pwd_field   passwd
 *
 *			These two directive specify the field names in the 'user_record'
 *			table. Currently the user_id field *MUST* be a primary key or
 *                      one must ensure that each user only occurs *once* in the table.
 *                      If a UID occurs twice access is denied by default.
 *
 * Auth_ORA8grp_table   user_records
 * Auth_ORA8grp_field	grp
 *
 *                      Optionaly one can also specify a table which contains the
 *			user/group combinations. This can be the same table which
 *			also contains the username/password combinations. However
 *			if a user belongs to two or more groups, one will have to
 *  		        use a differt table with multiple entries.
 *
 * Auth_ORA8_nopasswd	        off
 * Auth_ORA8_Authorative        on
 * Auth_ORA8_EncryptedPasswords on
 *
 *                      These three optional fields (all set to the sensible defaults,
 *			so you really do not have to enter them) are described in more
 *			detail below. If you choose to set these to any other values than
 *			the above be very sure you understand the security implications and
 *			do verify that apache does what you exect it to do.
 *
 * AuthName 		example ORA8 realm
 * AuthType		basic
 *
 *                      Normal apache/ncsa tokens for access control
 *
 * <limit get post head>
 *   order deny,allow
 *   allow from all
 *
 *   require valid-user
 *    	     	       'valid-user'; allow in any user which has a valid uid/passwd
 *    	     	       pair in the above pwd_table.
 * or
 *   require user smith jones
 *   	     	      Limit access to users who have a valid uid/passwd pair in the
 *		      above pwd_table AND whose uid is 'smith' or 'jones'. Do note that
 *		      the uid's are separated by 'spaces' for historic (ncsa) reasons.
 *		      So allowing uids with spaces might cause problems.
 *
 *   require group has_paid
 *   	     	      Optionally also ensure that the uid has the value 'has_paid' in
 *                    the group field in the group table.
 *   </limit>
 * </directory>
 *
 * End of the example
 *
 * - full description of all tokens: -
 *
 * Directives:
 *
 * Auth_ORA8_SID	Name of the database(SID) in which the following
 *			table(s) are contained.  This is the equivelent of
 *                      the ORACLE_SID environment variable.  If you're
 *                      not using SQLNET you probably just want to set this
 *                      as your local SID e.g. ORCL
 *
 * Auth_ORA8_TWO_TASK   This is the same as the TWO_TASK environment variable
 *                      and can be set to allow access to remote databases 
 *                      across a network.  If you use this setting it *WILL*
 *                      override the Auth_ORA8_SID setting above. You should
 *                      consult the proper  documentation if you want to use
 *                      this but here's an example: T:hostname:SID
 *
 * Auth_ORA8uid_login   User name to log in to Oracle as.
 * Auth_ORA8pwd_login   Password that is associated with the above user.
 *			It is *STRONGLY* recommended that you create a seperate
 *                      user in Oracle for this purpose that only has read
 *                      access to the database.  The reasonin behind this is
 *                      most configuration files are world readable on machines
 *                      and this would of course allow anyone who can read these
 *                      files an easy way in.  You probably also should only
 *                      allow permission to read the tables that you're using
 *                      for authentication.
 *
 * Auth_ORA8pwd_table	Contains at least the fields with the
 *			username and the (encrypted) password. Each
 *			uid should only occur once in this table and
 *			for performance reasons should be a primary key.
 *			Normally this table is compulsory, but it is
 *			possible to use a fall-through to other methods
 *			and use the ORA8 module for group control only;
 *			see the Authorative directive below.
 *
 * Auth_ORA8grp_table	Contains at least the fields with the
 *			username and the groupname. A user which
 *			is in multiple groups has therefore
 *			multiple entries; this might be some per-
 *			formance problems associated with this; and one
 *			might consider to have separate tables for each
 *			group (rather than all groups in one table) if
 *			your directory structure allows for it.
 *			One only needs to specify this table when doing
 *			group control.
 *
 * Auth_ORA8uid_field	Name of the field containing the username
 * Auth_ORA8pwd_field   Fieldname for the passwords
 * Auth_ORA8grp_field	Fieldname for the groupname
 *
 *                      Only the fields used need to be specified. When this
 *			module is compiled with the BACKWARD_VITEK option the
 *			uid and pwd field names default to 'user' and 'password'.
 *
 *
 * Auth_ORA8_nopasswd	<on|off>
 *			skip password comparison if passwd field is
 *			empty; i.e. allow 'any' password. This is off
 *			by default; thus to ensure that an empty field
 *			in the Oralce table does not allow people in by
 *			default with a random password.
 *
 * Auth_ORA8_Authorative <on|off>
 *			default is 'on'. When set on, there is no
 *		     	fall through to other authorization methods. So if a
 *			user is not in the Oracle table (and perhaps
 *		        not in the right group) or has the password wrong, then
 *                      he or she is denied access. When this directive is set to
 *			'off' control is passed on to any other authorization
 *			modules, such as the basic auth module wih the htpasswd
 *			file and or the unix-(g)dbm modules.
 *			The default is 'ON' to avoid nasty 'fall-through' sur-
 *			prizes. Do be sure you know what you decide to switch
 *			it off.
 *
 * Auth_ORA8_EncryptedPasswords <on|off>
 * 			default is on. When set on, the values in the
 *			pwd_field are assumed to be crypted using *your*
 *		        machines 'crypt' function; and the incoming password
 *		        is 'crypt'ed before comparison. When this function is
 *			off, the comparison is done directly with the plaintext
 *			entered password. (Yes; http-basic-auth does send the
 *			password as plaintext over the wire :-( ). The default
 *			is a sensible 'on', and I personally thing that it is
 *			a *very-bad-idea* to change this. However a multi
 *			vendor or international environment (which sometimes
 *			leads to different crypts functions) might force you to.
 *
 * Release History
 * 1.00 - April 7th, 1998 - Ben Reser <breser@vecdev.com> this was the Oracle7 version for Apache 1.2x
 * 2.00 - October 30th, 1999 - Ben Reser <ben@reser.org> Oracle8 version for Apache 1.3x
 */

/* #define ORACLE_HOME "/usr/oracle8" */
/* 
 * This should be set to the same thing as your ORACLE_HOME environment variable is
 * set to.  It's commented out so that if people follow the installation instructions
 * above then everything should work for them.
*/

/* Max field value length limit; well above the limit of some browsers :-)
 */
#define MAX_FIELD_LEN (64)

/* These are reasonable limits though certainly not the limits of Oracle.
 * Adjust these if you plan on using anything larger
 */
#define ORA8_FIELD_NAME_LEN (19)
#define ORA8_TABLE_NAME_LEN (19)

/* We only do the following two queries:
 *
 * - for the user/passwd combination
 *      select PWDFIELD from PWDTABEL where USERFIELD='UID'
 *
 * - optionally for the user/group combination:
 *   	select GROUPFIELD from GROUPTABLE where USERFIELD='UID' and GROUPFIELD='GID'
 *
 * This leads to the following limits: (we are ignoring escaping a wee bit bit here
 * assuming not more than 24 escapes.)
 */

#define MAX_QUERY_LEN (32+24+MAX_FIELD_LEN*2+3*ORA8_FIELD_NAME_LEN+1*ORA8_TABLE_NAME_LEN)


#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"


#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif

#include <oratypes.h>
#include <ocidfn.h>
#ifdef __STDCC__
#include <ociapr.h>
#else
#include <ocikpr.h>
#endif
#include <ocidem.h>

/* Oracle Params */
#define DEFER_PARSE	1
#define	VERSION_8	2

typedef struct  {

    char *auth_ora8_sid;
    char *auth_ora8_two_task;
    char *auth_ora8_uid_login;
    char *auth_ora8_pwd_login;

    char *auth_ora8_pwd_table;
    char *auth_ora8_grp_table;

    char *auth_ora8_pwd_field;
    char *auth_ora8_uname_field;
    char *auth_ora8_grp_field;

    int auth_ora8_nopasswd;
    int auth_ora8_authorative;
    int auth_ora8_encrypted;

} ora8_auth_config_rec;

void *create_ora8_auth_dir_config (pool *p, char *d)
{
    ora8_auth_config_rec * sec= (ora8_auth_config_rec *) ap_pcalloc (p, sizeof(ora8_auth_config_rec));

    /* just in case, to be nice... */
    sec->auth_ora8_sid         = NULL;
    sec->auth_ora8_two_task    = NULL;
    sec->auth_ora8_pwd_table   = NULL;
    sec->auth_ora8_grp_table   = NULL;
    sec->auth_ora8_pwd_field   = NULL;
    sec->auth_ora8_uname_field = NULL;
    sec->auth_ora8_grp_field   = NULL;
    sec->auth_ora8_uid_login   = NULL;
    sec->auth_ora8_pwd_login   = NULL;

    sec->auth_ora8_authorative = 1; /* set some defaults, just in case... */
    sec->auth_ora8_encrypted   = 1;
    sec->auth_ora8_nopasswd    = 0;

    return sec;
}

char *ora8_set_passwd_flag (cmd_parms *cmd, ora8_auth_config_rec *sec, int arg) {
    sec->auth_ora8_nopasswd=arg;
    return NULL;
}

char *ora8_set_authorative_flag (cmd_parms *cmd, ora8_auth_config_rec *sec, int arg) {
    sec->auth_ora8_authorative=arg;
    return NULL;
}

char *ora8_set_crypted_password_flag (cmd_parms *cmd, ora8_auth_config_rec *sec , int arg) {
    sec->auth_ora8_encrypted = arg;
    return NULL;
}

char *ora8_set_string_slot (cmd_parms *cmd, char *struct_ptr, char *arg) {
    int offset = (int)cmd->info;
    *(char **)(struct_ptr + offset) = ap_pstrdup (cmd->pool, arg);
    return NULL;
}


command_rec ora8_auth_cmds[] = {
{ "Auth_ORA8_SID", (void*)ora8_set_string_slot,
    (void*)XtOffsetOf(ora8_auth_config_rec, auth_ora8_sid),
    OR_AUTHCFG, TAKE1, "Name of the Oracle8 database(SID) which contains the password (and possibly the group) tables. Use Auth_ORA8_two_task for Network(SQL*NET) connections" },

{ "Auth_ORA8_TWO_TASK", (void*)ora8_set_string_slot,
    (void*)XtOffsetOf(ora8_auth_config_rec, auth_ora8_two_task),
    OR_AUTHCFG, TAKE1, "SQL*NET V2 Name of the Oracle8 database which contains the password (and possibly the group) tables. See the tnsname.ora file to set the aliases for this." },

{ "Auth_ORA8uid_login", (void*)ora8_set_string_slot,
    (void*)XtOffsetOf(ora8_auth_config_rec, auth_ora8_uid_login),
    OR_AUTHCFG, TAKE1, "The User Name to Log Into Oracle8 As" },

{ "Auth_ORA8pwd_login", (void*)ora8_set_string_slot,
    (void*)XtOffsetOf(ora8_auth_config_rec, auth_ora8_pwd_login),
    OR_AUTHCFG, TAKE1, "The Password to Log Into Orace8 As" },

{ "Auth_ORA8pwd_table", (void*)ora8_set_string_slot,
    (void*)XtOffsetOf(ora8_auth_config_rec, auth_ora8_pwd_table),
    OR_AUTHCFG, TAKE1, "Name of the Oracle8 table containing the password/user-name combination" },

{ "Auth_ORA8grp_table", (void*)ora8_set_string_slot,
    (void*)XtOffsetOf(ora8_auth_config_rec, auth_ora8_grp_table),
    OR_AUTHCFG, TAKE1, "Name of the Oracle8 table containing the group-name/user-name combination; can be the same as the password-table." },

{ "Auth_ORA8pwd_field", (void*)ora8_set_string_slot,
    (void*)XtOffsetOf(ora8_auth_config_rec, auth_ora8_pwd_field),
    OR_AUTHCFG, TAKE1, "The name of the field in the Oracle8 password table" },

{ "Auth_ORA8uid_field", (void*)ora8_set_string_slot,
    (void*)XtOffsetOf(ora8_auth_config_rec, auth_ora8_uname_field),
    OR_AUTHCFG, TAKE1, "The name of the user-name field in the Oracle8 password (and possibly group) table(s)." },

{ "Auth_ORA8grp_field", (void*)ora8_set_string_slot,
    (void*)XtOffsetOf(ora8_auth_config_rec, auth_ora8_grp_field),
    OR_AUTHCFG, TAKE1,
	"The name of the group field in the Oracle8 group table; must be set if you want to use groups." },

{ "Auth_ORA8_nopasswd", (void*)ora8_set_passwd_flag, NULL, OR_AUTHCFG, FLAG,
	"Enable (on) or disable (off) empty password strings; in which case any user password is accepted." },

{ "Auth_ORA8_Authorative", (void*)ora8_set_authorative_flag, NULL, OR_AUTHCFG, FLAG,
	"When 'on' the Oracle8 database is taken to be authorative and access control is not passed along to other db or access modules." },

{ "Auth_ORA8_EncryptedPasswords", (void*)ora8_set_crypted_password_flag, NULL, OR_AUTHCFG, FLAG,
	"When 'on' the password in the password table are taken to be crypt()ed using your machines crypt() function." },

{ NULL }
};

module ora8_auth_module;

/* boring little routine which escapes the ' and \ in the
 * SQL query. See the mSQL FAQ for more information :-) on
 * this very popular subject in the msql-mailing list.
 */
char *ora8_escape(char *out, char *in, char *ora8_errstr) {

  register int i=0,j=0;

  do {
    /* do we need to escape */
    if ( (in[i] == '\'') || (in[i] == '\\')) {

      /* does this fit ? */
      if (j >= (MAX_FIELD_LEN-1)) {
	sprintf(ora8_errstr,"Could not escape '%s', longer than %d",in,MAX_FIELD_LEN);
	return NULL;
	};

      out[j++] = '\\'; /* insert that escaping slash for good measure */
    };

    /* Do things still fit ? */
    if (j >= MAX_FIELD_LEN) return NULL;

  } while ( ( out[j++] = in[i++]) != '\0' );

  return out;
}

/* get the password for uname=user, and copy it
 * into r. Assume that user is a string and stored
 * as such in the Oracle8 database
 */
char *do_ora8_query(request_rec *r, char *query, ora8_auth_config_rec *sec, int once , char *ora8_errstr) {

	Lda_Def lda;
	ub4	hda[HDA_SIZE/sizeof(ub4)];
	Cda_Def cda1;

 	text 		*result;
	ub2		resultl;
        sb2             result_ind;

	if (!(result=ap_palloc(r->pool,256))) {
	  sprintf(ora8_errstr,"ORA8: Could not allocate memory for password");
          return NULL;
        }
        memset(result,0,256);


	/*  open if nessecary
	 */
        if (orlon(&lda,hda,(text*)sec->auth_ora8_uid_login,-1,(text*)sec->auth_ora8_pwd_login,-1,0))
	{
          text msg[512];
	  oerhms(&lda, lda.rc, msg, (sword) sizeof msg);
          msg[strlen(msg) - 1] = 0;
          sprintf(ora8_errstr,"ORA8: Could not connect to Oracle8(%s) as %s : %s",(sec->auth_ora8_two_task) ? sec->auth_ora8_two_task : sec->auth_ora8_sid ,sec->auth_ora8_uid_login,msg);
          return NULL; 
        }

	if (oopen(&cda1, &lda, (text *) 0, -1, -1, (text *) 0, -1)) {
	  text msg[512];
	  oerhms(&lda, cda1.rc, msg, (sword) sizeof msg);
          msg[strlen(msg) - 1] = 0;
	  sprintf(ora8_errstr,"ORA8: Could not get a cursor : %s",msg);
	  return NULL; 
	}

        /*  run the query
         */
        if (oparse(&cda1, query, (sb4) -1, DEFER_PARSE, (ub4) VERSION_8)) {   
          text msg[512];
          oerhms(&lda, cda1.rc, msg, (sword) sizeof msg);
          msg[strlen(msg) - 1] = 0;
          sprintf(ora8_errstr,"ORA8: Could not parse query : %s",msg);
	  return NULL;
	}


	if (odefin(&cda1, 1, result, 256,
                   (sword) STRING_TYPE,
                   (sword) -1, (sb2 *) &result_ind, (text *) 0, -1, -1,
                   (ub2 *) &resultl, (ub2 *) 0))
	{
          text msg[512];
          oerhms(&lda, cda1.rc, msg, (sword) sizeof msg);
          msg[strlen(msg) - 1] = 0;
          sprintf(ora8_errstr,"ORA8: Could not link query buffers : %s",msg);
	  return NULL;
	}


	if (oexfet(&cda1, (ub4) 1, FALSE, FALSE)) {
	  if (cda1.rc != NO_DATA_FOUND) {
            text msg[512];
            oerhms(&lda, cda1.rc, msg, (sword) sizeof msg);
            msg[strlen(msg) - 1] = 0;
            sprintf(ora8_errstr,"ORA8: Query Execution Error : %s",msg);
     	    return NULL;
	  } else {
            /* didn't find a matching user so return... the error message
             * will be picked up above
            */
            return NULL;
          }
	}   
          /* XXX: should complain if there are to many
           * matches.
           */
  
	/* Check for NULL returns */
	if (result_ind) {
	  strcpy(result,"\0");
        }

	oclose(&cda1);
	ologof(&lda);

	return result;
}

char *get_ora8_pw(request_rec *r, char *user, ora8_auth_config_rec *sec ,char *ora8_errstr) {
  	char 		query[MAX_QUERY_LEN];
	char 		esc_user[MAX_FIELD_LEN];
        int		err_esc = 0;


	/* do we have enough information to build a query */
	if ((!sec->auth_ora8_sid) && (!sec->auth_ora8_two_task)) {
                if (err_esc) {
			sprintf(ora8_errstr,"%s, %s",ora8_errstr, "Auth_ORA8_SID or Auth_ORA8_TWO_TASK");
		} else {
			sprintf(ora8_errstr,"%s","Auth_ORA8_SID or Auth_ORA8_TWO_TASK");		
		}
 		err_esc = 1;
        } if (!sec->auth_ora8_uid_login) {
                if (err_esc) {
			sprintf(ora8_errstr,"%s, %s",ora8_errstr, "Auth_ORA8uid_login");
		} else {
			sprintf(ora8_errstr,"%s","Auth_ORA8uid_login");		
		}
 		err_esc = 1;
	} if (!sec->auth_ora8_pwd_login) {
                if (err_esc) {
			sprintf(ora8_errstr,"%s, %s",ora8_errstr, "Auth_ORA8pwd_login");
		} else {
			sprintf(ora8_errstr,"%s","Auth_ORA8pwd_login");		
		}
 		err_esc = 1;
	} if (!sec->auth_ora8_pwd_table) {
                if (err_esc) {
			sprintf(ora8_errstr,"%s, %s",ora8_errstr, "Auth_ORA8pwd_table");
		} else {
			sprintf(ora8_errstr,"%s","Auth_ORA8pwd_table");		
		}
 		err_esc = 1;
	} if (!sec->auth_ora8_uname_field) {
                if (err_esc) {
			sprintf(ora8_errstr,"%s, %s",ora8_errstr, "Auth_ORA8uid_field");
		} else {
			sprintf(ora8_errstr,"%s","Auth_ORA8uid_field");		
		}
 		err_esc = 1;
	} if (!sec->auth_ora8_pwd_field) {
                if (err_esc) {
			sprintf(ora8_errstr,"%s, %s",ora8_errstr, "Auth_ORA8pwd_field");
		} else {
			sprintf(ora8_errstr,"%s","Auth_ORA8pwd_field");		
		}
 		err_esc = 1;
	} if (err_esc) {
		char tempstr[MAX_STRING_LEN];
		sprintf(tempstr,"ORA8: Missing parameters for password lookup: %s",ora8_errstr); 
		strcpy(ora8_errstr,tempstr);
		return NULL;
	}

    	if (!(ora8_escape(esc_user, user, ora8_errstr))) {
		sprintf(ora8_errstr,
			"ORA8: Could not cope/escape the '%s' user_id value; ",user);
		return NULL;
    	};
    	sprintf(query,"select %s from %s where %s='%s'",
		sec->auth_ora8_pwd_field,
		sec->auth_ora8_pwd_table,
		sec->auth_ora8_uname_field,
		esc_user
		);

	return do_ora8_query(r,query,sec,1,ora8_errstr);
}

char *get_ora8_grp(request_rec *r, char *group,char *user, ora8_auth_config_rec *sec, char *ora8_errstr) {
  	char 		query[MAX_QUERY_LEN];

	char 		esc_user[MAX_FIELD_LEN];
	char 		esc_group[MAX_FIELD_LEN];
	int 		err_esc = 0;

	/* do we have enough information to build a query */
	if ((!sec->auth_ora8_sid) && (!sec->auth_ora8_two_task)) {
                if (err_esc) {
			sprintf(ora8_errstr,"%s, %s",ora8_errstr, "Auth_ORA8_SID or Auth_ORA8_TWO_TASK");
		} else {
			sprintf(ora8_errstr,"%s","Auth_ORA8_SID or Auth_ORA8_TWO_TASK");		
		}
 		err_esc = 1;
        } if (!sec->auth_ora8_uid_login) {
                if (err_esc) {
			sprintf(ora8_errstr,"%s, %s",ora8_errstr, "Auth_ORA8uid_login");
		} else {
			sprintf(ora8_errstr,"%s","Auth_ORA8uid_login");		
		}
 		err_esc = 1;
	} if (!sec->auth_ora8_pwd_login) {
                if (err_esc) {
			sprintf(ora8_errstr,"%s, %s",ora8_errstr, "Auth_ORA8pwd_login");
		} else {
			sprintf(ora8_errstr,"%s","Auth_ORA8pwd_login");		
		}
 		err_esc = 1;
	} if ((!sec->auth_ora8_grp_table)) {
                if (err_esc) {
			sprintf(ora8_errstr,"%s, %s",ora8_errstr, "Auth_ORA8grp_table");
		} else {
			sprintf(ora8_errstr,"%s","Auth_ORA8grp_table");		
		}
 		err_esc = 1;
	} if ((!sec->auth_ora8_uname_field)) {
                if (err_esc) {
			sprintf(ora8_errstr,"%s, %s",ora8_errstr,"Auth_ORA8uname_field");
		} else {
			sprintf(ora8_errstr,"%s","Auth_ORA8uname_field");		
		}
 		err_esc = 1;
	} if ((!sec->auth_ora8_grp_field)) {
                if (err_esc) {
			sprintf(ora8_errstr,"%s, %s",ora8_errstr, "Auth_ORA8grp_field");
		} else {
			sprintf(ora8_errstr,"%s","Auth_ORA8grp_field");		
		}
 		err_esc = 1;
	} if (err_esc) {
		char tempstr[MAX_STRING_LEN];
		sprintf(tempstr,"ORA8: Missing parameters for password lookup: %s",ora8_errstr);
		strcpy(ora8_errstr,tempstr);
		return NULL;
	}

    	if (!(ora8_escape(esc_user, user,ora8_errstr))) {
		sprintf(ora8_errstr,
			"ORA8: Could not cope/escape the '%s' user_id value",user);

		return NULL;
    	}
    	if (!(ora8_escape(esc_group, group,ora8_errstr))) {
		sprintf(ora8_errstr,
			"ORA8: Could not cope/escape the '%s' group_id value",group);

		return NULL;
   	}

    	sprintf(query,"select %s from %s where %s='%s' and %s='%s'",
		sec->auth_ora8_grp_field,
		sec->auth_ora8_grp_table,
		sec->auth_ora8_uname_field,esc_user,
		sec->auth_ora8_grp_field,  esc_group
		);

	return do_ora8_query(r,query,sec,0,ora8_errstr);
}


int ora8_authenticate_basic_user (request_rec *r)
{
    ora8_auth_config_rec *sec =
      (ora8_auth_config_rec *)ap_get_module_config (r->per_dir_config,
						&ora8_auth_module);
    char ora8_errstr[MAX_STRING_LEN];
    conn_rec *c = r->connection;
    char *sent_pw, *real_pw;
    int res;
    char *ora_sid,*ora_home,*two_task;

    ora8_errstr[0]='\0';

    if (sec->auth_ora8_sid) {
      ora_sid = (char *)ap_palloc(r->pool,15 + strlen(sec->auth_ora8_sid));
      sprintf(ora_sid,"ORACLE_SID=%s",sec->auth_ora8_sid);
      putenv(ora_sid);
    }
    if (sec->auth_ora8_two_task) {
      two_task = (char *)ap_palloc(r->pool,15 + strlen(sec->auth_ora8_two_task));
      sprintf(two_task,"TWO_TASK=%s",sec->auth_ora8_two_task);
      putenv(two_task);
    }
    ora_home = (char *)ap_palloc(r->pool,15 + strlen(ORACLE_HOME)); 
    sprintf(ora_home,"ORACLE_HOME=%s",ORACLE_HOME);
    putenv(ora_home);

    if ((res = ap_get_basic_auth_pw (r, &sent_pw)))
        return res;

    /* if ORA8 *password* checking is configured in any way, i.e. then
     * handle it, if not decline and leave it to the next in line..
     * We do not check on dbase, group, userid or host name, as it is
     * perfectly possible to only do group control with ORA8 and leave
     * user control to the next (dbm) guy in line.
     * We no longer check on the user field name; to avoid problems
     * with Backward VITEK.
     */
    if (!sec->auth_ora8_pwd_table) return DECLINED;

    if(!(real_pw = get_ora8_pw(r, c->user, sec,ora8_errstr ))) {
	if ( ora8_errstr[0] ) {
		res = SERVER_ERROR;
		} else {
		if (sec->auth_ora8_authorative) {
          	   /* insist that the user is in the database
          	    */
          	   sprintf(ora8_errstr,"ORA8: Password for user %s not found", c->user);
		   ap_note_basic_auth_failure (r);
		   res = AUTH_REQUIRED;
		   } else {
		   /* pass control on to the next authorization module.
		    */
		   return DECLINED;
		   }; /* if authorative */
               }; /* if no error */
	ap_log_reason (ora8_errstr, r->filename, r);
	return res;
    }

    /* allow no password, if the flag is set and the password
     * is empty. But be sure to log this.
     */

    if ((sec->auth_ora8_nopasswd) && (!strlen(real_pw))) {
/*
        sprintf(ora8_errstr,"ORA8: user %s: Empty/'any' password accepted",c->user);
	ap_log_reason (ora8_errstr, r->uri, r);
 */
	return OK;
	};

    /* if the flag is off however, keep that kind of stuff at
     * an arms length.
     */
    if ((!strlen(real_pw)) || (!strlen(sent_pw))) {
        sprintf(ora8_errstr,"ORA8: user %s: Empty Password(s) Rejected",c->user);
	ap_log_reason (ora8_errstr, r->uri, r);
	ap_note_basic_auth_failure (r);
	return AUTH_REQUIRED;
	};

    if(sec->auth_ora8_encrypted) {
        /* anyone know where the prototype for crypt is?
         *
         * PLEASE NOTE:
         *    The crypt function (at least under FreeBSD 2.0.5) returns
         *    a ptr to a *static* array (max 120 chars) and does *not*
         *    modify the string pointed at by sent_pw !
         */
        sent_pw=(char *)crypt(sent_pw,real_pw);
        };

    if (strcmp(real_pw,sent_pw)) {
        sprintf(ora8_errstr,"ORA8: user %s: password mismatch",c->user);
	ap_log_reason (ora8_errstr, r->uri, r);
	ap_note_basic_auth_failure (r);
	return AUTH_REQUIRED;
    }
    return OK;
}

/* Checking ID */

int ora8_check_auth (request_rec *r) {
    int user_result=DECLINED,group_result=DECLINED;
    char *ora_sid,*ora_home,*two_task;

    ora8_auth_config_rec *sec =
      (ora8_auth_config_rec *)ap_get_module_config (r->per_dir_config,
						&ora8_auth_module);
    char ora8_errstr[MAX_STRING_LEN];
    char *user = r->connection->user;
    int m = r->method_number;
    array_header *reqs_arr = ap_requires (r);
    require_line *reqs = reqs_arr ? (require_line *)reqs_arr->elts : NULL;

    register int x;
    char *t, *w;
    ora8_errstr[0]='\0';

    /* If we are not configured, ignore */
    if (!sec->auth_ora8_pwd_table) return DECLINED;

    if (sec->auth_ora8_sid) {
      ora_sid = (char *)ap_palloc(r->pool,15 + strlen(sec->auth_ora8_sid));
      sprintf(ora_sid,"ORACLE_SID=%s",sec->auth_ora8_sid);
      putenv(ora_sid);
    } 
    if (sec->auth_ora8_two_task) {
      two_task = (char *)ap_palloc(r->pool,15 + strlen(sec->auth_ora8_two_task));
      sprintf(two_task,"TWO_TASK=%s",sec->auth_ora8_two_task);
      putenv(two_task);
    }   
    ora_home = (char *)ap_palloc(r->pool,15 + strlen(ORACLE_HOME)); 
    sprintf(ora_home,"ORACLE_HOME=%s",ORACLE_HOME);
    putenv(ora_home);

    if (!reqs_arr) {
	if (sec->auth_ora8_authorative) {
	        sprintf(ora8_errstr,"user %s denied, no access rules specified (ORA8-Authorative) ",user);
		ap_log_reason (ora8_errstr, r->uri, r);
	        ap_note_basic_auth_failure(r);
		return AUTH_REQUIRED;
		};
	return DECLINED;
 	};

    for(x=0; (x < reqs_arr->nelts) ; x++) {

	if (! (reqs[x].method_mask & (1 << m))) continue;

        t = reqs[x].requirement;
        w = ap_getword(r->pool, (void*)&t, ' ');

        if ((user_result != OK) && (!strcmp(w,"user"))) {
	    user_result=AUTH_REQUIRED;
            while(t[0]) {
                w = ap_getword_conf (r->pool,(void*) &t);
                if (!strcmp(user,w)) {
                    user_result= OK;
		    break;
		};
            }
	    if ((sec->auth_ora8_authorative) && ( user_result != OK)) {
           	sprintf(ora8_errstr,"User %s not found (ORA8-Authorative)",user);
		ap_log_reason (ora8_errstr, r->uri, r);
           	ap_note_basic_auth_failure(r);
		return AUTH_REQUIRED;
		};
        }

        if ( (group_result != OK) && 
	     (!strcmp(w,"group")) &&  
             (sec->auth_ora8_grp_table) && 
             (sec->auth_ora8_grp_field)
           ) {
	   /* look up the membership for each of the groups in the table
            */
	   group_result=AUTH_REQUIRED;
           while ( (t[0]) && (group_result != OK) && (!ora8_errstr[0]) ) {
                if (get_ora8_grp(r,ap_getword(r->pool,(void*) &t, ' '),user,sec,ora8_errstr)) {
			group_result= OK;
			break;
			};
       		};

	   if (ora8_errstr[0]) {
	   	ap_log_reason (ora8_errstr, r->filename, r);
		return SERVER_ERROR;
		};

	   if ( (sec->auth_ora8_authorative) && (group_result != OK) ) {
           	sprintf(ora8_errstr,"user %s not in right groups (ORA8-Authorative) ",user);
		ap_log_reason (ora8_errstr, r->uri, r);
           	ap_note_basic_auth_failure(r);
		return AUTH_REQUIRED;
		};
           };

        if(!strcmp(w,"valid-user")) {
            user_result= OK;
	    };
        }

    /* Get serious if we are authorative, previous
     * returns are only if ORA8 yielded a correct result. 
     * This really is not needed.
     */
    if (((group_result == AUTH_REQUIRED) || (user_result == AUTH_REQUIRED)) && (sec->auth_ora8_authorative) ) {
        sprintf(ora8_errstr,"ORA8-Authorative: Access denied on %s %s rule(s) ", 
		(group_result == AUTH_REQUIRED) ? "USER" : "", 
		(user_result == AUTH_REQUIRED) ? "GROUP" : ""
		);
	ap_log_reason (ora8_errstr, r->uri, r);
	return AUTH_REQUIRED;
	};

    if ( (user_result == OK) || (group_result == OK))
	return OK;

    return DECLINED;
}


module ora8_auth_module = {
   STANDARD_MODULE_STUFF,
   NULL,			/* initializer */
   create_ora8_auth_dir_config,	/* dir config creater */
   NULL,			/* dir merger --- default is to override */
   NULL,			/* server config */
   NULL,			/* merge server config */
   ora8_auth_cmds,		/* command table */
   NULL,			/* handlers */
   NULL,			/* filename translation */
   ora8_authenticate_basic_user,/* check_user_id */
   ora8_check_auth,		/* check auth */
   NULL,			/* check access */
   NULL,			/* type_checker */
   NULL,			/* pre-run fixups */
   NULL				/* logger */
};

