Front page | perl.vmsperl |
Postings from February 2002
possible cando_by_name() patch (was RE: Privileges won't letme "opendir"?)
From:
Craig A. Berry
Date:
February 1, 2002 10:56
Subject:
possible cando_by_name() patch (was RE: Privileges won't letme "opendir"?)
Message ID:
5.1.0.14.2.20020201122835.01b2e4b8@mail.mac.com
At 04:59 PM 1/29/2002 -0500, Tom Edelson wrote:
>Under the same conditions (BYPASS on, so DCL can read it, but I don't
>otherwise have access):
>
> $ perl -e "$z = opendir($dh,'[.temp]'); print qq([$!][$^E][$z]\n)"
> [permission denied][%RMS-E-PRV, insufficient privilege or file protection violation][]
OK, I have a provisional patch that takes care of this. Just by way of
review, it makes sure that we perform file access checks in cando_by_name()
against the current process privs rather than the user's default privs in
the UAF. It also makes opendir() check access before doing a stat() because
stat() does not accurately report whether something is a directory if you
don't have privs to look at it. Since this is a security-related issue, I
would especially appreciate code review, testing, or suggestions about a
better way to do this.
--- vms/vms.c_orig Fri Feb 1 11:11:37 2002
+++ vms/vms.c Fri Feb 1 12:41:13 2002
@@ -4705,15 +4705,18 @@
if (do_tovmspath(name,dir,0) == NULL) {
return NULL;
}
+ /* Check access before stat; otherwise stat does not
+ * accurately report whether it's a directory.
+ */
+ if (!cando_by_name(S_IRUSR,0,dir)) {
+ set_errno(EACCES); set_vaxc_errno(RMS$_PRV);
+ return NULL;
+ }
if (flex_stat(dir,&sb) == -1) return NULL;
if (!S_ISDIR(sb.st_mode)) {
set_errno(ENOTDIR); set_vaxc_errno(RMS$_DIR);
return NULL;
}
- if (!cando_by_name(S_IRUSR,0,dir)) {
- set_errno(EACCES); set_vaxc_errno(RMS$_PRV);
- return NULL;
- }
/* Get memory for the handle, and the pattern. */
New(1306,dd,1,DIR);
New(1307,dd->pattern,strlen(dir)+sizeof "*.*" + 1,char);
@@ -6581,6 +6584,9 @@
{sizeof privused, CHP$_PRIVUSED, &privused, &retlen},{0,0,0,0}};
struct itmlst_3 jpilst[2] = {{sizeof curprv, JPI$_CURPRIV, &curprv, &retlen},
{0,0,0,0}};
+ struct itmlst_3 usrprolst[2] = {{sizeof curprv, CHP$_PRIV, &curprv, &retlen},
+ {0,0,0,0}};
+ struct dsc$descriptor_s usrprodsc = {0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0};
if (!fname || !*fname) return FALSE;
/* Make sure we expand logical names, since sys$check_access doesn't */
@@ -6617,7 +6623,28 @@
return FALSE;
}
- retsts = sys$check_access(&objtyp,&namdsc,&usrdsc,armlst);
+ /* Before we call $check_access, create a user profile with the current
+ * process privs since otherwise it just uses the default privs from the
+ * UAF and might return SS$_NOPRIV even if the user does actually have access.
+ */
+
+ /* get current process privs */
+ _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
+ _ckvmssts(iosb[0]);
+
+ /* find out the space required for the profile */
+ _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,0,
+ &usrprodsc.dsc$w_length,0));
+
+ /* allocate space for the profile and get it filled in */
+ New(1330,usrprodsc.dsc$a_pointer,usrprodsc.dsc$w_length,char);
+ _ckvmssts(sys$create_user_profile(&usrdsc,&usrprolst,0,usrprodsc.dsc$a_pointer,
+ &usrprodsc.dsc$w_length,0));
+
+ /* use the profile to check access to the file; free profile & analyze results */
+ retsts = sys$check_access(&objtyp,&namdsc,0,armlst,0,0,0,&usrprodsc);
+ Safefree(usrprodsc.dsc$a_pointer);
+ if (retsts == SS$_NOCALLPRIV) retsts = SS$_NOPRIV; /* not really 3rd party */
if (retsts == SS$_NOPRIV || retsts == SS$_NOSUCHOBJECT ||
retsts == SS$_INVFILFOROP || retsts == RMS$_FNF || retsts == RMS$_SYN ||
retsts == RMS$_DIR || retsts == RMS$_DEV || retsts == RMS$_DNF) {
@@ -6631,7 +6658,6 @@
if (!privused) return TRUE;
/* We can get access, but only by using privs. Do we have the
necessary privs currently enabled? */
- _ckvmssts(sys$getjpiw(0,0,0,jpilst,iosb,0,0));
if ((privused & CHP$M_BYPASS) && !curprv.prv$v_bypass) return FALSE;
if ((privused & CHP$M_SYSPRV) && !curprv.prv$v_sysprv &&
!curprv.prv$v_bypass) return FALSE;
[end of patch]