/*****************************************************************************/
/*
                               AuthHTL.c

Licensed under the Apache License, Version 2.0 (the License);
you may not use this software except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an AS IS BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

This module provides functions related to the authentication of
case-insensitive usernames via simple lists of names (and optional
case-insensitive passwords and user detail).

See AUTH.C for overall detail on the WASD authorization environment.


VERSION HISTORY
---------------
26-JUN-2012  MGD  bugfix; AuthReadSimpleList() parameter /DIRECTORY= processing
26-AUG-2003  MGD  service directory located authorization databases
12-AUG-2003  MGD  allow the database directory location to be specified using
                  an authorization rule 'param="/directory=device:[directory]"'
27-APR-2002  MGD  use sys$setprv()
04-AUG-2001  MGD  support module WATCHing
05-FEB-2000  MGD  change HTL database type from ".HTL" to ".$HTL"
                  (keep the nomenclature in line with ".$HTA", see AUTHHTA.C)
01-JAN-2000  MGD  no significant modifications for ODS-5 (no NAM block)
28-AUG-1999  MGD  unbundled from AUTH.C for v6.1
*/
/*****************************************************************************/

#ifdef WASD_VMS_V7
#undef _VMS__V6__SOURCE
#define _VMS__V6__SOURCE
#undef __VMS_VER
#define __VMS_VER 70000000
#undef __CRTL_VER
#define __CRTL_VER 70000000
#endif

/* standard C header files */
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

/* VMS related header files */
#include <descrip.h>
#include <rms.h>
#include <rmsdef.h>
#include <ssdef.h>
#include <stsdef.h>

/* application related header files */
#include "wasd.h"

#define WASD_MODULE "AUTHHTL"

#if WATCH_MOD
#define FI_NOLI WASD_MODULE, __LINE__
#else
/* in production let's keep the exact line to ourselves! */
#define FI_NOLI WASD_MODULE, 0
#endif

/********************/
/* external storage */
/********************/

extern unsigned long  SysPrvMask[];

extern char  *AuthConfigHtaDirectory;

extern char  ErrorSanityCheck[];

extern ACCOUNTING_STRUCT  *AccountingPtr;
extern MSG_STRUCT  Msgs;
extern WATCH_STRUCT  Watch;

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

