How to change DCOM config identity programmatically - c#

Is there any way to get the information about Launching identity of DCOM application programmatically. See the picture attached to understand what i mean.
I tried to use WMI
ManagementObjectSearcher s = new ManagementObjectSearcher(new ManagementScope(#"\\.\root\cimv2"), new ObjectQuery(
"select * from Win32_DCOMApplicationSetting where AppID='{048EB43E-2059-422F-95E0-557DA96038AF}'"))
ManagementObjectCollection dcomSett = s.Get();
var value = dcomSett.Cast<ManagementObject>().ToArray()
[0].Properties["RunAsUser"].Value;
but "RunAsUser" property was empty.
Also tried Interop.COMAdmin
COMAdmin.COMAdminCatalogClass catalog = (COMAdmin.COMAdminCatalogClass)new COMAdmin.COMAdminCatalog();
(COMAdmin.COMAdminCatalogCollection)catalog.GetCollection("Applications")
in this way i managed to get applications which are listed under the "COM+ Applications" node in the "Component Services" snap-in of MMC:
I'm new in COM, DCOM, COM+ stuff and sure that i missed something important.
After a while i found out why i used to get NULL in the first approach (ManagementObject).
You will receive:
NULL if identity is currently set to The launching user
"Interactive User" in case of "The interactive user"
some string with username in case of third option (see the first picture)
But still i need a way to change identity for items like Microsoft PowerPoint Slide under DCOM Config node in MMC.

In the DCOM config, if you are using a specific user for the identity and you want to update the password via code, you need to update it in the Local Security Authority (LSA). This is possible with Windows API calls. MS has some sample code for a utility called dcomperm that does it, and you can see how they implemented in C++. You could make the same calls in C#. See the SetRunAsPassword method here. They are using the method LsaOpenPolicy to get a handle to the policy and calling LsaStorePrivateData to update the password. Then they are adding "login as a batch job" access to the account (but that shouldn't be necessary if you are only changing the password).
This sample code on pinvoke.net looks like it is making the requisite calls, except for the optional part about granting the login as a batch job permission. Note the "key" in the LSA is in the format SCM:{GUID-of-DCOM-object} Example: SCM:{00000000-0000-0000-0000-000000000000}
Oh, and I should mention as an aside that if you wanted to change the RunAs user itself (i.e. the username), you'd need to also update that in the windows registry directly (AFAIK that's the only way to do it). DCOM entries are stored under HKLM\SOFTWARE\Classes\AppID. You can do that with WMI or just use the Registry classes in .NET.

This is very simple , you can get APPId from
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{048EB43E-2059-422F-95E0-557DA96038AF}
using
(RegistryKey dcomPPTIdentity = Registry.LocalMachine.OpenSubKey("Software\\Classes\\AppID\\{048EB43E-2059-422F-95E0-557DA96038AF}"))
{
if (dcomPPTIdentity != null)
{
Registry.SetValue(dcomPPTIdentity.ToString(), "RunAs", "userName");
}
}

I am using COMAdmin DLL successfully. Try something like this:
COMAdminCatalog catalog = new COMAdminCatalog();
COMAdminCatalogCollection applications = catalog.GetCollection("Applications");
applications.Populate();
for (int i = 0; i < applications.Count; i++)
{
COMAdminCatalogObject application = COMAppCollectionInUse.Item[i];
if (application.Name == "Your COM+ application name")
{
application.Value["Identity"] = "nt authority\\localservice"; // for example
}
}

