/* ====================================================================
 * 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_ora7 version 1.0 - April, 7th 1998
 * Copyright (c) 1998 Ben Reser <breser@vecdev.com>
 *
 * Oracle7 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://www.vecdev.com/mod_auth_ora7/
 * 
 * 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: -lclient -lsqlnet -lncr -lsqlnet -lclient -lcommon -lgeneric -lsqlnet -lncr -lsqlnet -lclient -lcommon -lgeneric -lepc -lnlsrtl3 -lc3v6 -lcore3 -lnlsrtl3 -lcore3 -lnlsrtl3 -lsocket -lnsl -lm -ldl -lc -laio -lposix4 -lm -lcore3
 * Yes there are dupilicates in there... don't ask me why it just doesn't work unless 
 * they are there, be sure that this is all on one line and that they are in the same
 * order I've given them to you.
 * In EXTRA_INCLUDES add: -I$(ORACLE_HOME)/rdbms/demo
 * Add a line that says:  Module ora7_auth_module    mod_auth_ora7.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.6 X86, Oracle 7.3.2.3, Apache 1.2.4,
 * and gcc 2.7.2.3...  All other platforms are at your own risk.
 *
 * Outline:
 *
 * This module allows access control using the commercial
 * Oracle7 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_ORA7_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_ORA7_TWO_TASK (which
 *                      is explained below).
 *
 * Auth_ORA7uid_login   wwwauth
 * Auth_ORA7pwd_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_ORA7pwd_table   user_records
 *
 *                      Here the table which contain the uid/password combination
 *			is specified.
 *
 * Auth_ORA7uid_field	user_id
 * Auth_ORA7pwd_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_ORA7grp_table   user_records
 * Auth_ORA7grp_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_ORA7_nopasswd	        off
 * Auth_ORA7_Authorative        on
 * Auth_ORA7_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 ORA7 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_ORA7_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_ORA7_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_ORA7_SID setting above. You should
 *                      consult the proper  documentation if you want to use
 *                      this but here's an example: T:hostname:SID
 *
 * Auth_ORA7uid_login   User name to log in to Oracle as.
 * Auth_ORA7pwd_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_ORA7pwd_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 ORA7 module for group control only;
 *			see the Authorative directive below.
 *
 * Auth_ORA7grp_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_ORA7uid_field	Name of the field containing the username
 * Auth_ORA7pwd_field   Fieldname for the passwords
 * Auth_ORA7grp_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_ORA7_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 7 table does not allow people in by
 *			default with a random password.
 *
 * Auth_ORA7_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 7 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_ORA7_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>
 */

/* #define ORACLE_HOME "/usr/oracle7" */
/* 
 * 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 ORA7_FIELD_NAME_LEN (19)
#define ORA7_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*ORA7_FIELD_NAME_LEN+1*ORA7_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_7	2

typedef struct  {

    char *auth_ora7_sid;
    char *auth_ora7_two_task;
    char *auth_ora7_uid_login;
    char *auth_ora7_pwd_login;

    char *auth_ora7_pwd_table;
    char *auth_ora7_grp_table;

    char *auth_ora7_pwd_field;
    char *auth_ora7_uname_field;
    char *auth_ora7_grp_field;

    int auth_ora7_nopasswd;
    int auth_ora7_authorative;
    int auth_ora7_encrypted;

} ora7_auth_config_rec;

void *create_ora7_auth_dir_config (pool *p, char *d)
{
    ora7_auth_config_rec * sec= (ora7_auth_config_rec *) pcalloc (p, sizeof(ora7_auth_config_rec));

    /* just in case, to be nice... */
    sec->auth_ora7_sid         = NULL;
    sec->auth_ora7_two_task    = NULL;
    sec->auth_ora7_pwd_table   = NULL;
    sec->auth_ora7_grp_table   = NULL;
    sec->auth_ora7_pwd_field   = NULL;
    sec->auth_ora7_uname_field = NULL;
    sec->auth_ora7_grp_field   = NULL;
    sec->auth_ora7_uid_login   = NULL;
    sec->auth_ora7_pwd_login   = NULL;

    sec->auth_ora7_authorative = 1; /* set some defaults, just in case... */
    sec->auth_ora7_encrypted   = 1;
    sec->auth_ora7_nopasswd    = 0;

    return sec;
}

