/***************************************************************************** /* CLI.c Module to process (interpret) the HTTPD command line. Allows images activated by a "foreign verb" to behave in a way that approximates the CLI$ (Command Line Interpreter) utility calls. VERSION HISTORY --------------- 05-OCT-2009 MGD remove /FILBUF 11-JUL-2009 MGD add /ENV= 25-MAY-2005 MGD add /VALBLK[=16|64] 15-APR-2004 MGD obsolete /LOCK 19-JUL-2003 MGD add /[NO]NETWORK 03-MAY-2003 MGD add /SYSUAF=VMS keyword 30-JAN-2003 MGD add /PROFILE=NORULE keyword 23-SEP-2002 MGD add /SCRIPT=AS= 16-SEP-2002 MGD add /PERSONA[=RELAXED|AUTHORIZED|RELAXED=AUTHORIZED], rework qualifier keyword parsing 05-MAY-2002 MGD add /DEMO catch-all for freeware demo use, etc., /GBLSEC=NOPERM for no permanent global section 29-SEP-2001 MGD add /[NO]INSTANCE[=integer] number of per-node instances, /CLUSTER[=integer] grouping (changed from [=resname]) 20-MAY-2001 MGD add /GBLSEC=DELETE= 23-FEB-2001 MGD add /SYSUAF=PROXY 20-DEC-2000 MGD add /PROXY= 02-OCT-2000 MGD add /DETACH, /IDENT=, /PERSONA=, /USER= 18-JUN-2000 MGD add /ALL[=resname] 02-JAN-2000 MGD add /[NO]ODS5, ability to use HTTPD$PARAM 30-OCT-1999 MGD add /SYSPRV 29-SEP-1999 MGD change /SYSUAF= ALL to RELAXED (backward-compatible) 30-JUL-1999 MGD bugfix; /SYSUAF does not need a parameter! 26-MAY-1999 MGD /SSL= now takes a string parameter 24-FEB-1998 MGD /AUTH=ALL, /AUTH=(SSL,ALL), /SYSUAF=WASD_ID 07-NOV-1998 MGD /[NO]WATCH= 07-AUG-1998 MGD /PROMISCUOUS[=password] 06-JUL-1998 MGD v5.1, /SYSUAF=ID 11-MAR-1998 MGD /SYSUAF=(SSL,ALL) 04-FEB-1998 MGD v5.0, /SSL, /SYSUAF=SSL 11-OCT-1997 MGD v4.5, /CACHE, /VERSION 06-SEP-1997 MGD v4.4, /FORMAT, /PROFILE, /SYSUAF, /SOFTWAREID 01-FEB-1997 MGD HTTPd version 4 01-DEC-1995 MGD HTTPd version 3 20-DEC-1994 MGD multi-threaded daemon 20-JUN-1994 MGD single-threaded daemon */ /*****************************************************************************/ #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 #include #include #include /* VMS header files */ #include #include #include #include #include "wasd.h" #define WASD_MODULE "CLI" /******************/ /* global storage */ /******************/ BOOL CliCacheDisabled, CliCacheEnabled, CliDemo, CliDetach, CliGblSecDelete, CliGblSecNoPerm, CliInstanceNoCrePrc, CliLoggingDisabled, CliLoggingEnabled, CliMonitorDisabled, CliMonitorEnabled, CliNetworkMode, CliNoNetworkMode, CliOdsExtendedDisabled, CliOdsExtendedEnabled, CliPersonaAuthorized, CliPersonaEnabled, CliPersonaRelaxed, CliPersonaRelaxedAuthorized, CliSoftwareID, CliTests, CliVersion; int CliInstanceMax, CliLockValueBlockSize, CliServerPort; char CommandLine [256], CliParameter [256], CliProcessIdentName [64], CliProxyMaint [128], CliServices [256], CliScriptAs [64], CliUserName [64]; char *CliAcceptHostsPtr = NULL, *CliRejectHostsPtr = NULL; /********************/ /* external storage */ /********************/ #ifdef DBUG extern BOOL Debug; #else #define Debug 0 #endif extern BOOL AccountingZeroOnStartup, AuthPromiscuous, AuthPolicyAuthorizedOnly, AuthPolicySslOnly, AuthPolicySysUafProxy, AuthPolicySysUafSslOnly, AuthPolicySysUafRelaxed, AuthPolicySysUafIdentifiers, AuthPolicySysUafVms, AuthPolicySysUafWasdIdentifiers, AuthSysUafEnabled, AuthSysUafPromiscuous, AuthVmsUserProfileNoRule, AuthVmsUserProfileEnabled, ControlDoAllHttpd, NoSwapOut, OperateWithSysPrv; extern int DclCgiPlusInSize, DclSysCommandSize, DclSysOutputSize, DclCgiVariablePrefixLength, InstanceEnvNumber, NetReadBufferSize, OutputBufferSize, ProcessPriority, ServerPort; extern int ToLowerCase[], ToUpperCase[]; extern char *AuthPromiscuousPwdPtr; extern char ControlBuffer[], DclCgiVariablePrefix[], LoggingFileName[], LoggingFormatUser[], LoggingPeriodName[], PersonaIdentName[], SesolaParams[], SoftwareID[]; /****************************************************************************/ /* This function allows images activated by a "foreign verb" to behave in a way that approximates the CLI$ (Command Line Interpreter) utility calls. Get the entire command line following the verb that activated the image. The command line is returned in uppercase, space compressed (i.e. maximum of one space between text elements, trimmed of leading and trailing spaces). Returns a warning status if there were no parameters/qualifiers on the command line. The variable CommandLine is global. */ int ParseCommandLine () { int status; unsigned short Length; unsigned long Flags = 0; char *cptr; $DESCRIPTOR (CommandLineDsc, CommandLine); if (!(cptr = getenv ("WASD_HTTPD_PARAM")) && !(cptr = getenv ("HTTPD$PARAM"))) { /* get the entire command line following the verb */ if (VMSnok (status = lib$get_foreign (&CommandLineDsc, 0, &Length, &Flags))) return (status); CommandLine[Length] = '\0'; } else { strncpy (CommandLine, cptr, sizeof(CommandLine)); CommandLine[sizeof(CommandLine)-1] = '\0'; } if (ParseCommand (CommandLine)) return (SS$_NORMAL); else return (STS$K_ERROR | STS$M_INHIB_MSG); } /****************************************************************************/ /* This function allows images activated by a "foreign verb" to behave in a way that approximates the CLI$ (Command Line Interpreter) utility calls. Quoted strings are always indicated by being parsed to include a single leading quote. */ BOOL ParseCommand (char *CommandLine) { BOOL CommandLineOK = true; int QuoteCount = 0; char *cptr, *eptr; char Entity [256] = ""; /* set up any argument defaults */ ParseCommandEntity (NULL); cptr = CommandLine; eptr = Entity; for (;;) { if (*cptr == '\"') { QuoteCount++; *eptr++ = *cptr++; continue; } if (QuoteCount & 1 && *cptr) { /* inside quoted text, copy all characters as literals */ *eptr++ = *cptr++; continue; } if (*cptr == '/' || ISLWS (*cptr) || !*cptr) { if (ISLWS (*cptr)) { /* span the white space */ while (*cptr && ISLWS (*cptr)) cptr++; if (*cptr == '=') { /* part of a qualifier, continue to get the value */ *eptr++ = *cptr++; /* span any intervening white space */ while (*cptr && ISLWS (*cptr)) cptr++; continue; } } if (Entity[0]) { *eptr = '\0'; if (!ParseCommandEntity (Entity)) CommandLineOK = false; } /* if end of command line then break from loop */ if (!*cptr) break; /* start of new entity */ eptr = Entity; /* if start of qualifier ensure slash is copied */ if (*cptr == '/') *eptr++ = *cptr++; continue; } /* any other character, just copy, ensure upper case */ *eptr++ = TOUP(*cptr++); } return (CommandLineOK); } /*****************************************************************************/ /* Get a string value from a qualifier, e.g. '/EXAMPLE=TEST'. */ BOOL ParseCommandString ( char *Entity, char *String, BOOL Qualifier, BOOL ReportErrors, BOOL EnsureUpperCase ) { int QuoteCount = 0; char *eptr, *sptr; eptr = Entity; if (Qualifier) { /* scan down to equate symbol */ while (*eptr && *eptr != '=') eptr++; if (*eptr) eptr++; if (!*eptr) { if (ReportErrors) FaoToStdout ("%HTTPD-E-VALREQ, \ missing qualifier or keyword value\n \\!AZ\\\n", Entity+1); return (false); } } sptr = String; while (*eptr) { if (*eptr == '\"') { if (QuoteCount & 1) { /* are inside quotes, check for escaped quotes ("") */ if (*++eptr != '\"') { /* now outside quotes */ QuoteCount++; } /* drop thru to character copy */ } else { /* now inside quotes */ QuoteCount++; eptr++; continue; } } if (EnsureUpperCase) *sptr++ = TOUP(*eptr++); else *sptr++ = *eptr++; } *sptr = '\0'; return (true); } /*****************************************************************************/ /* Get an integer value from a qualifier, e.g. '/EXAMPLE=99'. */ BOOL ParseCommandInteger ( char *Entity, int *IntegerPtr, int Base, BOOL ReportErrors ) { char *eptr, *sptr; for (eptr = Entity; *eptr && *eptr != '='; eptr++); if (*eptr) eptr++; if (*eptr) { *IntegerPtr = strtol (eptr, &sptr, Base); if (sptr > eptr && !*sptr) return (true); else { if (ReportErrors) FaoToStdout ("%HTTPD-E-BADVALUE, \ '!AZ' is an invalid keyword value\n", eptr); return (false); } } else { if (ReportErrors) { FaoToStdout ("%HTTPD-E-VALREQ, \ missing qualifier or keyword value\n \\!AZ\\\n", Entity+1); } return (false); } } /*****************************************************************************/ /* A single command line "entity" has been parsed, check if its recognised. This function is the one modified for the individual requirements of each program. */ BOOL ParseCommandEntity (char *Entity) { BOOL ok; int Length; char Scratch [256]; char *cptr, *sptr, *tptr, *zptr; if (!Entity) { /* set up any argument defaults */ return (true); } ok = false; if (Entity[0] == '/') { if (strsame (Entity, "/ACCEPT=", 4)) { ok = ParseCommandString (Entity, Scratch, true, true, true); for (cptr = Scratch; *cptr && ISLWS(*cptr); cptr++); if (!*cptr) return (ok); CliAcceptHostsPtr = VmGet (Length = strlen(Scratch)+1); memcpy (CliAcceptHostsPtr, Scratch, Length); return (ok); } if (strsame (Entity, "/AUTHORIZE=", 4)) { for (cptr = Entity; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; while (*cptr) { while (*cptr == '(' || *cptr == ',' || *cptr == ')') cptr++; zptr = (sptr = Scratch) + sizeof(Scratch)-1; while (*cptr && *cptr != ',' && *cptr != ')' && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; if (!Scratch[0]) continue; if (strsame (Scratch, "ALL", -1)) AuthPolicyAuthorizedOnly = ok = true; else if (strsame (Scratch, "SSL", -1)) AuthPolicySslOnly = ok = true; else { FaoToStdout ("%HTTPD-E-IVKEYW, unrecognized keyword\n \\!AZ\\\n", Scratch); return (false); } } if (!ok) FaoToStdout ("%HTTPD-E-VALREQ, \ missing qualifier or keyword value\n \\!AZ\\\n", Entity); return (true); } if (strsame (Entity, "/CACHE", 4)) { CliCacheEnabled = true; CliCacheDisabled = false; return (true); } if (strsame (Entity, "/NOCACHE", 6)) { CliCacheEnabled = false; CliCacheDisabled = true; return (true); } if (strsame (Entity, "/CGI_PREFIX=", 4)) { ok = ParseCommandString (Entity, DclCgiVariablePrefix, true, true, true); DclCgiVariablePrefixLength = strlen(DclCgiVariablePrefix); return (ok); } if (strsame (Entity, "/CLUSTER=", 4) || strsame (Entity, "/ALL=", 4)) { ControlDoAllHttpd = true; for (cptr = Entity; *cptr && *cptr != '='; cptr++); if (!*cptr) return (true); /* optional grouping number */ return (ParseCommandInteger (Entity, &InstanceEnvNumber, 10, true)); } if (strsame (Entity, "/DCLCGIPLUSIN=", 6)) return (ParseCommandInteger (Entity, &DclCgiPlusInSize, 10, true)); if (strsame (Entity, "/DETACH", 4)) return (CliDetach = true); if (strsame (Entity, "/DO=", 4)) return (ParseCommandString (Entity, ControlBuffer, true, true, false)); #ifdef DBUG /* turns on all "if (Debug)" statements */ if (strsame (Entity, "/DBUG", -1)) return (Debug = true); #endif if (strsame (Entity, "/DEMO", -1)) return (CliDemo = true); if (strsame (Entity, "/ENV=", 4)) return (ParseCommandInteger (Entity, &InstanceEnvNumber, 10, true)); if (strsame (Entity, "/FORMAT=", 4)) return (ParseCommandString (Entity, LoggingFormatUser, true, true, false)); if (strsame (Entity, "/GBLSEC=DELETE", 15)) { CliGblSecDelete = true; return (true); } if (strsame (Entity, "/GBLSEC=NOPERM", 15)) { CliGblSecNoPerm = true; return (true); } if (strsame (Entity, "/IDENT=", 4)) return (ParseCommandString (Entity, CliProcessIdentName, true, true, true)); if (strsame (Entity, "/NOINSTANCE", 6)) return (CliInstanceNoCrePrc = true); if (strsame (Entity, "/INSTANCE=", 4)) { for (cptr = Entity; *cptr && *cptr != '='; cptr++); if (!*cptr) return (false); cptr++; if (strsame (cptr, "CPU", 3)) { CliInstanceMax = INSTANCE_PER_CPU; return (true); } return (ParseCommandInteger (Entity, &CliInstanceMax, 10, true)); } if (strsame (Entity, "/LOG=", 4)) { CliLoggingEnabled = true; CliLoggingDisabled = false; if (Entity[4] == '=') return (ParseCommandString (Entity, LoggingFileName, true, true, true)); else return (true); } if (strsame (Entity, "/NOLOG", 6)) { CliLoggingEnabled = false; CliLoggingDisabled = true; return (true); } if (strsame (Entity, "/MONITOR", 4)) { CliMonitorEnabled = true; CliMonitorDisabled = false; return (true); } if (strsame (Entity, "/NOMONITOR", 6)) { CliMonitorEnabled = false; CliMonitorDisabled = true; return (true); } if (strsame (Entity, "/NETWORK", 5)) { CliNetworkMode = true; CliNoNetworkMode = false; return (true); } if (strsame (Entity, "/NONETWORK", 5)) { CliNetworkMode = false; CliNoNetworkMode = true; return (true); } if (strsame (Entity, "/NETBUF=", 5)) return (ParseCommandInteger (Entity, &NetReadBufferSize, 10, true)); if (strsame (Entity, "/OUTBUF=", 5)) return (ParseCommandInteger (Entity, &OutputBufferSize, 10, true)); if (strsame (Entity, "/PERIOD=", 5)) return (ParseCommandString (Entity, LoggingPeriodName, true, true, false)); if (strsame (Entity, "/PERSONA=", 5)) { for (cptr = Entity; *cptr && *cptr != '='; cptr++); if (!*cptr) return (CliPersonaEnabled = true); cptr++; while (*cptr) { while (*cptr == '(' || *cptr == ',' || *cptr == ')') cptr++; zptr = (sptr = Scratch) + sizeof(Scratch)-1; while (*cptr && *cptr != ',' && *cptr != ')' && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; if (!Scratch[0]) continue; if (strsame (Scratch, "AUTHORIZED", 4)) CliPersonaAuthorized = true; else if (strsame (Scratch, "RELAXED", 5)) { CliPersonaRelaxed = true; for (sptr = Scratch; *sptr && *sptr != '='; sptr++); if (strsame (sptr, "=AUTHORIZED", 5)) CliPersonaRelaxedAuthorized = true; } else if (strsame (Scratch, "IDENT=", 6)) { strcpy (PersonaIdentName, Scratch+6); for (sptr = PersonaIdentName; *sptr; sptr++) *sptr = TOUP(*sptr); } else { strcpy (PersonaIdentName, Scratch); for (sptr = PersonaIdentName; *sptr; sptr++) *sptr = TOUP(*sptr); } } return (CliPersonaEnabled = true); } if (strsame (Entity, "/NOPERSONA", 5)) { CliPersonaAuthorized = CliPersonaEnabled = CliPersonaRelaxed = CliPersonaRelaxedAuthorized = false; return (true); } if (strsame (Entity, "/PORT=", 4)) return (ParseCommandInteger (Entity, &CliServerPort, 10, true)); if (strsame (Entity, "/PRIORITY=", 4)) return (ParseCommandInteger (Entity, &ProcessPriority, 10, true)); if (strsame (Entity, "/PROFILE=", 5)) { for (cptr = Entity; *cptr && *cptr != '='; cptr++); if (*cptr) { cptr++; if (strsame (cptr, "NORULE", -1)) AuthVmsUserProfileNoRule = true; else { FaoToStdout ("%HTTPD-E-IVKEYW, unrecognized keyword\n \\!AZ\\\n", cptr); return (AuthVmsUserProfileEnabled = false); } } return (AuthVmsUserProfileEnabled = true); } if (strsame (Entity, "/NOPROFILE", 6)) { AuthVmsUserProfileEnabled = false; return (true); } /* for testing only, also needs /SYSUAF=PROMSICUOUS if using SYSUAF! */ if (strsame (Entity, "/PROMISCUOUS=", 5)) { for (cptr = Entity; *cptr && *cptr != '='; cptr++); if (*cptr) { cptr++; AuthPromiscuousPwdPtr = VmGet (Length = strlen(cptr)+1); memcpy (AuthPromiscuousPwdPtr, cptr, Length); } return (AuthPromiscuous = true); } if (strsame (Entity, "/NOPROMISCUOUS", 7)) { AuthPromiscuous = false; return (true); } if (strsame (Entity, "/PROXY=", 5)) { ok = ParseCommandString (Entity, CliProxyMaint, true, true, true); if (CliProxyMaint[0]) return (ok); FaoToStdout ("%HTTPD-E-VALREQ, \ missing qualifier or keyword value\n \\!AZ\\\n", Entity); return (false); } if (strsame (Entity, "/REJECT=", 4)) { ok = ParseCommandString (Entity, Scratch, true, true, true); for (cptr = Scratch; *cptr && ISLWS(*cptr); cptr++); if (!*cptr) return (ok); CliRejectHostsPtr = VmGet (Length = strlen(Scratch)+1); memcpy (CliRejectHostsPtr, Scratch, Length); return (ok); } if (strsame (Entity, "/SCRIPT=AS=", 4)) { for (cptr = Entity; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; while (*cptr && *cptr != '=') cptr++; if (*cptr) { cptr++; zptr = (sptr = CliScriptAs) + sizeof(CliScriptAs)-1; while (*cptr && sptr < zptr) *sptr++ = TOUP(*cptr++); *sptr = '\0'; if (CliScriptAs[0]) return (true); } FaoToStdout ("%HTTPD-E-VALREQ, \ missing qualifier or keyword value\n \\!AZ\\\n", Entity); return (false); } if (strsame (Entity, "/SERVICES=", 4)) return (ParseCommandString (Entity, CliServices, true, true, false)); /* This qualifier may come in handy if having to use VMS or WASD causes embarressment :^) Seriously, if you consider providing the server identification a security issue use this qualifier to set it to something like "Mine", "Unknown", "?", etc. */ if (strsame (Entity, "/SOFTWAREid=", 4)) { CliSoftwareID = true; return (ParseCommandString (Entity, SoftwareID, true, true, false)); } if (strsame (Entity, "/SSL=", 4)) return (ParseCommandString (Entity, SesolaParams, true, true, false)); if (strsame (Entity, "/NOSSL", 6)) { strcpy (SesolaParams, "/NOSSL"); return (true); } if (strsame (Entity, "/SUBBUF=", 5)) return (ParseCommandInteger (Entity, &DclSysOutputSize, 10, true)); if (strsame (Entity, "/SWAP", -1)) { NoSwapOut = false; return (true); } if (strsame (Entity, "/NOSWAP", -1)) return (NoSwapOut = true); if (strsame (Entity, "/ODS5", -1)) { CliOdsExtendedEnabled = true; CliOdsExtendedDisabled = false; return (true); } if (strsame (Entity, "/NOODS5", -1)) { CliOdsExtendedEnabled = false; CliOdsExtendedDisabled = true; return (true); } if (strsame (Entity, "/SYSPRV", -1)) return (OperateWithSysPrv = true); if (strsame (Entity, "/NOSYSPRV", -1)) { OperateWithSysPrv = false; return (true); } if (strsame (Entity, "/SYSUAF=", 4)) { AuthSysUafEnabled = true; for (cptr = Entity; *cptr && *cptr != '='; cptr++); if (!*cptr) return (true); cptr++; while (*cptr) { while (*cptr == '(' || *cptr == ',' || *cptr == ')') cptr++; zptr = (sptr = Scratch) + sizeof(Scratch)-1; while (*cptr && *cptr != ',' && *cptr != ')' && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; if (!Scratch[0]) continue; if (strsame (Scratch, "ALL", -1) || strsame (Scratch, "RELAXED", -1)) AuthPolicySysUafRelaxed = true; else if (strsame (Scratch, "ID", -1)) AuthPolicySysUafIdentifiers = true; else if (strsame (Scratch, "PROXY", -1)) AuthPolicySysUafProxy = true; else if (strsame (Scratch, "SSL", -1)) AuthPolicySysUafSslOnly = true; else if (strsame (Scratch, "VMS", -1)) AuthPolicySysUafVms = true; else if (strsame (Scratch, "WASD", -1) || strsame (Scratch, "WASD_ID", -1)) AuthPolicySysUafWasdIdentifiers = true; else { FaoToStdout ("%HTTPD-E-IVKEYW, unrecognized keyword\n \\!AZ\\\n", Scratch); return (AuthSysUafEnabled = false); } } return (true); } if (strsame (Entity, "/NOSYSUAF", 6)) { AuthSysUafEnabled = false; return (true); } /* for testing SYSUAF-authenticated security profiles purposes only */ if (strsame (Entity, "/SYSUAF=PROMISCUOUS", -1)) return (AuthSysUafEnabled = AuthSysUafPromiscuous = true); if (strsame (Entity, "/TESTS", 4)) return (CliTests = true); if (strsame (Entity, "/USER=", 4)) return (ParseCommandString (Entity, CliUserName, true, true, true)); if (strsame (Entity, "/VALBLK=", 5)) { /* used without a value set to the default value block size */ CliLockValueBlockSize = -1; ParseCommandInteger (Entity, &CliLockValueBlockSize, 10, false); if (CliLockValueBlockSize == -1 || CliLockValueBlockSize == 16 || CliLockValueBlockSize == 64) return (true); FaoToStdout ("%HTTPD-E-IVKEYW, unrecognized keyword\n \\!UL\\\n", CliLockValueBlockSize); CliLockValueBlockSize = 0; return (false); } if (strsame (Entity, "/VERSION", 4)) return (CliVersion = true); if (strsame (Entity, "/WATCH=", 4) || strsame (Entity, "/NOWATCH", 6)) return (WatchCliParse (Entity)); if (strsame (Entity, "/ZERO", -1)) return (AccountingZeroOnStartup = true); if (strsame (Entity, "/NOZERO", -1)) { AccountingZeroOnStartup = false; return (true); } FaoToStdout ("%HTTPD-E-IVQUAL, unrecognised qualifier\n \\!AZ\\\n", Entity+1); return (false); } if (!CliParameter[0]) return (ParseCommandString (Entity, CliParameter, false, true, false)); FaoToStdout ("%HTTPD-E-MAXPARM, too many parameters\n \\!AZ\\\n", Entity); return (false); } /*****************************************************************************/