OpenVMS Source Code Demos
mod_auth_openvms
/* ====================================================================
* 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/>.
*
*/
/*
** FACILITY:
** Compaq Secure Web Server for OpenVMS Alpha
**
** ABSTRACT:
** This module provides a basic routine for authenticating a user based
** on a username and password contained in the SYSUAF records.
**
** NOTE: This module is based on MOD_AUTH_NIS.C. Credit goes to
** Dirk.vanGulik@jrc.it, the author of that module.
**
** RESTRICTION:
** There is no support for secondary passwords or external authentication
** at this time. (The MOD_AUTH_xxx services only provide one password.)
**
** CREATION DATE: November 29, 2000
**
** AUTHORS:
** Kevin D. O'Kelley
**
** MODIFICATION HISTORY:
**
** 000 KDO/29-Nov-2000
** Created.
**
** 001 RJB/02-Feb-2001
** vms_auth_princ_sysuaf syntax has changed to require
** the caller to supply the client's host-name string.
**
** 002 KDO/02-Nov-2001
** Add authoritative/non-authoritative support. Add support
** for case-insensitive username comparison. Add support for
** the "require group" directive.
**
** 003 MPD/15-Jan-2003
** Added Apache 2.0 API changes
**
*/
/*
** Include files
*/
#include <ctype.h>
#include <types.h>
#include <limits.h>
#include <string.h>
#include <ssdef.h>
#include <kgbdef.h>
#include <lgidef.h>
#include <stsdef.h>
#include <descrip.h>
#include <starlet.h>
#include <builtins.h>
#ifdef SHADOW
#undef SHADOW
#endif
#ifdef MULTITHREADING
#undef MULTITHREADING
#endif
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"
#include "http_request.h"
#include "apr_strings.h"
#include "protshr.h"
/*
** Conditional asssembly macros
*/
#ifndef __VMS_AUTH_SYSUAF_DEBUG
#define __VMS_AUTH_SYSUAF_DEBUG 0 /* 1= debugging */
#endif
#if __VMS_AUTH_SYSUAF_DEBUG
#define DEBUG_PRINTF(ARGS) printf ARGS
#define DEBUG_OFF (0)
#define DEBUG_ON (1)
#include <stdio.h>
#else
#define DEBUG_PRINTF(ARGS)
#define DEBUG_OFF (1)
#define DEBUG_ON (0)
#endif
/*
** Definitions
*/
#ifndef INTERNAL
#define INTERNAL static
#endif
#ifndef NULL
#define NULL (void *) 0
#endif
#ifndef alloca
#define alloca __ALLOCA
#endif
#define BAD_STATUS(I) (((I) & STS$M_SUCCESS) == 0)
#define GOOD_STATUS(I) (((I) & STS$M_SUCCESS) == 1)
/*
** Data structures
*/
typedef unsigned int VMS_STATUS;
typedef struct dsc$descriptor_s DSC_S;
typedef struct /* context block */
{
int fUserEnable; /* 0= false (this module abstains) */
int fGroupEnable; /* 0= false (no group processing) */
int fAuthoritative; /* 0= false (DECLINE, not REJECT) */
}
CTXBLK;
typedef struct /* temp structure for identifiers */
{
int iUsed; /* number of identifiers */
int iAllocated; /* number of slots allocated */
int aiIds[10]; /* initial allocation of space */
}
IDENT_LIST;
/*
** Function prototypes
*/
INTERNAL int save_identifier(const char *pcszName, IDENT_LIST **ppident);
extern module AP_MODULE_DECLARE_DATA auth_openvms_module;
void *create_auth_openvms_context (apr_pool_t *p, char *d)
{
CTXBLK *sec = (CTXBLK *) apr_pcalloc (p, sizeof(CTXBLK));
sec->fUserEnable = sec->fGroupEnable = -1;
return (void *) sec;
}
/*
** Directives handled by this module
**
** NOTE: "AuthUserOpenVMS" is the old syntax. We keep it for backwards
** compatability. However, by default, this module is enabled, although
** not authoritative.
*/
command_rec auth_openvms_cmds[] =
{
{ "AuthOpenVMSAuthoritative", ap_set_flag_slot,
(void *) APR_XtOffsetOf(CTXBLK,fAuthoritative), OR_AUTHCFG, FLAG,
"Set to 'no' to allow access control to be passed along "
"to lower modules if the userID is not known to this module" },
{ "AuthOpenVMSGroup", ap_set_flag_slot,
(void *) APR_XtOffsetOf(CTXBLK,fGroupEnable), OR_AUTHCFG, FLAG,
"OpenVMS authorization on or off" },
{ "AuthOpenVMSUser", ap_set_flag_slot,
(void *) APR_XtOffsetOf(CTXBLK,fUserEnable), OR_AUTHCFG, FLAG,
"OpenVMS authentication/authorization on or off" },
{ "AuthUserOpenVMS", ap_set_flag_slot,
(void *) APR_XtOffsetOf(CTXBLK,fUserEnable), OR_AUTHCFG, FLAG,
"OBSOLETE: Please switch to AuthOpenVMSUser" },
{ NULL }
};
/*
** These functions return 0 if client is OK, and proper error status
** if not... either HTTP_UNAUTHORIZED, if we made a check, and it failed, or
** SERVER_ERROR, if things are so totally confused that we couldn't
** figure out how to tell if the client is authorized or not.
**
** If they return DECLINED, and all other modules also decline, that's
** treated by the server core as a configuration error, logged and
** reported as such.
*/
/*
** a u t h e n t i c a t e _ o p e n v m s
**
** Basic HTTP authentication.
** Return with OK for success, DECLINED for an unknown user with the
** AuthOpenVMSAuthoritative directive disabled and HTTP_UNAUTHORIZED for a
** failure.
*/
INTERNAL int authenticate_openvms (request_rec *r)
{
unsigned int st;
CTXBLK *sec = (CTXBLK *)
ap_get_module_config (r->per_dir_config, &auth_openvms_module);
conn_rec *c = r->connection;
const char *pcszPassword;
const char *pcszHostName;
char errstr[MAX_STRING_LEN];
int res;
if ((res = ap_get_basic_auth_pw (r, &pcszPassword)))
return res;
if (!sec->fUserEnable)
return DECLINED;
pcszHostName = ap_get_remote_host ( r->connection, r->per_dir_config,
REMOTE_NOLOOKUP, 0 );
st = apache$$auth_princ_sysuaf (
(const char *) r->user, strlen(r->user),
pcszPassword, strlen(pcszPassword),
pcszHostName, strlen(pcszHostName) );
if (st == LGI$_NOSUCHUSER)
{
if (!sec->fAuthoritative)
return DECLINED;
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"OpenVMS user %s not found", r->user);
ap_note_basic_auth_failure(r);
return HTTP_UNAUTHORIZED;
}
if (BAD_STATUS(st))
{
apr_snprintf(errstr, sizeof(errstr),
"OpenVMS authorization failure %s status=0x%08x",
r->user, st);
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
"access to %s failed for %s, reason: %s",
r->uri,
ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME, 0),
errstr);
ap_note_basic_auth_failure (r);
return HTTP_UNAUTHORIZED;
}
return OK;
}
/*
** c h e c k _ o p e n v m s _ a c c e s s
**
** Parse the "require user" and "require group" directives to determine
** whether or not the user is allowed access.
** Return with OK for success, DECLINED for an unknown user with the
** AuthOpenVMSAuthoritative directive disabled and HTTP_UNAUTHORIZED for a
** failure.
*/
INTERNAL int check_openvms_access (request_rec *r)
{
unsigned int st;
int fAuthorized = 0;
int iRequireGroup = 0;
CTXBLK *sec = (CTXBLK *)
ap_get_module_config (r->per_dir_config, &auth_openvms_module);
conn_rec *c = r->connection;
int m = r->method_number;
register int x;
const char *t, *w;
const apr_array_header_t *reqs_arr = ap_requires(r);
require_line *reqs;
IDENT_LIST *pident = NULL;
/*
** If we're not enabled then decline.
*/
if ((!sec->fGroupEnable) || (!sec->fUserEnable))
return DECLINED;
/*
** If there are no require directives then any user will do: decline,
** same as MOD_AUTH_DB does.
*/
if (!reqs_arr)
{
DEBUG_PRINTF(("(check_openvms_access) No require directives\n"));
return DECLINED;
}
reqs = (require_line *) reqs_arr->elts;
/*
** Parse each require directive.
**
** NOTE: We intentionally parse and handle the "require user" directive
** to make the string comparison case-insensitive.
*/
for (x = 0; x < reqs_arr->nelts; x++)
{
if (!(reqs[x].method_mask & (1 << m)))
continue;
t = reqs[x].requirement;
w = ap_getword_white(r->pool, &t);
if (!strcasecmp(w, "valid-user"))
{
DEBUG_PRINTF(("(check_openvms_access) valid-user\n"));
fAuthorized++;
break;
}
if (!strcasecmp(w, "user"))
{
while (t[0])
{
w = ap_getword_conf(r->pool, &t);
DEBUG_PRINTF(("(check_openvms_access) user \"%s\"\n", w));
if (!strcasecmp(r->user, w))
{
fAuthorized++;
break;
}
}
if (fAuthorized > 0)
break;
}
else if (!strcasecmp(w, "group"))
{
while (t[0])
{
w = ap_getword_conf(r->pool, &t);
DEBUG_PRINTF(("(check_openvms_access) group \"%s\"\n", w));
if (save_identifier(w, &pident))
{
if (pident != NULL)
free(pident);
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"internal error: cannot save groups, "
"errno=%d (user %s)", errno, r->user);
return DECLINED;
}
iRequireGroup++;
}
}
else if (sec->fAuthoritative)
{
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"access to %s failed, reason: unknown require directive:"
"\"%s\"", r->uri, reqs[x].requirement);
}
}
/*
** All of the require directives have been parsed. If we didn't find
** a "require valid-user" or a matching "require user" directive but we
** did find one or more "require group" directives, then see if the
** user is a member of the specified group UICs or it the account has
** any of the specified identifiers granted to it. The tests for the
** validity of the account will be done at the same time.
**
** Otherwise, we still need to test the validity of the account, whether
** the access is allowed or not.
*/
if ((fAuthorized == 0) && (pident != NULL))
{
st = apache$$check_rights_list((const char *) r->user, strlen(r->user),
(const unsigned int *) &pident->aiIds[0], pident->iUsed);
if (GOOD_STATUS(st))
{
DEBUG_PRINTF(("(check_openvms_access) "
"apache$$check_rights_list(%s,%d) OK\n",
r->user, pident->iUsed));
free(pident);
return OK;
}
DEBUG_PRINTF(("(check_openvms_access) "
"apache$$check_rights_list(%s,%d) status=0x%x\n",
r->user, pident->iUsed, st));
free(pident);
}
else
{
if (pident != NULL)
free(pident);
st = apache$$check_rights_list((const char *) r->user, strlen(r->user),
(const unsigned int *) NULL, 0);
if (GOOD_STATUS(st))
{
DEBUG_PRINTF(("(check_openvms_access) "
"apache$$check_rights_list(%s) OK\n",
r->user, ((fAuthorized > 0) ? "" : " (not authorized)")));
if (fAuthorized > 0)
return OK;
if (iRequireGroup == 0)
return DECLINED; /* no require group directives */
st = SS$_NOCALLPRIV; /* some found but none were valid */
}
else
DEBUG_PRINTF(("(check_openvms_access) "
"apache$$check_rights_list(%s) status=0x%x\n", r->user, st));
}
/*
** All is lost: this user is not allowed access.
*/
if (st == LGI$_NOSUCHUSER)
{
DEBUG_PRINTF(("(check_openvms_access) user %s not found\n", r->user));
if (!sec->fAuthoritative)
return DECLINED;
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"access to %s failed, reason: OpenVMS user %s not found",
r->uri, r->user);
ap_note_basic_auth_failure(r);
return HTTP_UNAUTHORIZED;
}
if (SS$_NOCALLPRIV)
{
DEBUG_PRINTF(("(check_openvms_access) user %s not allowed\n", r->user));
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"access to %s failed, reason: user %s is not allowed",
r->uri, r->user);
ap_note_basic_auth_failure(r);
return HTTP_UNAUTHORIZED;
}
DEBUG_PRINTF(("(check_openvms_access) unexpected error 0x%x (user %s)\n",
st, r->user));
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"access to %s failed, reason: unexpected error 0x%x (user %s)",
r->uri, st, r->user);
ap_note_basic_auth_failure(r);
return HTTP_UNAUTHORIZED;
}
/*
** s a v e _ i d e n t i f i e r
**
** Add an identifier to the internal list. If necesssary, expand the list.
** Return with zero for success or minus one for failure. However, if the
** "require group" value is not an identifier, ignore it (not an error).
**
** NOTE: For simplicity, we use the realloc() function to expand an existing
** array. However, this function is not available to the low level pool
** allocation routines. Therefore, we must ensure that the structure is
** deallocated in all cases.
*/
INTERNAL int save_identifier(const char *pcszName, IDENT_LIST **ppident)
{
int i;
VMS_STATUS st;
unsigned int uiId;
unsigned int uiAttrib;
IDENT_LIST *pidentNew;
DSC_S dscsName;
/*
** Convert the name to an identifier.
*/
dscsName.dsc$b_dtype = DSC$K_DTYPE_T;
dscsName.dsc$b_class = DSC$K_CLASS_S;
dscsName.dsc$a_pointer = (char *) pcszName;
dscsName.dsc$w_length = strlen(dscsName.dsc$a_pointer);
if (dscsName.dsc$w_length == 0)
return(0);
if (dscsName.dsc$w_length > CHAR_MAX)
{
errno = EINVAL;
return(-1);
}
st = sys$asctoid(&dscsName, &uiId, &uiAttrib);
if (BAD_STATUS(st))
{
DEBUG_PRINTF(("(save_identifier) sys$asctoid(\"%.*s\") status=0x%x\n",
dscsName.dsc$w_length, dscsName.dsc$a_pointer, st));
return(0);
}
/*
** Only keep plain identifiers (i.e. no attributes), resource identifiers,
** and group ids. User names are also accepted here, but they are not
** relevant: user names are immediately handled by a string comparison.
*/
if (uiAttrib != (uiAttrib & (KGB$M_RESOURCE | KGB$M_DYNAMIC)))
return(0);
DEBUG_PRINTF(("(save_identifier) \"%.*s\" = 0x%08x (attrib=0x%08x)\n",
dscsName.dsc$w_length, dscsName.dsc$a_pointer, uiId, uiAttrib));
/*
** Save the value in the IDENT_LIST structure.
*/
if ((*ppident) == NULL)
{
(*ppident) = malloc(sizeof(**ppident));
if ((*ppident) == NULL)
return(-1);
(*ppident)->iUsed = 0;
(*ppident)->iAllocated = sizeof((*ppident)->aiIds) /
sizeof((*ppident)->aiIds[0]);
}
else
{
if ((*ppident)->iUsed >= (*ppident)->iAllocated)
{
pidentNew = (IDENT_LIST *) realloc((*ppident), sizeof(**ppident) +
((*ppident)->iAllocated * sizeof((*ppident)->aiIds[0])));
if (pidentNew == NULL)
return(-1);
(*ppident) = pidentNew;
(*ppident)->iAllocated += sizeof((*ppident)->aiIds) /
sizeof((*ppident)->aiIds[0]);
}
}
(*ppident)->aiIds[(*ppident)->iUsed++] = uiId;
return(0);
}
static void register_hooks(apr_pool_t *p)
{
ap_hook_check_user_id(authenticate_openvms,NULL,NULL,APR_HOOK_MIDDLE);
ap_hook_auth_checker(check_openvms_access,NULL,NULL,APR_HOOK_MIDDLE);
}
module AP_MODULE_DECLARE_DATA auth_openvms_module =
{
STANDARD20_MODULE_STUFF,
create_auth_openvms_context,/* dir config creater */
NULL, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
auth_openvms_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};