OpenVMS Source Code Demos
mod_auth_vms_ext
//================================================================================================================================
// title : mod_auth_vms_ext.c
// author : Neil Rieck (Waterloo, Ontario, Canada)
// created: 2015-03-03
// target : CSWS-2.0 (a.k.a. Apache httpd-2.0.63 on OpenVMS)
// notes : 1) this program is based upon "mod_auth_openvms.c" from HP
// : 2) I want to use "application authentication" rather than "SYSUAF authentication" (100% of our current >1k users are
// web-based so it was easier for us to use store usernames and VMS-created "password hashes" in an application
// database than create VMS accounts for each one; this will allow us to enable self-serve account generation and
// maintenance as is done on Amazon.com and eBay.com; this also allows us to have account names longer than 14
// characters -AND- email addresses)
// ver who when what
// --- --- ------ ----------------------------------------------------------------------------------------------------------------
// 100 NSR 150303 1. original effort (MOD_AUTH_VMS_EXTERNAL.C); compiles + links but is not complete
// 150307 2. the saga continues
// 150311 3. more work after some technical interruptions elsewhere
// 150313 4. split the module into two pieces (cookie only vs. user only)
// 5. cleaned up the debug macros
// 150314 6. renamed to MOD_AUTH_VMS_EXT_100.C (next version will be MOD_AUTH_VMS_RMS.C)
// 101 NSR 150316 1. renamed DCL symbols to something more sensible (now all have a prefix of "VMSEXT_")
// 150317 2. now also grab the local_host_name (required to create DOMAIN cookies)
// 102 NSR 150318 1. merged two modules back into one (so we only need to register one hook)
// 150319 2. final finished of the option GroupList testing
// 3. now do my own base64 extraction so we don't need to depend upon mod_auth(_basic)
// 103 NSR 150505 1. the saga continues (back from another distraction)
// NSR 150506 2. added code to send back a cookie
// NSR 150507 3. reworked group processing (what was I thinking?)
// 4. removed my developmental "goto" statements (don't want to be crucified by other c programmers)
// 104 NSR 150519 0. the saga continues (back from another distraction)
// 1. added code to pickup RealmName (used in password realm)
// 105 NSR 150609 1. started adding code to read cookies from our RMS-based session cache. This would eliminate the need to spawn
// multiple times for each page load; expecially important when Autoindex is enabled on a directory with no
// predefined index page (see http://httpd.apache.org/docs/2.4/mod/mod_authn_socache.html to see what I mean)
// Note: this session cache is written using RMS which is an ISAM technology only available on VMS and OpenVMS
// NSR 150615 2. bug fix in groupData extraction logic (RMS-cache) bf_105.2
// NSR 150616 3. bug fix in cache-hit test bf_105.3
// NSR 151210 4. mtce tweak to the VMSIFY macro
//================================================================================================================================
// Docs:
// 1) build this program then copy the execuatable to sys$common:[modules]
// be sure to check file ownership and protection bits
// consider using "$SET SECURITY/ACL" to modify/delete access control list params
// add the next line to file: apache$common:[conf]httpd.conf
// LoadModule auth_vms_ext_module modules/mod_auth_vms_ext.exe
// restart CSWS to load the new module
// 2) sample .htaccess
// #---------------------------------------------------------
// # title : apache$common:[neil_private].htaccess
// # author : Neil Rieck
// # created: 2015-03-04
// #---------------------------------------------------------
// AuthType Basic
// AuthVmsExtUser On off means plugin is disabled
// AuthVmsExtAuthoritative On off means exit with DECLINED rather than HTTP_UNAUTHORIZED
// AuthVmsExtCookie ICSIS_SESSION optional test (no value means disabled)
// AuthVmsExtGroupList ATS,BRT,WST,POW, optional test (no value means disabled)
// AuthVmsExtProgram "r CSMIS$EXE:WCSM_SESSION_CHECK.EXE" no value means disabled
// AuthVmsExtLogfile "CSMIS$LOG:WCSM_SESSION_CHECK.LOG" no value means disabled
// AuthVmsExtRealmName "OpenVMS External Authentication" optional, displayed during the password dialog
// AuthVmsExtSessionCache "csmis$dat:icsis_session_5.dat" optional, RMS-based ISAM file (no value means disabled)
// require valid-user
// #---------------------------------------------------------
//================================================================================================================================
#define RMS_SESSION_CACHE 1 // enable support for RMS-based session cache
//
// Include files (common c stuff)
//
#include <ctype.h>
#include <types.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h> // need this for getenv
//
// Include files (VMS and OpenVMS only)
//
#ifdef __VMS // if VMS or OpenVMS
#define __NEW_STARLET 1 // enable new (strict) starlet (OpenVMS Alpha 7.0 and above)
#include <ssdef.h> //
#include <kgbdef.h> //
#include <lgidef.h> //
#include <stsdef.h> //
#include <descrip.h> //
#include <starlet.h> //
#include <builtins.h> //
#include <lib$routines.h> // need this for lib$spawn
#include <rms.h> // OpenVMS Record Management Services
#else
#define RMS_SESSION_CACHE 0 // no VMS means no RMS
#endif //
//
#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"
//
// Definitions
//
#ifndef INTERNAL
#define INTERNAL static
#endif
#ifndef NULL
#define NULL (void *) 0
#endif
#ifndef alloca
#define alloca __ALLOCA
#endif
//
// VMSIFY1
// a macro for use in the VMS world (VMS strings employ this structure)
// notes: 1. this macro can be used to create VMS strings in c space
// 2. the $DESCRIPTOR macro does something similar employing sizeof-1
// 3. this macro combines two operations
// 4. use str$copy_dx() to copy string data up to the calling program
//
#define VMSIFY(a,b) { \
a.dsc$b_dtype = DSC$K_DTYPE_T; \
a.dsc$b_class = DSC$K_CLASS_S; \
a.dsc$w_length = strlen(b); \
a.dsc$a_pointer = (char *) malloc(strlen(b)); \
strncpy(a.dsc$a_pointer,b,a.dsc$w_length); \
}
//
#define DEBUG 1
#if DEBUG
//dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd Neil's debug code
//
// this block is for development purposes only
//
char spy_stamp[50]; // buffer for time-stamp
void build_stamp() {
#include <time.h>
//----------------------------------------------------------------------
struct timeb timebuffer; // for ftime()
struct tm *time_fields; // for localtime()
char millisecs[5]; //
char my_date_time[30]; //
//----------------------------------------------------------------------
ftime( &timebuffer ); // record current system time
sprintf(millisecs, "%03hu", timebuffer.millitm); // extract milliseconds as three chars
time_fields = localtime( &timebuffer.time ); // breakout other time fields
strftime( my_date_time, // ccyymmdd.hhmmss
sizeof(my_date_time), //
"%Y%m%d.%H%M%S", //
time_fields ); //
sprintf( spy_stamp, // ccyymmdd.hhmmss.xxx
"%s%s%s", //
my_date_time, //
".", //
millisecs); // xxx
}
char trc_buf[MAX_STRING_LEN]; //
FILE *trc_file = NULL; //
void TRC1(char *msg) { // trace (one param)
build_stamp(); //
trc_file = fopen("APACHE$COMMON:[000000]aaa_mod_auth_vms_ext.trc", "a"); // open the trace file
if (trc_file != NULL) { //
fprintf(trc_file, "%s %s\n",spy_stamp,msg); //
fclose (trc_file); //
} //
} //
// trace (two params)
#define TRC2(a,b) { \
sprintf(trc_buf,a,b); \
TRC1 (trc_buf); \
} //
//dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd end of debug
#else
#define TRC2(a,b)
#define TRC1(a)
#endif
//
// Data structures
//
typedef unsigned int VMS_STATUS;
//
typedef struct dsc$descriptor_s DSC_S; // all VMS strings must be passed by descriptor
//
typedef struct { // context block
int fUserEnable; // 0 = false (this module abstains)
int fAuthoritative; // 0 = false (DECLINED, not REJECT)
char *fCookieName; // whatever optional, leave blank to disable
char *fGroupList; // GRP1[,GRP2,GRP3,...] optional, leave blank to disable
char *fLogfile; // logfile (for program development) optional, leave blank to disable
char *fProgram; // program required
char *fRealmName; // authorization name (realm) optional (to override default)
char *fSessionCache; // session cache optional, leave blank to disable
}
CTXBLK;
//
// Function prototypes
//
extern module AP_MODULE_DECLARE_DATA auth_vms_ext_module;
#if RMS_SESSION_CACHE == 1 //
long session_cache_lookup(char*, char*, char*); //
#endif //
//
void *create_auth_vms_ext_cntxt (apr_pool_t *p, char *d)
{
CTXBLK *sec = (CTXBLK *) apr_pcalloc (p, sizeof(CTXBLK));
sec->fUserEnable = 0;
sec->fAuthoritative = 0;
sec->fCookieName = NULL;
sec->fGroupList = NULL;
sec->fLogfile = NULL;
sec->fProgram = NULL;
sec->fRealmName = NULL;
sec->fSessionCache = NULL;
return (void *) sec;
}
const char* my_first_cmd_func(cmd_parms* cmd, void* cfg, const char* arg);
//
// Directives handled by this module
//
command_rec auth_vms_ext_cmds[] =
{
{ "AuthVmsExtAuthoritative",
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" },
{ "AuthVmsExtUser",
ap_set_flag_slot,
(void *) APR_XtOffsetOf(CTXBLK,fUserEnable),
OR_AUTHCFG,
FLAG,
"OpenVMS user authentication/authorization on/off" },
{ "AuthVmsExtGroupList",
ap_set_string_slot,
(void*)APR_OFFSETOF(CTXBLK,fGroupList),
OR_AUTHCFG,
TAKE1,
"comma-delimited list of authorized groups (blank=disabled)" },
{ "AuthVmsExtCookie",
ap_set_string_slot,
(void*)APR_OFFSETOF(CTXBLK,fCookieName),
OR_AUTHCFG,
TAKE1,
"desired cookie (blank=disabled)" },
{ "AuthVmsExtProgram",
ap_set_string_slot,
(void*)APR_OFFSETOF(CTXBLK,fProgram),
OR_AUTHCFG,
TAKE1,
"desired program (eg. \"r path:file.ext\", blank=disabled)" },
{ "AuthVmsExtLogfile",
ap_set_string_slot,
(void*)APR_OFFSETOF(CTXBLK,fLogfile),
OR_AUTHCFG,
TAKE1,
"debug logfile (eg. \"path:file.log\", blank=disabled)" },
{ "AuthVmsExtRealmName",
ap_set_string_slot,
(void*)APR_OFFSETOF(CTXBLK,fRealmName),
OR_AUTHCFG,
TAKE1,
"realm name (eg. \"OpenVMS External Authentication\")" },
{ "AuthVmsExtSessionCache",
ap_set_string_slot,
(void*)APR_OFFSETOF(CTXBLK,fSessionCache),
OR_AUTHCFG,
TAKE1,
"session cache filename (eg. \"path:session.dat\", blank=disabled)" },
{ NULL }
};
//=====================================================================================================================
// a u t h e n t i c a t e _ v m s _ e x t
//
// General Overview:
//
// 1) On our system, users can come here with a valid session cookie (set by another app) but no Authorization string.
// In this case we do not want MOD_AUTH(_BASIC) to prompt for authorization credentials before testing the cookie
// which means the hook for "authenticate_vms_ext" must fire before MOD_AUTH(_BASIC)
// 2) On our system, users might come here with nothing (no cookie or Authorization string) so we will prompt for a
// username + password then create the session cookie
// 3) In order to keep our transaction overhead as low as possible, I do things here which might have been done by a
// combination of the core routines and MOD_AUTH(_BASIC)
// 4) Authorative Off means that authentication failures return DECLINED so other other modules can authenticate
// 5) Authorative On means we never DECLINE to another modules
// 6) Successfull authentication is not enough.
// If a GroupList has been defined then the external authenticator must return a group string.
// If the group string is in the list then we return OK else 403 (HTTP_FORBIDDEN)
//
// Caveats:
//
// 1) Cookie authentication only works properly on our system when we employ DOMAIN cookies
// 2) You should never do plain text Basic authentication over port 80; use SSL encryption via port 443 or switch to
// something else like Digest authentication (mod_auth_digest.c)
//======================================================================================================================
//
// <<< External Authentication >>>
//
// pass as much stuff (cookie and user + password) to the external authenticator
// if the authenticator say the cookie and remote host are valid then that is all we need to move on
// if the cookie is not valid then we will prompt for a username and password (if the authenticator is satisfied
// then it will also pass back a session cookie to be used on the next pass through here
//======================================================================================================================
INTERNAL int authenticate_vms_ext (request_rec *r)
{
unsigned int st; //
unsigned int rc; //
int cache_hit = 0; //
CTXBLK *sec = (CTXBLK *) ap_get_module_config (r->per_dir_config, &auth_vms_ext_module);
//
// cookie-testing is optional but username is not (we prompt for username+password on a cookie miss)
//
if (!sec->fUserEnable) { // if no enabled username directive
return DECLINED; // then exit with a defer
} //
TRC1("new transaction ==========");
conn_rec *c = r->connection; //
const char *pcszRemoteHost = ap_get_remote_host ( r->connection, r->per_dir_config, REMOTE_NOLOOKUP, 0 );
const char *cookie_data = apr_table_get( r->headers_in, "Cookie"); //
const char *type = ap_auth_type(r); // Basic, Digest, etc.
const char *auth_line = apr_table_get( r->headers_in, "Authorization");
const char *pcszUser = apr_pstrcat(r->pool, r->user, NULL, NULL); //
const char *pcszPassword = 0; //
char cookie_payload[8192] = ""; // max total limit for browser cookies
char errstr[MAX_STRING_LEN]= ""; //
char *newCookie = NULL; //
char groupData[32] = ""; //
int score = 0; //
int junk = 0;
char default_realm[] = "OpenVMS External Authentication";
const char *base64_buf = 0;
const char *decode_buf = 0;
const char *cursor = 0;
//--------------------------------------------------------------------------
TRC2("fCookieName: %s",sec->fCookieName);
TRC2("fUserEnable: %d",sec->fUserEnable);
TRC2("fGroupList : %s",sec->fGroupList);
TRC2("fProgram : %s",sec->fProgram);
TRC2("fLogfile : %s",sec->fLogfile);
TRC2("fRealmName : %s",sec->fRealmName);
TRC2(" LocalHost : %s",r->server->server_hostname);
TRC2(" RemoteHost: %s",pcszRemoteHost);
TRC2(" CookieData: %s",cookie_data);
TRC2(" Authtype : %s",type);
TRC2(" Authorizat: %s",auth_line);
TRC2(" USER : %s",r->user);
if (r->user!=0){ //
junk = ap_get_basic_auth_pw (r, &pcszPassword); //
} //
TRC2(" PASS : %s",pcszPassword); //
//--------------------------------------------------------------------------
if ((auth_line) && // if we have an authorization line
((strlen(pcszUser)==0) || (strlen(pcszPassword)==0))) // but no user or password
{
base64_buf = strstr(auth_line,"Basic "); //
if (base64_buf){
base64_buf += 6;
}
decode_buf = ap_pbase64decode(r->pool, base64_buf); //
TRC2("extracted info %s",decode_buf); // user:pass
cursor = decode_buf; //
pcszUser = ap_getword (r->pool, &cursor, ':'); //
TRC2("extracted user %s",pcszUser); //
pcszPassword = ap_getword (r->pool, &cursor, ':'); //
TRC2("extracted pass %s",pcszPassword); //
}
//--------------------------------------------------------------------------
// if a cookie was specified, look for it
//--------------------------------------------------------------------------
if ((sec->fCookieName == NULL) || (cookie_data == NULL)) { // no 'cookie specified' or 'no cookie data'
TRC1("skipping cookie logic"); //
}else{
char *start_cookie, *end_cookie, *payload; //
if (start_cookie = strstr(cookie_data, sec->fCookieName)) { // locate the desired cookie
start_cookie += strlen(sec->fCookieName) + 1; // slide past cookie name (and '=')
end_cookie = (char*) cookie_data + strlen(cookie_data); //
for (char* i=start_cookie;i<=end_cookie;i++) { //
switch(*i) { //
case ';': //
case '\0': //
end_cookie=i; //
break; //
default: //
} //
} //
strncpy(cookie_payload, start_cookie, end_cookie-start_cookie); //
cookie_payload[end_cookie-start_cookie] = '\0'; // ensure string is null terminated
TRC2("Cookie Payload: %s",cookie_payload); //
if (strlen(cookie_payload)>0){ //
score = 1; // indicate we detected the named cookie
} //
} //
} //
//--------------------------------------------------------------------------
// if a username was specified and both username + password are present
//--------------------------------------------------------------------------
if ((!sec->fUserEnable) || // if not enabled
(pcszPassword==0) || // or no password
(pcszUser==0) ){ // or no username
TRC1("skipping user+pass logic");
}else{
score = score | 2; // indicate we detected user + pass
}
if (score==0)
//--------------------------------------------------------------------------
// if we have no data to send to the authenticator then exit now
//--------------------------------------------------------------------------
if (score==0) { // we have nothing to work with
if (!sec->fUserEnable) { // if username not required
if (sec->fAuthoritative) { // if authoritative
TRC1("no data so HTTP_UNAUTHORIZED"); // the buck stops here
return HTTP_UNAUTHORIZED;
}else{
TRC1("no data so DECLINED");
return DECLINED;
}
}else{ // username required but not provided
//
// this is how we command the client's browser to raise a user+pass dialog
// (he will have a user+pass next time through here)
//
TRC1("no data so throw 401 (to get username+password)");
if (strlen(sec->fRealmName)<10) {
apr_table_setn(r->err_headers_out,
(PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate"
: "WWW-Authenticate",
apr_pstrcat(r->pool, "Basic realm=\"", default_realm , "\"", NULL));
}else{
apr_table_setn(r->err_headers_out,
(PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate"
: "WWW-Authenticate",
apr_pstrcat(r->pool, "Basic realm=\"", sec->fRealmName , "\"", NULL));
}
return HTTP_UNAUTHORIZED; //
}
}
#if RMS_SESSION_CACHE==1
if ((sec->fSessionCache!= NULL) && // if sessionFs...
(strlen(cookie_payload)>0)) { // and cookie
junk = session_cache_lookup(sec->fSessionCache, (char*)&cookie_payload, (char*)&groupData);
TRC2("-i-cache_lookup status: %d",junk); //
if (junk==0) { // bf_105.3
TRC2("-i-session-group-info : %s",groupData); //
cache_hit = 1; // block overwriting of groupData below
st = 1; // simulate VMS-S-
goto skip_spawn; // don't hate me because I used "goto' :-)
} //
} //
#endif
//--------------------------------------------------------------------------
// call the external authenticator (lib$spawn is semi-expensive on Alpha)
//--------------------------------------------------------------------------
struct dsc$descriptor_s user2, // username
pass2, // password
cook2, // cookie
lhost2, // local host
rhost2, // remote host (for cookie security check)
debug2, // (optional) debug parameter
spawn_command, // dcl command string
logfile; // (optional) logfile spec
if (sec->fProgram==0) { // if no external dcl command
TRC1("HTTP_NOT_IMPLEMENTED - no program"); //
return HTTP_NOT_IMPLEMENTED; // throw 501 (can't recover from this)
}
VMSIFY(spawn_command, sec->fProgram); //
//--------------------------------------------------
$DESCRIPTOR( debug1,"VMSEXT_DEBUG"); // can only debug if we have a log file
if (sec->fLogfile!=0){
VMSIFY(logfile, sec->fLogfile);
VMSIFY(debug2,"Y");
}else{
VMSIFY(logfile,"NL:");
VMSIFY(debug2,"N");
}
st = lib$set_symbol( &debug1, &debug2, &2 );
//--------------------------------------------------
$DESCRIPTOR( lhost1,"VMSEXT_LHOST");
VMSIFY(lhost2,r->server->server_hostname);
st = lib$set_symbol( &lhost1, &lhost2, &2 );
//--------------------------------------------------
if (pcszRemoteHost!=0){
$DESCRIPTOR( rhost1,"VMSEXT_RHOST");
VMSIFY(rhost2,pcszRemoteHost);
st = lib$set_symbol( &rhost1, &rhost2, &2 );
}
//--------------------------------------------------
if (strlen(cookie_payload)>0){
$DESCRIPTOR( cook1,"VMSEXT_COOKIE");
VMSIFY(cook2,cookie_payload);
st = lib$set_symbol( &cook1, &cook2, &2 );
}
//--------------------------------------------------
if ((score & 2)==2){
if (pcszUser !=0) {
$DESCRIPTOR( user1,"VMSEXT_USER");
VMSIFY(user2,pcszUser);
st = lib$set_symbol( &user1, &user2, &2 );
}
if (pcszPassword!=0) {
$DESCRIPTOR( pass1,"VMSEXT_PASS");
VMSIFY(pass2,pcszPassword);
st = lib$set_symbol( &pass1, &pass2, &2 );
}
}
//--------------------------------------------------
TRC1("calling lib$spawn()");
rc = lib$spawn(
&spawn_command, // command-string
0, // input-file
&logfile, // output-file
0, // flags
0, // process-name
0, // process-id
&st, // completion-status-address
0, // byte-integer-event-flag-num
0, // AST-address
0, // varying-AST-argument
0, // prompt-string
0, // cli
0); // table
//
TRC2("lib$spawn-rc: %ld",rc);
if ((st & 7)!=1) { // if spawn failed
TRC1("forbidden-0"); //
return HTTP_FORBIDDEN; // send back 403 (putting up a user+pass dialog would be pointless)
} //
//--------------------------------------------------
skip_spawn:;
TRC2("exit status: %ld",st);
//
// note: according to the release notes for lib$spawn, a completion code of 0 is mapped to sys$normal (1)
//
switch(st){
case 1:
case 0:
newCookie = getenv("VMSEXT_SETCOOKIE"); // this "might" be supplied by the authenticator
if (newCookie==NULL){
TRC1("authenticator SETCOOKIE data: BLANK");
}else{
TRC2("authenticator SETCOOKIE data: %s",newCookie);
if (sec->fCookieName==NULL) {
TRC1("oops, NO COOKIE NAME defined");
}else{
//
// notes:
// 1) we hope to end up with something like one of these (example two is for a "domain" session cookie)
// ICSIS_SESSION=abcdefghabcdefghabcdefghabcdefgh;
// ICSIS_SESSION=abcdefghabcdefghabcdefghabcdefgh; path=/; HttpOnly; Domain=kawc09.on.bell.ca;
// 2) docs suggest using "apr_table_add()" rather than "apr_table_set()" so I did
// 3) some website say to end with "\n" but that doesn't work here
// 4) our authenticator is expected to send back all the cookie data including the trailing semi-colon
//
char cookie_hack[128];
sprintf(cookie_hack,"%s%c%s",sec->fCookieName,'=',newCookie);
TRC2("SETCOOKIE-HACK: %s",cookie_hack);
apr_table_add(r->headers_out, "Set-Cookie", cookie_hack);
}
}
if (sec->fGroupList) { // if a group list directive is present
if (cache_hit==0){ // if no cache hit, then call getenv
char *temp; //
temp = getenv("VMSEXT_GROUP"); // this "might" be supplied by the authenticator
if (temp==NULL) { //
groupData[0]='\0'; //
}else{ //
sprintf(groupData,temp); // copy temp to group
} //
}
if (strlen(groupData)==0) { // if blank...
TRC1("authenticator GROUP data: BLANK"); //
TRC1("forbidden-1"); //
return HTTP_FORBIDDEN; // send back 403
}else{ //
TRC2("authenticator GROUP data: %s",groupData); //
if (!strstr(sec->fGroupList, groupData)) { // if group is not in the defined list
TRC1("forbidden-2"); //
return HTTP_FORBIDDEN; // send back 403
} //
}
}
TRC1("OK"); // yippee!
return OK; // yippee!
default:
//
// this is how we command the client's browser to raise a user+pass dialog
// (he will have a user+pass next time through here)
//
if (strlen(sec->fRealmName)<10){
apr_table_setn(r->err_headers_out,
(PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate"
: "WWW-Authenticate",
apr_pstrcat(r->pool, "Basic realm=\"", default_realm , "\"", NULL));
}else{
apr_table_setn(r->err_headers_out,
(PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate"
: "WWW-Authenticate",
apr_pstrcat(r->pool, "Basic realm=\"", sec->fRealmName , "\"", NULL));
}
return HTTP_UNAUTHORIZED; // send back 401
}
}
//=====================================================================================================================
// common apache module stuff
//=====================================================================================================================
static void register_hooks(apr_pool_t *p) {
ap_hook_check_user_id(authenticate_vms_ext,NULL,NULL,APR_HOOK_FIRST); // run before MOD_AUTH(_BASIC)
}
module AP_MODULE_DECLARE_DATA auth_vms_ext_module =
{
STANDARD20_MODULE_STUFF,
create_auth_vms_ext_cntxt, // per dir config creater
NULL, // per dir merger --- default is to override
NULL, // server config
NULL, // merge server config
auth_vms_ext_cmds, // command apr_table_t
register_hooks // register hooks
};
#if RMS_SESSION_CACHE==1 // enable support for RMS-based session cache
//==============================================================
// This stuff is for an optional RMS-based session cache
// Note: RMS is an ISAM technology only availble on VMS and OpenVMS
//
// technology: ISAM databases relational databases
// ============== ====================
// terminology: "keys" "indexes"
// "key options" "contraints"
//==============================================================
//
// need some more global variables
//
struct FAB fab; // file access block (an RMS structure)
struct RAB rab; // record access block (an RMS structure)
struct XABKEY key0, // extended access block (for each) key
key1, // first alternate key
key2; // second alternate key
struct {
char icsis_session_id [32]; // 128 bit session ID (cookie) key #0 (primary)
char icsis_session_pin [ 7]; // employee pin key #1 (alternate,dups,changes)
char icsis_session_priv [ 8]; // copied from profile-db at logon
char icsis_session_title [ 3]; // copied from priv at logon
char icsis_session_ip [15]; // ip address where the logon took place
char icsis_session_timestamp[15]; // ccyymmddhhmmsst key #2 (alternate,dups,changes)
char icsis_session_grp [ 3]; //
char icsis_session_ini [ 3]; //
char icsis_session_org [ 8]; //
char icsis_session_name [35]; //
char icsis_session_script [50]; //
char icsis_session_region [ 1]; // copied from d91_region
char icsis_session_language [ 1]; // copied from d91_language
char icsis_session_jobcode [ 3]; // copied from schedule at login
char icsis_session_bu [ 3]; //
char icsis_session_room [13]; //
} session_record;
//=====================================================================================================================
// session cache lookup
// it is assumed that another routine elsewhere is clearing out stale records
//=====================================================================================================================
long session_cache_lookup(char *fSessionCache, char *cookieData, char *groupData) {
int junk;
int rms_status;
char temp1[32];
if ((strlen(fSessionCache)==0) ||
(strlen(cookieData)==0) ) {
TRC1("session_cache_lookup: failed in step 01");
return(-1); // error
}
//-----------------------------------------------------------
// initialize rms
//-----------------------------------------------------------
fab = cc$rms_fab; // Initialize FAB (be a good citizen)
//
// now make changes to the just-initialized fab
//
fab.fab$b_bks = 4; // blocksize: 4
fab.fab$b_fac = FAB$M_GET ; // intend to: GET (read)
fab.fab$l_fna = fSessionCache; // file name addr: filename.ext
fab.fab$b_fns = strlen(fSessionCache); // file name length: whatever
// fab.fab$l_fop = FAB$M_CIF; xx file operation: create if (not found)
fab.fab$w_mrs = sizeof(session_record); // maximum record size: whatever
fab.fab$b_org = FAB$C_IDX; // organization: indexed
fab.fab$b_rat = FAB$M_CR; // record-attribute: <CR>
fab.fab$b_rfm = FAB$C_FIX; // format: fixed
fab.fab$b_shr = FAB$M_NIL; // share: none
fab.fab$l_xab = &key0; // indicate our primary key
//
rab = cc$rms_rab; // Initialize RAB (be a good citizen)
//
// now make changes to the just initialized rab
//
rab.rab$l_fab = &fab; // now point our rab at our fab
//
key0 = cc$rms_xabkey; // Initialize XAB for our key#0 (primary)
//
// now make changes to the just initialized xab
//
key0.xab$b_ref = 0; // reference: this is key #0 (primary key)
key0.xab$b_dtp = XAB$C_STG; // key data type: string
key0.xab$b_flg = 0; // key options: clear all flags (nodup, nochange, etc)
key0.xab$w_pos0 = (char*) &session_record.icsis_session_id - // start of segment0: compute offset from start of record
(char*) &session_record; //
key0.xab$b_siz0= sizeof(session_record.icsis_session_id); // length of segment0: whatever
key0.xab$l_nxt = &key1; // next xab address:
//
key1 = cc$rms_xabkey; // Initialize XAB for our key#1 (first alternate)
//
// now make changes to the just initialized xab (this one is chained to the previous xab)
//
key1.xab$b_ref = 1; // reference: this is key #1
key1.xab$b_dtp = XAB$C_STG; // key data type: string
key1.xab$b_flg = XAB$M_DUP | XAB$M_CHG; // key options: DUP + CHANGE
key1.xab$w_pos0 = (char*) &session_record.icsis_session_pin- // start of segment0: offset from start of record
(char*) &session_record; //
key1.xab$b_siz0= sizeof(session_record.icsis_session_pin); // length of segment0: whatever
key1.xab$l_nxt = &key2; // next xab address:
//
key2 = cc$rms_xabkey; // Initialize XAB for our key#2 (second alternate)
//
// now make changes to the just initialized xab (this one is chained to the previous xab)
//
key2.xab$b_ref = 2; // reference: this is key #2
key2.xab$b_dtp = XAB$C_STG; // key data type: string
key2.xab$b_flg = XAB$M_DUP | XAB$M_CHG; // key options: DUP + CHANGE
key2.xab$w_pos0 = // start of segment0: offset from start of record
(char*) &session_record.icsis_session_timestamp - //
(char*) &session_record; //
key2.xab$b_siz0= // length of segment0: whatever
sizeof(session_record.icsis_session_timestamp);
//-----------------------------------------------------------
// now open the file
//-----------------------------------------------------------
rms_status = sys$open(&fab); // use fab to open the file
if (rms_status != RMS$_NORMAL){
TRC2("session_cache open error: %d",rms_status);
return(-1);
}
rms_status = sys$connect(&rab); // connect rab to fab
if (rms_status != RMS$_NORMAL){ // if some sort of error
TRC2("session_cache connect error: %d",rms_status);
return(-1);
}
//-----------------------------------------------------------
// now use the cookie to do a primary key lookup
//-----------------------------------------------------------
rab.rab$b_krf = 0; // we want to use key#0 (primary)
rab.rab$l_kbf = cookieData; // key buf addr: whatever
rab.rab$b_ksz = sizeof(session_record.icsis_session_id); // key buf size: whatever
rab.rab$b_rac = RAB$C_KEY; // record access: lookup-by-key
rab.rab$l_rop = RAB$M_NLK; // record operations: no-lock
rab.rab$l_ubf = (char *) &session_record; // need for $get (but not $find)
rab.rab$w_usz = sizeof(session_record); // need for $get (but not $find)
//
rms_status = sys$get(&rab); // get-by-key
//
switch(rms_status) {
case RMS$_NORMAL:
TRC1("session_cache_lookup worked (cookie found)");
//
strncpy(temp1, session_record.icsis_session_pin, sizeof(session_record.icsis_session_pin));
temp1[ sizeof(session_record.icsis_session_pin) ] = '\0';
TRC2("-i-session pin: %s", temp1);
//
// in our system, business unit is used in group restriction
//
strncpy(temp1, session_record.icsis_session_bu, sizeof(session_record.icsis_session_bu));
temp1[ sizeof(session_record.icsis_session_bu) ] = '\0';
TRC2("-i-session bu : %s", temp1);
//
// now save BU + GRP as comma-delimited data in the global variable "group"
//
strcpy(groupData,temp1);
TRC2("-i-groupData : %s",groupData);
//
junk = sys$close(&fab); // at this point we will ALWAYS close the file
return(0); // zero means "no errors"
case RMS$_RNF:
TRC1("session_cache_lookup miss");
junk = sys$close(&fab); // at this point we will ALWAYS close the file
return(1); //
default:
TRC2("session_cache find error: %d",rms_status);
junk = sys$close(&fab); // at this point we will ALWAYS close the file
return(1);
}
}
#endif
//==============================================================
Back to
Home
Neil Rieck
Waterloo, Ontario, Canada.