/*
 * Copyright (c) 1997.  Author: Yuan John Jiang
 */


/*
 * auth_mysql.c: NSAPI function to authenticate (BASIC) users
 * against MYSQL tables..
 * V 0.8Alpha	MySQL socket/connection is opened/closed for
 *		each Request.  Maybe enhanced to use a pool of
 *		connections later on.
 */

/* ---------------------------- auth_mysql usage ------------------------ 
 *
 * At the beginning of obj.conf:
 *Init fn=load-modules shlib=auth_mysql.so funcs=auth_mysql
 *
 * Inside an object in obj.conf:
 *AuthTrans fn=auth_mysql auth-type="basic" mysql_db=MYDB mysql_tb="user_records" \
 *	mysql_host="localhost" [mysql_user=ME] [mysql_pw=PASSWORD]
 *
 *PathCheck realm="mysql" fn="require-auth" [auth-users=(USER1|USER2...)]
 *		auth-type="basic" 
 *
 *---------------------------- User table -------------------------------
 *  You may modify this section according to your table
 *
create table USERS (
 	  user  char(32),
	  password  char(32),
	  usergroup   char(32), 
        primary key(user)
	  ) 
 */

#include	"mysql.h"
#define	GET_USER "select password, usergroup from %s where user='%s'"
/*------------------------------------------------------------------------*/
#include	<crypt.h>

/* ------------------------------------------------------------------------
 * 
 * The following three are standard headers for SAFs.  They're used to
 * get the data structures and prototypes needed to declare and use SAFs.
 */

#define	net_read	ns_net_read
#define	net_write	ns_net_write
#include "base/pblock.h"
#include "base/session.h"
#include "frame/req.h"
#include "frame/protocol.h"
#include "frame/log.h"

#ifdef XP_WIN32
#define NSAPI_PUBLIC __declspec(dllexport)
#else /* !XP_WIN32 */
#define NSAPI_PUBLIC
#endif /* !XP_WIN32 */

/*************************************************************/


NSAPI_PUBLIC int auth_mysql(pblock *param, Session *sn, Request *rq)
{
int ret=REQ_NOACTION;

	/* Parameters given by obj.conf */
char *group = NULL;
char *mysql_db = pblock_findval("mysql_db", param);
char *mysql_tb = pblock_findval("mysql_tb", param);
char *mysql_host = pblock_findval("mysql_host", param);
char *mysql_user = pblock_findval("mysql_user", param);
char *mysql_pw = pblock_findval("mysql_pw", param);

	/* Authentication input */
char *auth = pblock_findval("authorization", rq->headers);

	/* working variables */
char *auth_dec=NULL,*user,*pw,*pw_crypt,*pw_r,*t;
int uudecode(char *, char * );

	/* MYSQL variables */
MYSQL db_sock;
MYSQL_RES *db_res;
MYSQL_ROW db_row;
int	num_fields;
char	query[256];

	/***** check configuration *****/
/* get user:pw */
if(auth!=NULL)
   {
   if( (t=strstr(auth,"Basic "))!=NULL )
	{
	if(t!=auth)
	   auth=t;	/* skip spaces */

   /* auth_dec holds the decoded authentication info */
	auth_dec = (char *) MALLOC( sizeof(char *)*sizeof(auth) );
	uudecode(auth+6,auth_dec);

	if( (t=strchr(auth_dec,':'))!=NULL )
	   {
	   t[0]='\0';
	   pw=t+1;
	   }
	else pw=auth_dec+strlen(auth_dec);
	user=auth_dec;
	ret=REQ_PROCEED;
	}
   }
else return ret;

if( (!mysql_db)||(strlen(mysql_db)==0) )
  {
  log_error(LOG_MISCONFIG, "auth_mysql", sn, rq, 
  "Configuration: fn=auth_pw mysql_db=DBNAME mysql_tb=TABLENAME mysql_host=HOST");
   /* When we abort, the default status code is 500 Server Error */
  return REQ_ABORTED;
  }

/* Check against our passwd */
if( (ret==REQ_PROCEED)&&(mysql_tb!=NULL) )
  {
  ret=REQ_NOACTION; /* set default */

  if( mysql_connect(&db_sock, mysql_host, mysql_user, mysql_pw) )
    {
    if( !mysql_select_db(&db_sock, mysql_db) )
      {
      if(strlen(mysql_tb)+strlen(user) < 64 )
	sprintf(query, GET_USER, mysql_tb, user );
      else
	query[0]='\0';

      if( (!mysql_query(&db_sock, query))
        &&(db_res=mysql_store_result(&db_sock))
	&&(num_fields=mysql_num_fields(db_res))
	&&(db_row=mysql_fetch_row(db_res)) )
	{
	pw_r=db_row[0];

	pw_crypt = crypt(pw, pw_r);
	if( strcmp(pw_crypt, pw_r) == 0 )
	  {
	  /* authenticated */
	  param_free(pblock_remove("auth-type", rq->vars));
	  pblock_nvinsert("auth-type", "basic", rq->vars);
	  param_free(pblock_remove("auth-user", rq->vars));
	  pblock_nvinsert("auth-user", user, rq->vars);

	  if(num_fields>1)
	    {
	    param_free(pblock_remove("auth-group", rq->vars));
	    pblock_nvinsert("auth-group", db_row[1], rq->vars);
	    }

	  ret=REQ_PROCEED;
	  }	/* pw correct	*/
	}	/* query ok	*/
      else fprintf(stderr, "query error: %s\n", mysql_error(&db_sock));
      mysql_free_result(db_res);
      }		/* DB connect ok */
    else fprintf(stderr, "query error: %s\n", mysql_error(&db_sock));
    }
  else fprintf(stderr, "query error: %s\n", mysql_error(&db_sock));
  mysql_close(&db_sock);
  }	/* input ok	*/

return ret;
}

/* From Apache source code */
/* $Id: auth_mysql.c,v 1.1 1997/05/30 22:00:24 yjj Exp yjj $
 * ====================================================================
 * 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/>.
 *
 */
/* aaaack but it's fast and const should make it shared text page. */
static const int pr2six[256]={
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,62,64,64,64,63,52,53,54,55,56,57,58,59,60,61,64,64,
64,64,64,64,64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27,28,
29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,
49,50,51,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64
};

/* modified Apache code */
int uudecode(char *bufcoded,char *bufplain)
/* bufout is created.  Remember to free it! */
{
int nbytesdecoded;
unsigned char *bufin;
unsigned char *bufout;
int nprbytes;

/* Strip leading whitespace. */ 
while(*bufcoded==' ' || *bufcoded == '\t') bufcoded++;

/* Figure out how many characters are in the input buffer.
 * Allocate this many from the per-transaction pool for the result.
 */
bufin = (unsigned char *)bufcoded;
while(pr2six[*(bufin++)] <= 63) ;
nprbytes = (char *)bufin - bufcoded - 1;
nbytesdecoded = ((nprbytes+3)/4) * 3;

bufout = (unsigned char *)bufplain;

bufin = (unsigned char *)bufcoded;

while (nprbytes > 0)
   {
   *(bufout++) =
	(unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
   *(bufout++) =
	(unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
   *(bufout++) =
	(unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
   bufin += 4;
   nprbytes -= 4;
   }

if(nprbytes & 03)
   {
   if(pr2six[bufin[-2]] > 63)
	nbytesdecoded -= 2;
   else
   nbytesdecoded -= 1;
   }
bufplain[nbytesdecoded] = '\0';

return nbytesdecoded;
}