int AuthReadSimpleList
(
REQUEST_STRUCT* rqptr,
char *DatabaseName,
BOOL AuthenticatePassword
)
{
   int  status,
        AuthFileNameLength;
   char  *cptr, *sptr, *zptr,
         *cptrBuffer;
   char  AuthFileName [256],
         Line [1024];
   struct FAB  AuthFileFab;
   struct RAB  AuthFileRab;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_AUTH))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_AUTH,
                 "AuthReadSimpleList() !&Z !&Z !&B",
                 DatabaseName, rqptr->RemoteUser, AuthenticatePassword);

   /* flag that case-less username and password checks were performed */
   rqptr->rqAuth.CaseLess = true;

   /* must supply a user name and database name */
   if (!rqptr->RemoteUser[0] || !DatabaseName[0])
      return (AUTH_DENIED_BY_LOGIN);

   if (rqptr->rqAuth.PathParameterPtr &&
       rqptr->rqAuth.PathParameterPtr[0])
   {
      if ((cptr = strstr (rqptr->rqAuth.PathParameterPtr, "/directory=")) ||
          (cptr = strstr (rqptr->rqAuth.PathParameterPtr, "/DIRECTORY=")))
         cptr += 11;
      else
         cptr = NULL;
   }
   else
      cptr = NULL;

   if (!cptr)
   {
      if (rqptr->rqAuth.DirectoryPtr)
        cptr = rqptr->rqAuth.DirectoryPtr;
      else
      if (rqptr->ConfigDirectory[0])
         cptr = rqptr->ConfigDirectory;
      else
         cptr = AuthConfigHtaDirectory;
   }

   /* just a safeguard against the service directory not being configured */
   if (!*cptr) cptr = AUTH_DIR_NOT_CONFIGURED;

   zptr = (sptr = AuthFileName) + sizeof(AuthFileName)-1;
   while (*cptr && sptr < zptr) *sptr++ = *cptr++;
   for (cptr = DatabaseName; *cptr && sptr < zptr; *sptr++ = *cptr++);
   for (cptr = HTL_FILE_TYPE; *cptr && sptr < zptr; *sptr++ = *cptr++);
   *sptr = '\0';
   AuthFileNameLength = sptr - AuthFileName;

   if (WATCHING (rqptr, WATCH_AUTH))
      WatchThis (WATCHITM(rqptr), WATCH_AUTH, "FILE !AZ", AuthFileName);

   AuthFileFab = cc$rms_fab;
   AuthFileFab.fab$b_fac = FAB$M_GET;
   AuthFileFab.fab$l_fna = AuthFileName;  
   AuthFileFab.fab$b_fns = AuthFileNameLength;
   AuthFileFab.fab$b_shr = FAB$M_SHRGET;

   /* turn on SYSPRV to allow access to authentication database file */
   sys$setprv (1, &SysPrvMask, 0, 0);

   status = sys$open (&AuthFileFab, 0, 0);

   sys$setprv (0, &SysPrvMask, 0, 0);

   /* status from sys$open() */
   if (VMSnok (status))
   {
      rqptr->rqResponse.HttpStatus = 500;
      rqptr->rqResponse.ErrorTextPtr = MsgFor(rqptr,MSG_AUTH_DATABASE);
      rqptr->rqResponse.ErrorOtherTextPtr = DatabaseName;
      ErrorVmsStatus (rqptr, status, FI_LI);
      return (status);
   }

   AuthFileRab = cc$rms_rab;
   AuthFileRab.rab$l_fab = &AuthFileFab;
   AuthFileRab.rab$b_mbf = 2;
   AuthFileRab.rab$l_rop = RAB$M_RAH;

   if (VMSnok (status = sys$connect (&AuthFileRab, 0, 0)))
   {
      sys$close (&AuthFileFab, 0, 0);
      rqptr->rqResponse.HttpStatus = 500;
      rqptr->rqResponse.ErrorTextPtr = MsgFor(rqptr,MSG_AUTH_DATABASE);
      rqptr->rqResponse.ErrorOtherTextPtr = DatabaseName;
      ErrorVmsStatus (rqptr, status, FI_LI);
      return (status);
   }

   /*****************/
   /* locate record */
   /*****************/

   AuthFileRab.rab$l_ubf = Line;
   AuthFileRab.rab$w_usz = sizeof(Line)-1;

   while (VMSok (status = sys$get (&AuthFileRab, 0, 0)))
   {
      AuthFileRab.rab$l_rbf[AuthFileRab.rab$w_rsz] = '\0';
      if (AuthFileRab.rab$w_rsz)
      {
         if (AuthFileRab.rab$l_ubf[AuthFileRab.rab$w_rsz-1] == '\\')
         {
            /* directive is continued on next line */
            AuthFileRab.rab$l_ubf[AuthFileRab.rab$w_rsz-1] = ' ';
            AuthFileRab.rab$l_ubf += AuthFileRab.rab$w_rsz;
            AuthFileRab.rab$w_usz -= AuthFileRab.rab$w_rsz;
            continue;
         }
      }
      for (cptr = Line; *cptr && ISLWS(*cptr); cptr++)
      if (*cptr == '#' || *cptr == '!') continue;

      sptr = rqptr->RemoteUser;
      while (*cptr && !ISLWS(*cptr) && *cptr != '=' && *sptr &&
             to_lower(*cptr) == to_lower(*sptr))
         { cptr++; sptr++; }
      if ((*cptr && !ISLWS(*cptr) && *cptr != '=') || *sptr) continue;
      break;
   }

   sys$close (&AuthFileFab);

   if (status == RMS$_EOF)
   {
      /* user record not found */
      if (WATCHING (rqptr, WATCH_AUTH))
         WatchThis (WATCHITM(rqptr), WATCH_AUTH,
                    "FAIL !AZ!AZ username", DatabaseName,
                    AuthSourceString (DatabaseName, AUTH_SOURCE_LIST));

      if (AuthenticatePassword)
         return (AUTH_DENIED_BY_LOGIN);
      else
         return (AUTH_DENIED_BY_GROUP);
   }

   /* if just checking if the user name is an entry in the list file */
   if (!AuthenticatePassword) return (SS$_NORMAL);

   sptr = cptr;
   if (*sptr == '=') while (*sptr && !ISLWS(*sptr)) sptr++;
   while (*sptr && ISLWS(*sptr)) sptr++;
   if (*sptr)
   {
      cptrBuffer = cptr;
      for (cptr = sptr; *cptr; cptr++);
      if (cptr > sptr) cptr--;
      while (cptr > sptr && ISLWS(*cptr)) cptr--;
      if (!ISLWS(*cptr)) cptr++;
      *cptr = '\0';
      rqptr->rqAuth.UserDetailsLength = cptr - sptr;
      rqptr->rqAuth.UserDetailsPtr = cptr =
         VmGetHeap (rqptr, rqptr->rqAuth.UserDetailsLength+1);
      strzcpy (cptr, sptr, rqptr->rqAuth.UserDetailsLength+1);
      cptr = cptrBuffer;
   }

   /* if just checking if the user name is an entry in the list file */
   if (!AuthenticatePassword) return (status);

   /* simple, clear-text password comparison */
   if (*cptr != '=')
   {
      if (WATCHING (rqptr, WATCH_AUTH))
         WatchThis (WATCHITM(rqptr), WATCH_AUTH,
                    "FAIL !AZ!AZ no password", DatabaseName,
                    AuthSourceString (DatabaseName, AUTH_SOURCE_LIST));
      return (AUTH_DENIED_BY_LOGIN);
   }
   while (*cptr && *cptr == '=') cptr++;
   sptr = rqptr->RemoteUserPassword;
   while (*cptr && !ISLWS(*cptr) && *sptr && to_lower(*cptr) == to_lower(*sptr))
      { cptr++; sptr++; }
   if ((*cptr && !ISLWS(*cptr)) || *sptr)
   {
      /* passwords do not match */
      if (WATCHING (rqptr, WATCH_AUTH))
         WatchThis (WATCHITM(rqptr), WATCH_AUTH,
                    "FAIL !AZ!AZ password", DatabaseName,
                    AuthSourceString (DatabaseName, AUTH_SOURCE_LIST));
      return (AUTH_DENIED_BY_LOGIN);
   }

   /* passwords match */
   return (SS$_NORMAL);
}

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

