develooper 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]




nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About