This works for me on my development server. Keep in mind, it is run against the server directly on the server
using COMAdmin;
using System;
namespace ComComponents
{
class Program
{
static void Main(string[] args)
{
COMAdminCatalog catalog = new COMAdminCatalog();
COMAdminCatalogCollection applications = catalog.GetCollection("Applications");
applications.Populate();
for (int i = 0; i < applications.Count; i++)
{
COMAdminCatalogObject application = applications.Item[i];
Console.WriteLine(application.Name);
Console.WriteLine(application.Value["Identity"]);
}
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
}

Related

How do I programmatically give ownership of a Registry Key to Administrators?

I ran into a bizarre issue when I upgraded some machines to Windows 10 where incorrect permissions on RuntimeBroker caused problems. I found a solution online that recommended changing permissions (first in the registry, then in DCOM configuration), and I'm trying to write a small .NET application to automate the process.
Presently the owner of the relevant registry keys is NT SERVICE\TrustedInstaller and I'm trying to change it to COMPUTER\Administrators. I have a simple WPF application with the requestedExecutionLevel set to "requireAdministrator," but I'm still running into problems. Here's a snippet of code to illustrate the problem:
using System.Security.AccessControl;
using System.Security.Principal;
using Microsoft.Win32;
namespace PermissionFixer
{
public class Fixer
{
public void Fix()
{
var subKey = Registry.ClassesRoot.OpenSubKey(#"AppID\{9CA88EE3-ACB7-47c8-AFC4-AB702511C276}", true);
if (subKey != null)
{
var admins = new NTAccount("Administrators");
var ac = subKey.GetAccessControl();
ac.SetOwner(admins);
ac.AddAccessRule(new RegistryAccessRule(admins, RegistryRights.FullControl, AccessControlType.Allow));
subKey.SetAccessControl(ac);
}
}
}
}
The trouble is that it doesn't even get past the call to OpenSubKey() before hitting a SecurityException that says "Requested registry access is not allowed." I think that's because Administrators doesn't yet have the access (remember it belongs to TrustedInstaller), but it becomes a bit of a chicken and egg problem. The strange thing is that when I use regedit by hand I am allowed to change the owner to Administrators, and I'm pretty sure my instance of regedit is running as Administrators.
How can I get this working in .NET?
I figured it out, and fortunately it is possible to achieve with the .NET classes. Here is how you have to call OpenSubKey:
var subKey = Registry.ClassesRoot.OpenSubKey(#"AppID\{9CA88EE3-ACB7-47c8-AFC4-AB702511C276}", RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership);
Then you have to nix the call to AddAccessRule()... you can't modify that until you have ownership; and you have to do those two operations in serial. So take ownership first, then re-open the key with different access rights to add the access rule.
EDIT: I discovered today that you must first manipulate the token with which your application is running, by hooking into P/Invoke calls. I found a class called TokenManipulator referenced in another Stack Overflow question. Include that class in your project, and then grant Backup, Restore, and TakeOwnership privileges to your token before calling OpenSubKey. So your method will end up looking something like this:
try
{
TokenManipulator.AddPrivilege("SeRestorePrivilege");
TokenManipulator.AddPrivilege("SeBackupPrivilege");
TokenManipulator.AddPrivilege("SeTakeOwnershipPrivilege");
var subKey = Registry.ClassesRoot.OpenSubKey(#"AppID\{9CA88EE3-ACB7-47c8-AFC4-AB702511C276}", RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership);
// code to change owner...
}
finally
{
TokenManipulator.RemovePrivilege("SeRestorePrivilege");
TokenManipulator.RemovePrivilege("SeBackupPrivilege");
TokenManipulator.RemovePrivilege("SeTakeOwnershipPrivilege");
}

SharePoint Event Reciever Works On One Machine/computer/user

So we have created an updated version of a WSP for SharePoint 2010 due to our migration/update from 2007 to 2010.
The WSP is a event handler/reciever for ItemAdded() and we have it working as intended. Issue is that the operation seems to only work for one computer/machine and no others.
When the Item is Added to a list the WSP creates a Folder in Shared Documents library, creates a wiki page, then updates the new List Item with links to the Shared Doc and Wiki.
When triggered by Machine #1 and User #1 all operations work, when Machine #2(M2) and user #2(U2) or M3 and U3 non of the tasks take place when a new Item is created.
User #2 can log in on M1 and create a new item and all operations work. But if U1 uses M2 or M3 to create an item the events don't trigger. Machine #1 is able to trigger the event as many times as they want but no other computer is able to.
If you were able to follow is it something with the code or some sort of cache setting on the local machine or the SP server, or something else? Any help is appreciated.
Update: All machines are on the same network. Non of the machines are the server but various personal laptops. Development was done on a separate machine. All are accessing via the same URL. All users have same access. This is on our test site currently which would be switched to being production once migration/upgrade takes place.
Before current .WSP deployment we noticed the same issue but it was reverse, Machine #2 did all the updates but Machine #1 and #3 couldn't. Only thing we can think of was that those machines were the first to trigger the event after deployment.
I'm Not doing the .WSP install but our IT guy is(won't let us have access :/ but I understand) but below is the install commands he is running.
Add-SPSolution -LiteralPath "OurPath/ourFile.wsp"
Install-SPSolution -Identity ourIdentity -WebApplication http://myhost.com/ -GACDeployment
Below is the main part of the code
public class CreateWikiAndFolder : Microsoft.SharePoint.SPItemEventReceiver
{
public override void ItemAdded(SPItemEventProperties properties)
{
try
{
//this.DisableEventFiring();
base.EventFiringEnabled = false;
string sUrlOfWikiPage = string.Empty;
string sUrlOfNewFolder = string.Empty;
string sSubsiteRUL = string.Empty;
string sCurrentItemTitle = properties.ListItem["Title"].ToString();
string sWikiListName = "TR Wikis";
string sDocLibName = "Shared Documents";
string sTRListID = "TR Status";
if (sTRListID.ToUpper().Equals(properties.ListTitle.ToString().ToUpper()))
{
//Create the Folder
sUrlOfNewFolder = CreateFolder(properties.ListItem.Web, sDocLibName, sCurrentItemTitle);
//Create the Wiki
string ItemDispFormUrl = String.Concat(properties.ListItem.Web.Url, "/", properties.ListItem.ParentList.Forms[PAGETYPE.PAGE_DISPLAYFORM].Url, "?ID=", properties.ListItem.ID.ToString());
sUrlOfWikiPage = CreateWiki(properties.ListItem.Web, sWikiListName, sCurrentItemTitle, ItemDispFormUrl, sUrlOfNewFolder);
//Update the current TR Item
SPWeb myWeb = properties.ListItem.Web;
myWeb.AllowUnsafeUpdates = true;
SPListItem myListItem = properties.ListItem;
SPFieldUrlValue shareFolderURLValue = new SPFieldUrlValue();
shareFolderURLValue.Description = "Shared Folder";
shareFolderURLValue.Url = sUrlOfNewFolder ;
myListItem["SharedFolder"] = shareFolderURLValue;
myListItem.Update();
myWeb.AllowUnsafeUpdates = false;
}
base.EventFiringEnabled = true;
}
catch (Exception e)
{
//Currently throwing nothing
}
}
}
It could be a hardcoded path/url, however there is not enough information to identify the problem, I would be glad to update my answer with a more detailed theory if you provide more details or if you share some of your code.
Figured out the issue. I didn't include them with the above file code. But we were StreamWriting to a text file on the server to help us with debugging. Issue was with that, When user 1 was logged on their machine and the log files didn't exist, they would get generated. Now no other users then had read/write access to those files and so it errored out at our debug files for anyone else. But that Windows user could run it as much as they wanted as they were the owner of the file :/

How can I find the SID for an app pool via Microsoft.Web.Administration?

I am working on a configuration tool that needs to set some permissions on a directory based on the identity that a specific web application runs under. The original code simply built the login name based on IIS APPPOOL\<ApppoolName> or based on the well know SID if it was a built in account.
Some similar code failed in a localized environment so I am now trying to get rid of the baked in string.
My solution was this:
public static SecurityIdentifier GetApplicationPoolSid(string name)
{
ApplicationPool pool = Manager.ApplicationPools[name];
if (pool != null)
{
var sddlForm = pool.GetAttributeValue("applicationPoolSid") as string;
if (!String.IsNullOrEmpty(sddlForm))
return new SecurityIdentifier(sddlForm);
}
return null;
}
The problem is that I found "applicationPoolSid" by poking around in the debugger and I cannot find any documentation that says that I am not taking advantage of an undocumented implementation feature that will go away in the future. (This means it won't pass code review.)
I would love to know the approved way of doing what I am looking at here. I would also be happy to know that IIS APPPOOL\<ApppoolName> is guaranteed to never be localized so then we could go back to the old way.
I can find no reference to IIS APPPOOL being localized, which would make sense since neither IIS nor APPPOOL are words in any language, so there's nothing to translate to. Although they are acronyms in English and possibly other languages, I think they are still called IIS and AppPool even in other languages.
You can, by the way, get the SID officially like this:
NTAccount f = new NTAccount(#"IIS AppPool\DefaultAppPool");
SecurityIdentifier s = (SecurityIdentifier)f.Translate(typeof(SecurityIdentifier));
String sidString = s.ToString();
You don't even really need the SID if you're going to pass it to FileSystemAccessRule(), since it has an overload that takes an IdentityReference, which NTAccount is. Just pass it the NTAccount.
This will get you the SID, but does not use the function you asked about and is written in powershell not C#. I am posting this to hopefully assist in getting on the right track regarding the generation using SHA1 rather than actually querying from Windows. Also some systems do not have some SID and user account modules installed so this can be used in those situations.
Function to generate SID based off apppool username (explanation in link at the bottom)
function Get-SID ([String]$winver, [String]$username) {
$sidPrefix = switch ($winver) {
'6.0' { 'S-1-5-80' }
default { 'S-1-5-82' }
}
$userToString = switch ( $sidPrefix ) {
'S-1-5-82' { $username.ToLower() }
default { $username.ToUpper() }
}
$userBytes = [Text.Encoding]::Unicode.GetBytes($userToString)
$sha = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider
$hash = $sha.ComputeHash($userBytes)
$sid = $sidPrefix
for ( $i=0; $i -lt 5; $i++ ) {
$sid += '-' + [BitConverter]::ToUInt32($hash, $i*4)
}
return $sid
}
Example usage
Get-SID -winver "6.0" -username "DefaultAppPool"
User 6.0 for Vista or equivalent release, use 6.1/6.2 etc for after that.
Partial credit to Tomas Restrepo from winterdom.com for the powershell, although that script does not actually work because some functions are not included in the code.

Permission For writing to LOCAL_MACHINE

I made an app that allows windows users to spoof Mac Address .
It works by adding "NetworkAdapter": "00ff00ff00ff" key/value pair to registry of the users selected nic.
The problem is that every time the app tries to make changes to windows registry Windows pop's up a warning dialog, e.g.:
but clicking continue will add the registry values successfully and the app functions normally.
What can i do/or add changes in my code to make the dialog box disappear or can i do it in a better way?
The app requires Admin Privileges
here's the git repo of the app
here's the method:
public void SetMac(string macAddress)
{
const string Name = #"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002bE10318}";
using (RegistryKey key0 = Registry.LocalMachine.OpenSubKey(Name, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.FullControl))
{
string[] x = key0.GetSubKeyNames();
foreach (string name in x)
{
var var1 = Registry.LocalMachine.OpenSubKey(Name,RegistryKeyPermissionCheck.ReadWriteSubTree,RegistryRights.FullControl);
var v = var1.OpenSubKey(name, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.FullControl);
var z = v.GetValue("DriverDesc");
if (comboBox1.Text == z.ToString() )
{
v.SetValue("NetworkAddress",comboBox2.Text);
MessageBox.Show(z.ToString());
}
v.Close();
var1.Close();
}
key0.Close();
}
}
You need to run your app under elevated privileges, see Requested registry access is not allowed.
The problem here is that the user does not have permission to open the target key for writing. As abatishchev has already suggested, you need to run the application elevated so that the user actually has Administrators group membership when the code is executed.
The reason that this looks like a CAS permission error is a design flaw in the RegistryKey.OpenSubKey method. It ought to throw an UnauthorizedAccessException when the target key cannot be opened for writing due to inadequate user permissions, but it actually throws a SecurityException instead. The problem ends up appearing to be due to insuffience CAS permissions when it is really the user, not the code, that lacks permissions to edit the key.

Use C# to interact with Windows Update

Is there any API for writing a C# program that could interface with Windows update, and use it to selectively install certain updates?
I'm thinking somewhere along the lines of storing a list in a central repository of approved updates. Then the client side applications (which would have to be installed once) would interface with Windows Update to determine what updates are available, then install the ones that are on the approved list. That way the updates are still applied automatically from a client-side perspective, but I can select which updates are being applied.
This is not my role in the company by the way, I was really just wondering if there is an API for windows update and how to use it.
Add a Reference to WUApiLib to your C# project.
using WUApiLib;
protected override void OnLoad(EventArgs e){
base.OnLoad(e);
UpdateSession uSession = new UpdateSession();
IUpdateSearcher uSearcher = uSession.CreateUpdateSearcher();
uSearcher.Online = false;
try {
ISearchResult sResult = uSearcher.Search("IsInstalled=1 And IsHidden=0");
textBox1.Text = "Found " + sResult.Updates.Count + " updates" + Environment.NewLine;
foreach (IUpdate update in sResult.Updates) {
textBox1.AppendText(update.Title + Environment.NewLine);
}
}
catch (Exception ex) {
Console.WriteLine("Something went wrong: " + ex.Message);
}
}
Given you have a form with a TextBox this will give you a list of the currently installed updates. See http://msdn.microsoft.com/en-us/library/aa387102(VS.85).aspx for more documentation.
This will, however, not allow you to find KB hotfixes which are not distributed via Windows Update.
The easiest way to do what you want is using WSUS. It's free and basically lets you setup your own local windows update server where you decide which updates are "approved" for your computers. Neither the WSUS server nor the clients need to be in a domain, though it makes it easier to configure the clients if they are. If you have different sets of machines that need different sets of updates approved, that's also supported.
Not only does this accomplish your stated goal, it saves your overall network bandwidth as well by only downloading the updates once from the WSUS server.
If in your context you're allowed to use Windows Server Update Service (WSUS), it will give you access to the Microsoft.UpdateServices.Administration Namespace.
From there, you should be able to do some nice things :)
P-L right. I tried first the Christoph Grimmer-Die method, and in some case, it was not working. I guess it was due to different version of .net or OS architecture (32 or 64 bits).
Then, to be sure that my program get always the Windows Update waiting list of each of my computer domain, I did the following :
Install a serveur with WSUS (may save some internet bandwith) : http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=5216
Add all your workstations & servers to your WSUS server
Get SimpleImpersonation Lib to run this program with different admin right (optional)
Install only the administration console component on your dev workstation and run the following program :
It will print in the console all Windows updates with UpdateInstallationStates.Downloaded
using System;
using Microsoft.UpdateServices.Administration;
using SimpleImpersonation;
namespace MAJSRS_CalendarChecker
{
class WSUS
{
public WSUS()
{
// I use impersonation to use other logon than mine. Remove the following "using" if not needed
using (Impersonation.LogonUser("mydomain.local", "admin_account_wsus", "Password", LogonType.Batch))
{
ComputerTargetScope scope = new ComputerTargetScope();
IUpdateServer server = AdminProxy.GetUpdateServer("wsus_server.mydomain.local", false, 80);
ComputerTargetCollection targets = server.GetComputerTargets(scope);
// Search
targets = server.SearchComputerTargets("any_server_name_or_ip");
// To get only on server FindTarget method
IComputerTarget target = FindTarget(targets, "any_server_name_or_ip");
Console.WriteLine(target.FullDomainName);
IUpdateSummary summary = target.GetUpdateInstallationSummary();
UpdateScope _updateScope = new UpdateScope();
// See in UpdateInstallationStates all other properties criteria
_updateScope.IncludedInstallationStates = UpdateInstallationStates.Downloaded;
UpdateInstallationInfoCollection updatesInfo = target.GetUpdateInstallationInfoPerUpdate(_updateScope);
int updateCount = updatesInfo.Count;
foreach (IUpdateInstallationInfo updateInfo in updatesInfo)
{
Console.WriteLine(updateInfo.GetUpdate().Title);
}
}
}
public IComputerTarget FindTarget(ComputerTargetCollection coll, string computername)
{
foreach (IComputerTarget target in coll)
{
if (target.FullDomainName.Contains(computername.ToLower()))
return target;
}
return null;
}
}
}

Categories

Resources