I've noticed if you change the security settings for a particular directory, you can make that folder no longer "browsable" in windows. In particular, changing the "Read" permission for Administrators to "Deny" will make that folder inaccessible.
The question I now have, is how do I figure this out in code? I following gets me close, but it still ain't right:
/// <summary>
/// Takes in a directory and determines if the current user has read access to it (doesn't work for network drives)
/// THIS IS VERY HACKY
/// </summary>
/// <param name="dInfo">directoryInfo object to the directory to examine</param>
/// <returns>true if read access is available, false otherwise</returns>
public static bool IsDirectoryReadable(DirectoryInfo dInfo)
{
try
{
System.Security.AccessControl.DirectorySecurity dirSec = dInfo.GetAccessControl();
System.Security.Principal.WindowsIdentity self = System.Security.Principal.WindowsIdentity.GetCurrent();
System.Security.Principal.WindowsPrincipal selfGroup = new System.Security.Principal.WindowsPrincipal(self);
// Go through each access rule found for the directory
foreach (System.Security.AccessControl.FileSystemAccessRule ar in dirSec.GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)))
{
if (selfGroup.IsInRole((System.Security.Principal.SecurityIdentifier)ar.IdentityReference))
{
// See if the Read right is included
if ((ar.FileSystemRights & System.Security.AccessControl.FileSystemRights.Read) == System.Security.AccessControl.FileSystemRights.Read)
{
if (ar.AccessControlType == System.Security.AccessControl.AccessControlType.Allow)
{
// If all of the above are true, we do have read access to this directory
return true;
}
else
{
return false;
}
}
}
}
// If we didn't find anything
return false;
}
catch
{
// If anything goes wrong, assume false
return false;
}
}
I'm close with the above, but I'm still missing something huge. If I right click on a folder to set permissions, I see (in my example) 3 groups or user names: "Administrators, myUserName and SYSTEM". If I set the "Read" to Deny for either "Administrators" or "myUserName" I can no longer browse the directory. If I only set "System" to "Deny", I can still browse it.
There seems to be some sort of permission hierarchy implied, where either myUserName or Administrator supersedes the SYSTEM group/user.
The code above looks for the first Allow for "Read" it finds for my user identity and returns true. I could also write code that looks for the first "Deny" for Read and returns false.
I can set a folder to be Read - "Deny" for SYSTEM and Read - "Allow" for the other two accounts and still read the folder. If I change the code to look for Deny's and it encounters the SYSTEM user identity first, my function will return "false", which is... false. It might very well be Read - "Allow" for the other two accounts.
The problem that I still can't figure out is, how can I determine which user identity permission has priority over all of the others?
It gets very tricky because ACL's allow inheritance, but they also have a model of most restrictive access. In other words, if you have a DENY anywhere in your user chain to a resource, no matter how many other groups may give you an ALLOW, you are denied. There is a good article on the subect on MSDN.
The system group is related to the O/S process and not directly related to your user account. It's what the O/S would use to access the filesystem if there was no user context. Since your application is running as your "username" the permissions come from it and the groups it's in. Don't think you need to be checking System in this case unless I'm missing something.
Update: Keep in mind that Directory.Exists() will also check that you have permissions to read the directory.
The issue isn’t a permission hierarchy. The problem is that your code is returning true or false based on the first matching Role. You really need to evaluate all permissions.
I recently had to deal with this issue... I posted my code here.
Related
I am using SetVolumeMountPoint to mount a vhd to a drive letter of my choosing. The problem is that when the vhd is mounted file explorer automatically opens at the new drive directory. This is a problem for me as I need my programs to remain on the foreground and sometimes the spawned file explorer becomes part of the foreground.
https://learn.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-setvolumemountpointa
Thoughts?
UPDATE:
I programmatically set the noautorun registry key using these two methods before mounting my vhd:
/// <summary>
/// Removing file explorer auto run for the given DriveLetter so that when a vhd is mounted file explorer doesn't open
/// </summary>
/// <param name="DriveLetter"></param>
private void RemoveFileExplorerAutoRun(char DriveLetter)
{
var KeyPath = "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer";
RegistryKey AutoRunKey = Registry.CurrentUser.OpenSubKey(KeyPath, true);
var DriveLetterValue = DriveLetter - 'A';
if (AutoRunKey != null)
{
RemoveFileExplorerAutoRun(AutoRunKey, DriveLetterValue);
}
else // create key as it does not exist
{
AutoRunKey = Registry.CurrentUser.CreateSubKey(KeyPath);
RemoveFileExplorerAutoRun(AutoRunKey, DriveLetterValue);
}
}
private void RemoveFileExplorerAutoRun(RegistryKey AutoRunKey, int DriveLetterValue)
{
if (AutoRunKey != null)
{
AutoRunKey.SetValue("NoDriveTypeAutoRun", DriveLetterValue);
AutoRunKey.Close();
}
}
Cleanest way seem to be to catch RegisterWindowMessage("QueryCancelAutoPlay") message by your foreground window and return TRUE from your window procedure.
https://learn.microsoft.com/en-us/windows/desktop/shell/autoplay-reg
EDIT:
If foreground window is not your application window, then I would recommend against editing registry, since it is global state, whereas you need just temporary autorun bypass.
Besides windows hook, mentioned in other answer, I would suggest registering your implementation of IQueryCancelAutoPlay interface in running object table
Another way is using Registry.
Please reference "Using the Registry to Disable AutoRun" "How to disable the Autorun functionality in Windows"
Note
The NoDriveAutoRun and NoDriveTypeAutoRun values should only be
modified by system administrators to change the value for the entire
system for testing or administrative purposes. Applications should not
modify these values, as there is no way to reliably restore them to
their original values.
The third way is based on #Alexander Gutenev have pointed out that register a "QueryCancelAutoPlay" window message and then install a global hook from your application to monitor this message.
Note
You should use global hooks only for debugging purposes; otherwise,
you should avoid them. Global hooks hurt system performance and cause
conflicts with other applications that implement the same type of
global hook.
Hooks tend to slow down the system because they increase the amount of
processing the system must perform for each message. You should
install a hook only when necessary, and remove it as soon as possible.
I have an odd problem. I have an application that allows a user to select folders from their Outlook mailboxes/stores, and it's supposed to skip over any folders that are hidden.
To accomplish this, I perform the following check on the folder:
try
{
if (folder.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x10F4000B"))
{
// Yes, it's a hidden folder
return true;
}
}
catch (System.Runtime.InteropServices.COMException comex)
{
// If the hidden property doesn't exist at all, we'll get a COM exception
// here, which is fine.
return false;
}
catch (Exception)
{
// Some other failure
throw;
}
Now, on my test machine, this works perfectly fine. I use MFCMAPI to toggle on/off the hidden attribute, and the application responds appropriately.
On another remote user's machine, ALL of their folders are showing up, even the normally-hidden ones like Quick Step Settings.
I turned up the logging and in the COMException catch() block, I end up seeing things like this:
Checking to see if Quick Step Settings is hidden: NOT HIDDEN
COMException: The property "http://schemas.microsoft.com/mapi/proptag/0x10F4000B" is unknown or cannot be found.
But I walked the user through examining that folder's properties via MFCMAPI and I took screenshots that show that the folder DOES have that hidden flag set to true. So I'm not sure why the application can't see that property...?
The user does make use of mailboxes that are online-only mailbox stores / shared mailboxes, so I'm wondering if there's maybe a different namespace for the property or something under certain circumstances? Can anyone weight in here?
The following path always returns false:
Directory.Exists(#"\\SERVERIP\aFolder\bFolder");
// where SERVERIP is the server-IP which is being accessed using Impersonation
After debugging the code, it places double-slashes in the Debugger.
I have accessed the above file path without the # and double-quotes in WindowsExplorer.
What am I doing wrong?
[ The code will run on a network ]
The problem might be in the paths-[Source/Destinations] (both or one of it[source/destination] might be causing the problem) due to the default-paths used by Visual-Studio. So let me explain how to check wether the paths are correct/incorrect step by step.
Configuring ** SOURCE-PATH **:
Some times this path DRIVE:\ProgramFiles\IISExpress (or some other path depending on the installation location of IIS) gets concatenated with the SOURCE-PATH you give in the input To solve this problem, follow/verify these steps:
Ensure that the SOURCE-PATH or File you are using is in the Project-Folder
To Access the SOURCE-PATH or File. Always use this path/way:
// 1. SOURCE-PATH + fileName with Extension<br>
Server.MapPath("~\FolderInsideProjectFolder\", "fileName.extension");
Configuring ** DESTINATION-PATH (to a Mapped-NETWORK) **:
This path creates a problem if the path you entered has some words mispelled OR if you don't have access to the specified Server-IP[DestinationServerIP]. To solve this problem, follow/verify these steps:
Before Accessing the DESTINATION-PATH or File , ensure that the IP-Address you are referring to is Accessible to the Account under which your Application-code is running.To learn how to run Applications under an Account. See Impersonization
To Access the DESTINATION-PATH or File. Always use this path/way:
// 2. DESTINATION-PATH + fileName with Extension
#"\\SERVERIP\aFolder\bFolder" + "fileName.extension";
NOTE:
Remember that the SOURCE-PATH can be checked if it (exists/does not exist) by addressing its Fully-Qualified-Address and in that case, it will return true if it exists (The full-path that windows-explorer shows you in the Address Bar (Windows-Explorer) like DRIVE:/....../
EXTRA-INFORMATION: (as it was the basic INTENSION)
One line instruction to Copy the file from local-system → networked-mapped drive/path is:
System.IO.File.Copy(
Server.MapPath("~\FolderInsideProjectFolder\", "fileName.extension"),
#"\\SERVERIP\aFolder\bFolder" + "fileName.extension"
[, true ] // Optional if you want the file to be over-written or not
);
Please inform, if any thing still is not cleared (but after some nice searching ☋ ☛ )
Many a times I have seen file (or directory) access problems when the user (a human, system user such as IIS_IUSR or an application) lacks required privileges.
According to this question where the asker is facing similar problem, I believe that this may help you.
Let us know, if it helps.
I need to find a way to guarantee that people using my library have set the requestedExecutionLevel to a minimum of highestAvailable in the application manifest, as follows:
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
</requestedPrivileges>
When they haven't set up the manifest correctly, I want to throw an exception so the developer is aware about this need, without having to read the documentation.
The main question: Is it possible to access this information, if so how?
Instead of checking this setting I already thought about checking the result of this setting. When set to highestAvailable I would expect any user part of the administrators group, to be running as administrator.
This would be possible using:
WindowsPrinciple.IsInRole() to check the currently used role.
Seemingly more difficult to find, a method to check whether a user is in a given group, or more particularly the administrator group. The IsUserInAdminGroup() method listed on the UAC code samples might do the trick.
Your approach is flawed, the dev that uses your library is going to dislike it greatly. The issue is that the manifest isn't the only way to get a process to run elevated. And in fact is almost never the way it is done when you debug code, the Visual Studio Hosting Process is the EXE you run and has the active manifest, without "highestAvailable". The dev will instead elevate Visual Studio itself, the program he starts inherits the security token and will run elevated as well.
What you really want to know is whether the process is elevated when it uses your library and that the user account belongs to the Administrators group. That's rather hard to come by, if the process does not run elevated then the security token is one of a normal user.
You need the code available here. Throw the exception if IsUserInAdminGroup is true and IsProcessElevated is false.
As Hans Passant points out in his answer, the real underlying question - why in this scenario you would want to check the manifest - is:
How to check whether the current user can run the process with elevated privileges?
As suggested in the question, the following would work:
var myPrincipal = new WindowsPrincipal( WindowsIdentity.GetCurrent() );
if ( !myPrincipal.IsInRole( WindowsBuiltInRole.Administrator ) &&
IsUserInAdminGroup() )
{
throw new NotSupportedException( "Some useful comments ..." );
}
The main question is thus, how do you write IsUserInAdminGroup()? The code listed in the UAC self-elevation sample albeit useful, doesn't explain what is going on, and why it is needed.
Hans Passant replied in a comment "Windows emulates a non-elevated process too well, no way to tell from .NET that the user account is in fact an admin account. Falling back to interrogating the token with pinvoke is the workaround used in the linked code.".
In short, you'll need to rely on P/Invoke in order to implement IsUserInAdminGroup(), of which the code can be found in the UAC sample.
More interesting perhaps, is why?
In order to find out I refactored the sample code and incorporated the function into my library. The result in my opinion is a bit more clear. Below you can find the outline, the comments are probably more relevant than the code since it depends on other classes etc ...
Starting from Windows Vista, you have different token types as expressed by TOKEN_ELEVATION_TYPE. Although you can access WindowsIdentity.Token through .NET, this isn't the token we need to check whether someone is administrator. This is a limited token. It has a linked elevated token attached to it, but this isn't exposed in .NET.
Pretty much all the (semi-pseudo) code below does is look up whether there is such an elevated token attached to the original token, and use that to check IsInRole() instead.
// Default token's received aren't impersonation tokens,
// we are looking for an impersonation token.
bool isImpersonationToken = false;
// Open the access token of the current process.
SafeTokenHandle processToken;
if ( !AdvApi32.OpenProcessToken( ..., out processToken ) )
{
MarshalHelper.ThrowLastWin32ErrorException();
}
// Starting from Vista linked tokens are supported which need to be checked.
if ( EnvironmentHelper.VistaOrHigher )
{
// Determine token type: limited, elevated, or default.
SafeUnmanagedMemoryHandle elevationTypeHandle = ...;
if ( !AdvApi32.GetTokenInformation( ... elevationTypeHandle ) )
{
MarshalHelper.ThrowLastWin32ErrorException();
}
var tokenType = (AdvApi32.TokenElevationType)Marshal.ReadInt32(
elevationTypeHandle.DangerousGetHandle() );
// If limited, get the linked elevated token for further check.
if ( tokenType == AdvApi32.TokenElevationType.TokenElevationTypeLimited )
{
// Get the linked token.
SafeUnmanagedMemoryHandle linkedTokenHandle = ...;
if ( !AdvApi32.GetTokenInformation( ... linkedTokenHandle ) )
{
MarshalHelper.ThrowLastWin32ErrorException();
}
processToken = new SafeTokenHandle(
Marshal.ReadIntPtr( linkedTokenHandle.DangerousGetHandle() ) );
// Linked tokens are already impersonation tokens.
isImpersonationToken = true;
}
}
// We need an impersonation token in order
// to check whether it contains admin SID.
if ( !isImpersonationToken )
{
SafeTokenHandle impersonatedToken;
if ( !AdvApi32.DuplicateToken( ..., out impersonatedToken ) )
{
MarshalHelper.ThrowLastWin32ErrorException();
}
processToken = impersonatedToken;
}
// Check if the token to be checked contains admin SID.
var identity= new WindowsIdentity( processToken.DangerousGetHandle() );
var principal = new WindowsPrincipal( identity );
return principal.IsInRole( WindowsBuiltInRole.Administrator );
I'm fairly new to file systems and permissions/rights access (aka Access Control List, ACL). While coding with regards to ACL, I am not able to set properties I want to the files. I'm unsure if my understanding of FileSystemRights members are wrong, or I'm totally doing the wrong thing. (And I'm spending quite some time on this part already)
What I'd like to do is change the rights of a file, so that it can only be soelly readable AND cannot be edited, renamed, deleted and copied elsewhere.
Using the MSDN's example, here's what I have so far:
try
{
//Get current identity
WindowsIdentity self = System.Security.Principal.WindowsIdentity.GetCurrent();
// Add the access control entry to the file.
AddFileSecurity(filename, self.Name, FileSystemRights.Modify, AccessControlType.Deny);
AddFileSecurity(filename, self.Name, FileSystemRights.Write, AccessControlType.Deny);
AddFileSecurity(filename, self.Name, FileSystemRights.ReadAndExecute, AccessControlType.Allow);
// Remove the access control entry from the file.
RemoveFileSecurity(filename, self.Name, FileSystemRights.ReadAndExecute, AccessControlType.Deny);
RemoveFileSecurity(filename, self.Name, FileSystemRights.Read, AccessControlType.Deny);
Console.WriteLine("Done.");
}
catch (Exception e)
{
Console.WriteLine(e);
}
My logic is that:
Add Deny Modify rights (Denying .Modify will cause the file to become unreadable)
Add Deny Write rights
Add Allow ReadAndExecute rights
Remove Deny entry on ReadAndExecute (As .Modify denies ReadAndExecute)
Remove Deny entry on Read (As .Modify denies Read)
Am I doing this part correctly? If not, please advise on what should I do to make the file only readable only and not editable, renamable, deletable and copiable. Many thanks in advance!
Please explain what it is doing wrong. The only thing I see that may be an issue is that you're setting the permissions for yourself, but not the other users, or groups. Perhaps you should iterate through all the groups (admins, users, etc) and disable all but read&execute. Although I think SYSTEM always has full control of all files.