/*****************************************************************************/ /* ChkAcc.c ----- WASD (HFRD) VMS Hypertext Services, Copyright (C) 1996-2002 Mark G.Daniel. This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License, or any later version. This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ----- CGI utility that can check whether a specified user name is permitted access to a specified file. Relies on the WASD HTTPd authenticating a remote user via the SYSUAF. This is indicated by the CGI variable WWW_AUTH_REALM being "VMS", and the variable WWW_REMOTE_USER containing the authenticated user name. The CGI variable WWW_PATH_TRANSLATED contains the unambiguous VMS file specification being assessed (i.e. cannot contain wildcards). The HTTP method determines whether read or write access is being sought. If "GET" or "HEAD" then read access, any other write access. The exit status always has message reporting inhibited, so it is up to the using procedure to check and act on the $STATUS symbol, etc. Can be used as a stand-alone utility, or just the CheckAccess() function as a template for inclusion in another CGI script. Just to be able to make the access assessment on behalf of a third-party the executable would have to be installed with SYSPRV privilege: $ INSTALL ADD HT_EXE:CHKACC /PRIVILEGE=(SYSPRV) If this was a CGI script accessing a file on behalf of an authenticated third-party then SYSPRV could be needed to open the file for either read or write. This should enabled immediately before opening the file, then disabled immediately after. If it is a temporary file (e.g. delete-on-close) then SYSPRV would require re-enabling immediately before closing, then immediately re-disabling. Good advice: do not attempt writing privileged images unless you are confident you are not creating security holes. COPYRIGHT --------- Copyright (C) 1997-2021 Mark G.Daniel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 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. VERSION HISTORY --------------- 23-DEC-2003 MGD v1.0.2, minor conditional mods to support IA64 08-JUL-2002 MGD v1.0.1, minor changes for stand-along build procedure 25-AUG-1997 MGD v1.0.0, initial development (and happy birthday :^) */ /*****************************************************************************/ #define SOFTWAREVN "1.0.2" #define SOFTWARENM "CHKACC" #ifdef __ALPHA char SoftwareID [] = SOFTWARENM " AXP-" SOFTWAREVN; #endif #ifdef __ia64 char SoftwareID [] = SOFTWARENM " IA64-" SOFTWAREVN; #endif #ifdef __VAX char SoftwareID [] = SOFTWARENM " VAX-" SOFTWAREVN; #endif #ifdef __x86_64 # define SOFTWAREID SOFTWARENM " X86-" SOFTWAREVN #endif /* standard C header files */ #include #include #include #include #include /* VMS related header files */ #include #include #include #include #include #include #include #define VMSok(x) ((x) & STS$M_SUCCESS) #define VMSnok(x) !(((x) & STS$M_SUCCESS)) #define boolean int #define true 1 #define false 0 #define READ_ACCESS 1 #define WRITE_ACCESS 2 /* macro provides NULL pointer if CGI variable does not exist */ #define GetCgiVarIfExists(CharPointer,CgiVariableName) \ CharPointer = getenv(CgiVariableName) /* macro provides pointer to empty string even if CGI variable does not exist */ #define GetCgiVar(CharPointer,CgiVariableName) \ if ((CharPointer = getenv(CgiVariableName)) == NULL) \ CharPointer = ""; \ if (Debug) fprintf (stdout, "%s |%s|\n", CgiVariableName, CharPointer); char Utility [] = "CHKACC"; boolean Debug; /*****************************************************************************/ /* */ main () { int status; char *CgiAuthRealmPtr, *CgiPathTranslatedPtr, *CgiRemoteUserPtr, *CgiRequestMethodPtr; /*********/ /* begin */ /*********/ if (getenv ("CHKACC$DBUG") != NULL) Debug = true; GetCgiVar (CgiAuthRealmPtr, "WWW_AUTH_REALM"); GetCgiVar (CgiPathTranslatedPtr, "WWW_PATH_TRANSLATED"); GetCgiVar (CgiRemoteUserPtr, "WWW_REMOTE_USER"); GetCgiVar (CgiRequestMethodPtr, "WWW_REQUEST_METHOD"); if (!CgiAuthRealmPtr[0] || strcmp (CgiAuthRealmPtr, "VMS")) { fprintf (stdout, "%%%s-E-AUTHREALM, not VMS\n", Utility); exit (STS$K_ERROR | STS$M_INHIB_MSG); } if (!CgiRemoteUserPtr[0]) { fprintf (stdout, "%%%s-E-REMOTEUSER, no authenticated user\n", Utility); exit (STS$K_ERROR | STS$M_INHIB_MSG); } if (!CgiPathTranslatedPtr[0]) { fprintf (stdout, "%%%s-E-PATHTRANS, no file specification\n", Utility); exit (STS$K_ERROR | STS$M_INHIB_MSG); } if (!CgiRequestMethodPtr[0]) { fprintf (stdout, "%%%s-E-REQUESTMETHOD, method?\n", Utility); exit (STS$K_ERROR | STS$M_INHIB_MSG); } if (!strcmp (CgiRequestMethodPtr, "GET") || !strcmp (CgiRequestMethodPtr, "HEAD")) status = CheckAccess (CgiRemoteUserPtr, CgiPathTranslatedPtr, READ_ACCESS); else status = CheckAccess (CgiRemoteUserPtr, CgiPathTranslatedPtr, WRITE_ACCESS); exit (status | STS$M_INHIB_MSG); } /*****************************************************************************/ /* Can be called one or more times using different user names. Using VMS security system services checks that the specified user name can access the specified file name either for read or write. Returns SS$_NOPRIV if access is denied, SS$_NORMAL if access permitted, or other status code. */ int CheckAccess ( char *UserName, char *FileName, int Access ) { static unsigned long SysPrvMask [2] = { PRV$M_SYSPRV, 0 }; static unsigned long Flags = 0, ArmReadAccess = ARM$M_READ, ArmWriteAccess = ARM$M_WRITE; static $DESCRIPTOR (ClassNameDsc, "FILE"); static struct { short BufferLength; short ItemCode; void *BufferPtr; void *LengthPtr; } ReadItem [] = { { sizeof(ArmReadAccess), CHP$_ACCESS, &ArmReadAccess, 0 }, { 0, 0, 0, 0 } }, WriteItem [] = { { sizeof(ArmWriteAccess), CHP$_ACCESS, &ArmWriteAccess, 0 }, { 0, 0, 0, 0 } }; int status, SetPrvStatus; unsigned short UserProfileLength; unsigned char UserProfile [2048]; $DESCRIPTOR (FileNameDsc, FileName); $DESCRIPTOR (UserNameDsc, UserName); $DESCRIPTOR (UserProfileDsc, ""); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CheckAccess() |%s|%s|\n", UserName, FileName); UserNameDsc.dsc$w_length = strlen(UserName); UserProfileLength = sizeof(UserProfile); if (VMSnok (status = sys$setprv (1, &SysPrvMask, 0, 0))) return (status); status = sys$create_user_profile (&UserNameDsc, 0, Flags, UserProfile, &UserProfileLength, 0); if (Debug) fprintf (stdout, "sys$create_user_profile() %%X%08.08X\n", status); if (VMSnok (status)) { if (VMSnok (SetPrvStatus = sys$setprv (0, &SysPrvMask, 0, 0))) exit (SetPrvStatus); return (status); } FileNameDsc.dsc$a_pointer = FileName; FileNameDsc.dsc$w_length = strlen(FileName); UserProfileDsc.dsc$a_pointer = (char*)UserProfile; UserProfileDsc.dsc$w_length = UserProfileLength; if (Access == READ_ACCESS) status = sys$check_access (0, &FileNameDsc, 0, &ArmReadAccess, 0, &ClassNameDsc, 0, &UserProfileDsc); else if (Access == WRITE_ACCESS) status = sys$check_access (0, &FileNameDsc, 0, &ArmWriteAccess, 0, &ClassNameDsc, 0, &UserProfileDsc); else { if (VMSnok (SetPrvStatus = sys$setprv (0, &SysPrvMask, 0, 0))) exit (SetPrvStatus); return (SS$_BADPARAM); } if (Debug) fprintf (stdout, "sys$check_access() %%X%08.08X\n", status); if (VMSnok (SetPrvStatus = sys$setprv (0, &SysPrvMask, 0, 0))) exit (SetPrvStatus); return (status); } /*****************************************************************************/