char *ora7_set_passwd_flag (cmd_parms *cmd, ora7_auth_config_rec *sec, int arg) {
    sec->auth_ora7_nopasswd=arg;
    return NULL;
}

char *ora7_set_authorative_flag (cmd_parms *cmd, ora7_auth_config_rec *sec, int arg) {
    sec->auth_ora7_authorative=arg;
    return NULL;
}

char *ora7_set_crypted_password_flag (cmd_parms *cmd, ora7_auth_config_rec *sec , int arg) {
    sec->auth_ora7_encrypted = arg;
    return NULL;
}

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


command_rec ora7_auth_cmds[] = {
{ "Auth_ORA7_SID", (void*)ora7_set_string_slot,
    (void*)XtOffsetOf(ora7_auth_config_rec, auth_ora7_sid),
    OR_AUTHCFG, TAKE1, "Name of the Oracle7 database(SID) which contains the password (and possibly the group) tables. Use Auth_ORA7_two_task for Network(SQL*NET) connections" },

{ "Auth_ORA7_TWO_TASK", (void*)ora7_set_string_slot,
    (void*)XtOffsetOf(ora7_auth_config_rec, auth_ora7_two_task),
    OR_AUTHCFG, TAKE1, "SQL*NET V2 Name of the Oracle7 database which contains the password (and possibly the group) tables. See the tnsname.ora file to set the aliases for this." },

{ "Auth_ORA7uid_login", (void*)ora7_set_string_slot,
    (void*)XtOffsetOf(ora7_auth_config_rec, auth_ora7_uid_login),
    OR_AUTHCFG, TAKE1, "The User Name to Log Into Oracle7 As" },

{ "Auth_ORA7pwd_login", (void*)ora7_set_string_slot,
    (void*)XtOffsetOf(ora7_auth_config_rec, auth_ora7_pwd_login),
    OR_AUTHCFG, TAKE1, "The Password to Log Into Orace7 As" },

{ "Auth_ORA7pwd_table", (void*)ora7_set_string_slot,
    (void*)XtOffsetOf(ora7_auth_config_rec, auth_ora7_pwd_table),
    OR_AUTHCFG, TAKE1, "Name of the Oracle7 table containing the password/user-name combination" },

{ "Auth_ORA7grp_table", (void*)ora7_set_string_slot,
    (void*)XtOffsetOf(ora7_auth_config_rec, auth_ora7_grp_table),
    OR_AUTHCFG, TAKE1, "Name of the Oracle7 table containing the group-name/user-name combination; can be the same as the password-table." },

{ "Auth_ORA7pwd_field", (void*)ora7_set_string_slot,
    (void*)XtOffsetOf(ora7_auth_config_rec, auth_ora7_pwd_field),
    OR_AUTHCFG, TAKE1, "The name of the field in the Oracle7 password table" },

{ "Auth_ORA7uid_field", (void*)ora7_set_string_slot,
    (void*)XtOffsetOf(ora7_auth_config_rec, auth_ora7_uname_field),
    OR_AUTHCFG, TAKE1, "The name of the user-name field in the Oracle7 password (and possibly group) table(s)." },

{ "Auth_ORA7grp_field", (void*)ora7_set_string_slot,
    (void*)XtOffsetOf(ora7_auth_config_rec, auth_ora7_grp_field),
    OR_AUTHCFG, TAKE1,
	"The name of the group field in the Oracle7 group table; must be set if you want to use groups." },

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

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

{ "Auth_ORA7_EncryptedPasswords", (void*)ora7_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 ora7_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 *ora7_escape(char *out, char *in, char *ora7_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(ora7_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 Oracle7 database
 */
char *do_ora7_query(request_rec *r, char *query, ora7_auth_config_rec *sec, int once , char *ora7_errstr) {

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

 	text 		*result;
	ub2		resultl;
        sb2             result_ind;

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


	/*  open if nessecary
	 */
        if (orlon(&lda,hda,(text*)sec->auth_ora7_uid_login,-1,(text*)sec->auth_ora7_pwd_login,-1,0))
	{
          text msg[512];
	  oerhms(&lda, lda.rc, msg, (sword) sizeof msg);
          msg[strlen(msg) - 1] = 0;
          sprintf(ora7_errstr,"ORA7: Could not connect to Oracle7(%s) as %s : %s",(sec->auth_ora7_two_task) ? sec->auth_ora7_two_task : sec->auth_ora7_sid ,sec->auth_ora7_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(ora7_errstr,"ORA7: Could not get a cursor : %s",msg);
	  return NULL; 
	}

        /*  run the query
         */
        if (oparse(&cda1, query, (sb4) -1, DEFER_PARSE, (ub4) VERSION_7)) {   
          text msg[512];
          oerhms(&lda, cda1.rc, msg, (sword) sizeof msg);
          msg[strlen(msg) - 1] = 0;
          sprintf(ora7_errstr,"ORA7: 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(ora7_errstr,"ORA7: 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(ora7_errstr,"ORA7: 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_ora7_pw(request_rec *r, char *user, ora7_auth_config_rec *sec ,char *ora7_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_ora7_sid) && (!sec->auth_ora7_two_task)) {
                if (err_esc) {
			sprintf(ora7_errstr,"%s, %s",ora7_errstr, "Auth_ORA7_SID or Auth_ORA7_TWO_TASK");
		} else {
			sprintf(ora7_errstr,"%s","Auth_ORA7_SID or Auth_ORA7_TWO_TASK");		
		}
 		err_esc = 1;
        } if (!sec->auth_ora7_uid_login) {
                if (err_esc) {
			sprintf(ora7_errstr,"%s, %s",ora7_errstr, "Auth_ORA7uid_login");
		} else {
			sprintf(ora7_errstr,"%s","Auth_ORA7uid_login");		
		}
 		err_esc = 1;
	} if (!sec->auth_ora7_pwd_login) {
                if (err_esc) {
			sprintf(ora7_errstr,"%s, %s",ora7_errstr, "Auth_ORA7pwd_login");
		} else {
			sprintf(ora7_errstr,"%s","Auth_ORA7pwd_login");		
		}
 		err_esc = 1;
	} if (!sec->auth_ora7_pwd_table) {
                if (err_esc) {
			sprintf(ora7_errstr,"%s, %s",ora7_errstr, "Auth_ORA7pwd_table");
		} else {
			sprintf(ora7_errstr,"%s","Auth_ORA7pwd_table");		
		}
 		err_esc = 1;
	} if (!sec->auth_ora7_uname_field) {
                if (err_esc) {
			sprintf(ora7_errstr,"%s, %s",ora7_errstr, "Auth_ORA7uid_field");
		} else {
			sprintf(ora7_errstr,"%s","Auth_ORA7uid_field");		
		}
 		err_esc = 1;
	} if (!sec->auth_ora7_pwd_field) {
                if (err_esc) {
			sprintf(ora7_errstr,"%s, %s",ora7_errstr, "Auth_ORA7pwd_field");
		} else {
			sprintf(ora7_errstr,"%s","Auth_ORA7pwd_field");		
		}
 		err_esc = 1;
	} if (err_esc) {
		char tempstr[MAX_STRING_LEN];
		sprintf(tempstr,"ORA7: Missing parameters for password lookup: %s",ora7_errstr); 
		strcpy(ora7_errstr,tempstr);
		return NULL;
	}

    	if (!(ora7_escape(esc_user, user, ora7_errstr))) {
		sprintf(ora7_errstr,
			"ORA7: Could not cope/escape the '%s' user_id value; ",user);
		return NULL;
    	};
    	sprintf(query,"select %s from %s where %s='%s'",
		sec->auth_ora7_pwd_field,
		sec->auth_ora7_pwd_table,
		sec->auth_ora7_uname_field,
		esc_user
		);

	return do_ora7_query(r,query,sec,1,ora7_errstr);
}

char *get_ora7_grp(request_rec *r, char *group,char *user, ora7_auth_config_rec *sec, char *ora7_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_ora7_sid) && (!sec->auth_ora7_two_task)) {
                if (err_esc) {
			sprintf(ora7_errstr,"%s, %s",ora7_errstr, "Auth_ORA7_SID or Auth_ORA7_TWO_TASK");
		} else {
			sprintf(ora7_errstr,"%s","Auth_ORA7_SID or Auth_ORA7_TWO_TASK");		
		}
 		err_esc = 1;
        } if (!sec->auth_ora7_uid_login) {
                if (err_esc) {
			sprintf(ora7_errstr,"%s, %s",ora7_errstr, "Auth_ORA7uid_login");
		} else {
			sprintf(ora7_errstr,"%s","Auth_ORA7uid_login");		
		}
 		err_esc = 1;
	} if (!sec->auth_ora7_pwd_login) {
                if (err_esc) {
			sprintf(ora7_errstr,"%s, %s",ora7_errstr, "Auth_ORA7pwd_login");
		} else {
			sprintf(ora7_errstr,"%s","Auth_ORA7pwd_login");		
		}
 		err_esc = 1;
	} if ((!sec->auth_ora7_grp_table)) {
                if (err_esc) {
			sprintf(ora7_errstr,"%s, %s",ora7_errstr, "Auth_ORA7grp_table");
		} else {
			sprintf(ora7_errstr,"%s","Auth_ORA7grp_table");		
		}
 		err_esc = 1;
	} if ((!sec->auth_ora7_uname_field)) {
                if (err_esc) {
			sprintf(ora7_errstr,"%s, %s",ora7_errstr,"Auth_ORA7uname_field");
		} else {
			sprintf(ora7_errstr,"%s","Auth_ORA7uname_field");		
		}
 		err_esc = 1;
	} if ((!sec->auth_ora7_grp_field)) {
                if (err_esc) {
			sprintf(ora7_errstr,"%s, %s",ora7_errstr, "Auth_ORA7grp_field");
		} else {
			sprintf(ora7_errstr,"%s","Auth_ORA7grp_field");		
		}
 		err_esc = 1;
	} if (err_esc) {
		char tempstr[MAX_STRING_LEN];
		sprintf(tempstr,"ORA7: Missing parameters for password lookup: %s",ora7_errstr);
		strcpy(ora7_errstr,tempstr);
		return NULL;
	}

    	if (!(ora7_escape(esc_user, user,ora7_errstr))) {
		sprintf(ora7_errstr,
			"ORA7: Could not cope/escape the '%s' user_id value",user);

		return NULL;
    	}
    	if (!(ora7_escape(esc_group, group,ora7_errstr))) {
		sprintf(ora7_errstr,
			"ORA7: 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_ora7_grp_field,
		sec->auth_ora7_grp_table,
		sec->auth_ora7_uname_field,esc_user,
		sec->auth_ora7_grp_field,  esc_group
		);

	return do_ora7_query(r,query,sec,0,ora7_errstr);
}


int ora7_authenticate_basic_user (request_rec *r)
{
    ora7_auth_config_rec *sec =
      (ora7_auth_config_rec *)get_module_config (r->per_dir_config,
						&ora7_auth_module);
    char ora7_errstr[MAX_STRING_LEN];
    conn_rec *c = r->connection;
    char *sent_pw, *real_pw;
    int res;
    char *ora_sid,*ora_home,*two_task;

    ora7_errstr[0]='\0';

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

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

    /* if ORA7 *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 ORA7 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_ora7_pwd_table) return DECLINED;

    if(!(real_pw = get_ora7_pw(r, c->user, sec,ora7_errstr ))) {
	if ( ora7_errstr[0] ) {
		res = SERVER_ERROR;
		} else {
		if (sec->auth_ora7_authorative) {
          	   /* insist that the user is in the database
          	    */
          	   sprintf(ora7_errstr,"ORA7: Password for user %s not found", c->user);
		   note_basic_auth_failure (r);
		   res = AUTH_REQUIRED;
		   } else {
		   /* pass control on to the next authorization module.
		    */
		   return DECLINED;
		   }; /* if authorative */
               }; /* if no error */
	log_reason (ora7_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_ora7_nopasswd) && (!strlen(real_pw))) {
/*
        sprintf(ora7_errstr,"ORA7: user %s: Empty/'any' password accepted",c->user);
	log_reason (ora7_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(ora7_errstr,"ORA7: user %s: Empty Password(s) Rejected",c->user);
	log_reason (ora7_errstr, r->uri, r);
	note_basic_auth_failure (r);
	return AUTH_REQUIRED;
	};

    if(sec->auth_ora7_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(ora7_errstr,"ORA7: user %s: password mismatch",c->user);
	log_reason (ora7_errstr, r->uri, r);
	note_basic_auth_failure (r);
	return AUTH_REQUIRED;
    }
    return OK;
}

/* Checking ID */

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

    ora7_auth_config_rec *sec =
      (ora7_auth_config_rec *)get_module_config (r->per_dir_config,
						&ora7_auth_module);
    char ora7_errstr[MAX_STRING_LEN];
    char *user = r->connection->user;
    int m = r->method_number;
    array_header *reqs_arr = requires (r);
    require_line *reqs = reqs_arr ? (require_line *)reqs_arr->elts : NULL;

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

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

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

    if (!reqs_arr) {
	if (sec->auth_ora7_authorative) {
	        sprintf(ora7_errstr,"user %s denied, no access rules specified (ORA7-Authorative) ",user);
		log_reason (ora7_errstr, r->uri, r);
	        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 = getword(r->pool, (void*)&t, ' ');

        if ((user_result != OK) && (!strcmp(w,"user"))) {
	    user_result=AUTH_REQUIRED;
            while(t[0]) {
                w = getword_conf (r->pool,(void*) &t);
                if (!strcmp(user,w)) {
                    user_result= OK;
		    break;
		};
            }
	    if ((sec->auth_ora7_authorative) && ( user_result != OK)) {
           	sprintf(ora7_errstr,"User %s not found (ORA7-Authorative)",user);
		log_reason (ora7_errstr, r->uri, r);
           	note_basic_auth_failure(r);
		return AUTH_REQUIRED;
		};
        }

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

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

	   if ( (sec->auth_ora7_authorative) && (group_result != OK) ) {
           	sprintf(ora7_errstr,"user %s not in right groups (ORA7-Authorative) ",user);
		log_reason (ora7_errstr, r->uri, r);
           	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 ORA7 yielded a correct result. 
     * This really is not needed.
     */
    if (((group_result == AUTH_REQUIRED) || (user_result == AUTH_REQUIRED)) && (sec->auth_ora7_authorative) ) {
        sprintf(ora7_errstr,"ORA7-Authorative: Access denied on %s %s rule(s) ", 
		(group_result == AUTH_REQUIRED) ? "USER" : "", 
		(user_result == AUTH_REQUIRED) ? "GROUP" : ""
		);
	log_reason (ora7_errstr, r->uri, r);
	return AUTH_REQUIRED;
	};

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

    return DECLINED;
}


module ora7_auth_module = {
   STANDARD_MODULE_STUFF,
   NULL,			/* initializer */
   create_ora7_auth_dir_config,	/* dir config creater */
   NULL,			/* dir merger --- default is to override */
   NULL,			/* server config */
   NULL,			/* merge server config */
   ora7_auth_cmds,		/* command table */
   NULL,			/* handlers */
   NULL,			/* filename translation */
   ora7_authenticate_basic_user,/* check_user_id */
   ora7_check_auth,		/* check auth */
   NULL,			/* check access */
   NULL,			/* type_checker */
   NULL,			/* pre-run fixups */
   NULL				/* logger */
};

