I have a requirement to read and display the owner of a file (for audit purposes), and potentially changing it as well (this is secondary requirement). Are there any nice C# wrappers?
After a quick google, I found only the WMI solution and a suggestion to PInvoke GetSecurityInfo
No need to P/Invoke. System.IO.File.GetAccessControl will return a FileSecurity object, which has a GetOwner method.
Edit: Reading the owner is pretty simple, though it's a bit of a cumbersome API:
const string FILE = #"C:\test.txt";
var fs = File.GetAccessControl(FILE);
var sid = fs.GetOwner(typeof(SecurityIdentifier));
Console.WriteLine(sid); // SID
var ntAccount = sid.Translate(typeof(NTAccount));
Console.WriteLine(ntAccount); // DOMAIN\username
Setting the owner requires a call to SetAccessControl to save the changes. Also, you're still bound by the Windows rules of ownership - you can't assign ownership to another account. You can give take ownership perms, and they have to take ownership.
var ntAccount = new NTAccount("DOMAIN", "username");
fs.SetOwner(ntAccount);
try {
File.SetAccessControl(FILE, fs);
} catch (InvalidOperationException ex) {
Console.WriteLine("You cannot assign ownership to that user." +
"Either you don't have TakeOwnership permissions, or it is not your user account."
);
throw;
}
FileInfo fi = new FileInfo(#"C:\test.txt");
string user = fi.GetAccessControl().GetOwner(typeof(System.Security.Principal.NTAccount)).ToString();
Related
I am uploading / creating file on Google Drive using .NET SDK for google drive api. Everything works fine and I can give permission to user as per my business logic like writer,reader,commenter or owner. But I want to hide the Share button from everybody except Owner as my business logic should decide which file should be shared with whom and when.
Here is the code for sharing the document:
try
{
Google.Apis.Drive.v2.Data.Permission permission = new Google.Apis.Drive.v2.Data.Permission();
switch (role)
{
case GoogleRoles.WRITER:
case GoogleRoles.READER:
case GoogleRoles.OWNER:
{
permission.Role = role;
permission.Value = userEmail;
permission.Type = "user";
break;
}
case GoogleRoles.COMMENTER:
{
permission.Role = GoogleRoles.READER; //Need to assign role before we assign the additional role of commenter.
List<String> additionalRoles = new List<string>();
additionalRoles.Add(GoogleRoles.COMMENTER);
permission.AdditionalRoles = additionalRoles;
permission.Type = "user";
permission.Value = userEmail;
break;
}
}
PermissionsResource.InsertRequest insertRequest = DriveService.Permissions.Insert(permission, fileId);
insertRequest.SendNotificationEmails = true;
insertRequest.Execute();
Where DriveService is an instance of service account. Any pointer would be a great help.
Unfortunately the Drive API doesn't yet support the feature of disabling sharing or disabling downloading. Please file a feature request here: https://code.google.com/a/google.com/p/apps-api-issues/issues/entry?template=Feature%20request&labels=Type-Enhancement,API-Drive
I had raised this as an enhancement, and got the response too. So in Google drive API its not part of permission but these are properties of file itself, so we need to set he properties instead of permissions like:
File.LabelsData labels = new File.LabelsData();
labels.Restricted = true;
File body = new File();
body.Labels = labels;
body.WritersCanShare = false;
It has solved the issue of Share but download issue is not solved it by above changes. More details about this can be found at https://developers.google.com/drive/v2/reference/files
I am trying to take ownership of a file and delete it via C#.
The file is iexplorer.exe, current owner by default - TrustedInstaller.
The method FileSecurity.SetOwner seems to set the specified ownership, but actually doesn't change the initial owner and throws no exception.
Obviously, the next attempt to delete the file throws an exception.
What should be changed in the code to take ownership of the file and delete it ?
var fileS = File.GetAccessControl(#"C:\Program Files (x86)\Internet Explorer\iexplore.exe");
fileS.SetOwner(new System.Security.Principal.NTAccount(Environment.UserDomainName, Environment.UserName));
File.Delete(#"C:\Program Files (x86)\Internet Explorer\iexplore.exe");
You must explicitly enable SeTakeOwnershipPrivilege:
Required to take ownership of an object without being granted
discretionary access. This privilege allows the owner value to be set
only to those values that the holder may legitimately assign as the
owner of an object. User Right: Take ownership of files or other
objects.
I suggest you to read the great article written by Mark Novak: Manipulate Privileges in Managed Code Reliably, Securely, and Efficiently.
And/or take a look at his sample.
Update
Example usage:
var fileS = File.GetAccessControl(#"C:\Program Files (x86)\Internet Explorer\iexplore.exe");
Privilege p;
bool ownerChanged = false;
try
{
p = new Privilege(Privilege.TakeOwnership);
p.Enable();
fileS.SetOwner(new System.Security.Principal.NTAccount(
Environment.UserDomainName, Environment.UserName));
ownerChanged = true;
}
catch(PrivilegeNotHeldException e)
{
// privilege not held
// TODO: show an error message, write logs, etc.
}
finally
{
p.Revert();
}
if (ownerChanged)
File.Delete(#"C:\Program Files (x86)\Internet Explorer\iexplore.exe");
string filepath = #"C:\Program Files (x86)\Internet Explorer\iexplore.exe";
//Get Currently Applied Access Control
FileSecurity fileS = File.GetAccessControl(filepath);
//Update it, Grant Current User Full Control
SecurityIdentifier cu = WindowsIdentity.GetCurrent().User;
fileS.SetOwner(cu);
fileS.SetAccessRule(new FileSystemAccessRule(cu, FileSystemRights.FullControl, AccessControlType.Allow));
//Update the Access Control on the File
File.SetAccessControl(filepath, fileS);
//Delete the file
File.Delete(filepath);
Add the following imports
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;
Run the Code in Elevated Mode.
Powered in Windows 8.1 using class Privilege from example:
Manipulate Privileges in Managed Code Reliably, Securely, and Efficiently
private bool TryDeleteFile(string fileName)
{
string filePath = Path.GetFullPath(fileName);
var fi = new FileInfo(filePath);
bool ownerChanged = false;
bool accessChanged = false;
bool isDelete = false;
FileSecurity fs = fi.GetAccessControl();
Privilege p = new Privilege(Privilege.TakeOwnership);
try
{
p.Enable();
fs.SetOwner(WindowsIdentity.GetCurrent().User);
File.SetAccessControl(filePath, fs); //Update the Access Control on the File
ownerChanged = true;
}
catch (PrivilegeNotHeldException ex) { }
finally { p.Revert(); }
try
{
fs.SetAccessRule(new FileSystemAccessRule(WindowsIdentity.GetCurrent().User, FileSystemRights.FullControl, AccessControlType.Allow));
File.SetAccessControl(filePath, fs);
accessChanged = true;
}
catch (UnauthorizedAccessException ex) { }
if (ownerChanged && accessChanged)
{
try
{
fi.Delete();
isDelete = true;
}
catch (Exception ex) { }
}
return isDelete;
}
See these registry entries for adding a context menu. I was able to rename the folder as well as iexplorer_OFF.exe on Windows 7.
You can probably shell/execute the same from your code.
https://www.howtogeek.com/howto/windows-vista/add-take-ownership-to-explorer-right-click-menu-in-vista/
Having an issue with a Windows service that needs to monitor/have access to a set of folders, and move files around between those folders.
There's have a bit of boilerplate code that's been used in the past, which will check a given folder for the specific granular permissions for the given user. The odd thing is that I discovered through testing that if I manually deny all permissions on that folder for the account the service is running under, and then run the code, it reports that all is well and the user does in fact have those permissions, even though it's obvious (and demonstrable) that he doesn't.
At first I thought this might be because the service was running under the local System account, but the same issue crops up if it is run with NetworkService as well as with a local user account. This is on Windows 7/2008 R2.
Boilerplate method:
public static void ValidateFolderPermissions(WindowsIdentity userId, string folder, FileSystemRights[] requiredAccessRights)
{
SecurityIdentifier secId;
StringBuilder sb = new StringBuilder();
bool permissionsAreSufficient = false;
bool notAuthorized = false;
String errorMsg = String.Empty;
IdentityReferenceCollection irc = userId.Groups;
foreach (IdentityReference ir in irc)
{
secId = ir.Translate(typeof(SecurityIdentifier)) as SecurityIdentifier;
try
{
DirectoryInfo dInfo = new DirectoryInfo(folder);
DirectorySecurity dSecurity = dInfo.GetAccessControl();
AuthorizationRuleCollection rules = dSecurity.GetAccessRules(true, true, typeof(SecurityIdentifier));
foreach (FileSystemAccessRule ar in rules)
{
if (secId.CompareTo(ar.IdentityReference as SecurityIdentifier) == 0)
{
sb.AppendLine(ar.FileSystemRights.ToString());
foreach (FileSystemRights right in requiredAccessRights)
{
if (right == ar.FileSystemRights)
{
permissionsAreSufficient = true;
break;
}
}
}
}
}
catch (UnauthorizedAccessException)
{
notAuthorized = true;
errorMsg = "user not authorized";
}
catch (SecurityException)
{
// If we failed authorization do not update error
if (!notAuthorized)
errorMsg = "security error";
}
catch (Exception)
{
// If we failed authorization do not update error
if (!notAuthorized)
errorMsg = "invalid folder or folder not accessible";
}
}
if (!permissionsAreSufficient)
{
if (!String.IsNullOrEmpty(errorMsg))
throw new Exception(String.Format("User {0} does not have required access to folder {1}. The error is {2}.", userId.Name, folder, errorMsg));
else
throw new Exception(String.Format("User {0} does not have required access rights to folder {1}.", userId.Name, folder));
}
}
And the calling snippet:
FileSystemRights[] requireAccessRights =
{
FileSystemRights.Delete,
FileSystemRights.Read,
FileSystemRights.FullControl
};
try
{
FolderPermissionValidator.ValidateFolderPermissions(WindowsIdentity.GetCurrent(), inputFolder, requireAccessRights);
Log.Debug("In ServiceConfigurationValidator: {0}, {1}", WindowsIdentity.GetCurrent().Name, inputFolder);
}
catch (Exception ex)
{
Log.Debug("Throwing exception {0}", ex.Message);
}
I don't see anything in ValidateFolderPermissions to check for denials before checking for allowed permissions. If a deny entry prevents access then no amount of allow entries can override it.
This code enumerates the entries in the ACL as FileSystemAccessRule objects, but doesn't bother to check whether AccessControlType is allow or deny.
I also note that the logic returns true if any ACE exactly matches any of the elements of the requiredAccessRights array; I suspect the intended behaviour is that it return true if all of the specified rights are present. This could cause false positives if only some of the requested rights are present, but because it only looks for exact matches it could also cause a false negative, e.g., if the ACE actually gives more rights than are being requested. (Not such a problem in the example given, though, because you're asking for Full Control.)
Another flaw is that it only checks for access entries matching groups the user belongs to; access entries for the user account itself will be ignored. (I'm not sure what the behaviour of WindowsIdentity.Groups is for security primitives such as SYSTEM and NetworkService that are not actual user accounts, although it sounds like that part was working as desired.)
Note that because it is very hard to cope properly with all the possible situations (consider, e.g., an access control entry for Everyone, or for SERVICE) it would be wise to allow the administrator to override the check if it is mistakenly reporting that the account doesn't have the necessary access.
public Object IsAuthenticated()
{
String domainAndUsername = strDomain + "\\" + strUser;
***DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, strPass);***
SearchResult result;
try
{
//Bind to the native AdsObject to force authentication.
DirectorySearcher search = new DirectorySearcher(entry) { Filter = ("(SAMAccountName=" + strUser + ")") };
search.PropertiesToLoad.Add("givenName"); // First Name
search.PropertiesToLoad.Add("sn"); // Last Name
search.PropertiesToLoad.Add("cn"); // Last Name
result = search.FindOne();
if (null == result)
{
return null;
}
//Update the new path to the user in the directory.
_path = result.Path;
_filterAttribute = (String)result.Properties["cn"][0];
}
catch (Exception ex)
{
return new Exception("Error authenticating user. " + ex.Message);
}
return user;
}
In the above code segment, is there a way to retrieve the user's Windows login password so that the LDAP authentication works without asking the user his password another time?
Can the value for "strPass",that is being passed when DirectoryEntry object is being created, be retrieved by any way?
The password does not exist anywhere. It would be a big security hole if it did.
Also, BTW, get rid of the try/catch block. It's doing nothing but hiding the reason for the exception.
Use the ActiveDirectoryMemebershipProvider - You can authenticate without writing code, thus eliminating the login scenario you currently have.
You could set up Windows Authentication in your ASP.NET app.
http://msdn.microsoft.com/en-us/library/ff647405.aspx
Once you set this up, only authenticated users have access to the protected parts of your site.
This gives you access some key bits of information.
For example:
System.Web.HttpContext.Current.User.Identity.Name - gives the name (domain\username) of an authenticated user.
System.Web.HttpContext.Current.User.IsInRole("role_name_here") - will tell you if the authenticated user is in a given role.
Authentication can be tricky to do for the first time - please do not ask a user for their windows password - this is a security risk - allow IIS and the .NET framework take care of this for you. The article above may be a bit long and seem a bit complicated but it has a lot of good information in it.
I posted a question re LDAP account management, but after exploring this, it's not what i'm after. I've managed to find two ways of creating users on a machine, and i find one is much neater than the other, however, i am uncertain how to convert the first option over to the second option entirely.
This was my first solution:
Process MyProc = new Process();
MyProc.StartInfo.WorkingDirectory = System.Environment.SystemDirectory;
MyProc.StartInfo.FileName = "net.exe";
MyProc.StartInfo.UseShellExecute = false;
MyProc.StartInfo.RedirectStandardError = true;
MyProc.StartInfo.RedirectStandardInput = true;
MyProc.StartInfo.RedirectStandardOutput = true;
MyProc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
MyProc.StartInfo.Arguments = string.Format(#" user {0} {1} /ADD /ACTIVE:YES /EXPIRES:NEVER /FULLNAME:{0}"" /PASSWORDCHG:NO /PASSWORDREQ:YES", username, password);
MyProc.Start();
MyProc.WaitForExit();
int exit = MyProc.ExitCode;
MyProc.Close();
return exit == 0;
And this was my second (preffered) solution:
DirectoryEntry root = GetDELocalRoot();
DirectoryEntry user = root.Children.Add(username, "user");
//TODO: Always Active
//TODO: Never Expires
//TODO: No Password Change
//TODO: Password Required
user.Properties["description"].Value = "Account for running the MicaService and handling updates.";
user.Invoke("SetPassword", new object[] { password });
user.CommitChanges();
user.Close();
I would like to map all the settings in my TODO: from the first solution into my second neater solution.
I have tried the following line as well:
user.Properties["userAccountControl"].Value = ADS_USER_FLAG.ADS_UF_NORMAL_ACCOUNT | ADS_USER_FLAG.ADS_UF_PASSWD_CANT_CHANGE | ADS_USER_FLAG.ADS_UF_DONT_EXPIRE_PASSWD;
But this does not work as the property does not exist in cache.
NOTE: the GetDELocalRoot() = return new DirectoryEntry("WinNT://" + Environment.MachineName);
Thanks for any input!
Regards
Tris
Check out my friend Richard Mueller's web site which has lots of useful information and reference material on what those two providers - WinNT for local machine accounts vs. LDAP for network accounts - have to offer.
There's also a Excel sheeet with all attributes that the WinNT provider exposes - it's a lot less than what the LDAP provider has, so I'm not sure if you'll be able to set all the properties you're looking for.
Marc