Change Windows Service user programmatically - c#

I need to change Logon user for a Windows service programmatically. And I am using the following code to do that:
string objPath = string.Format("Win32_Service.Name='{0}'", ServiceName);
using (ManagementObject service = new ManagementObject(new ManagementPath(objPath)))
{
object[] wmiParams = new object[11];
if (PredefinedAccount)
{
wmiParams[6] = "LocalSystem";
wmiParams[7] = "";
}
else
{
wmiParams[6] = ServiceUsername; // provided by user
wmiParams[7] = ServicePassword; // provided by user
}
object invokeResult = service.InvokeMethod("Change", wmiParams);
// handle invokeResult - no error up to this point
}
This code works in 90% of situations, but in some situations service cannot be started due to logon failure. There is usually no error on InvokeMetod but when we try to start the service we get the following error:
System.InvalidOperationException: Cannot start service X on computer
'.'. --> System.ComponentModel.Win32Exception: The service did not
start due to a logon failure.
The workaround solution is simple, we just need to enter the same credentials via Windows interface and problem is solved.
So my question is, has anybody experienced the similar problem with ManagementObject because it seems that in some situation it does not relate Username and password to windows service?

It's because the account has no "Log On as service" privilege. You need to use LsaAddAccountRights to add such privilege to the account.

Do you notice any patterns amongst those failures? Same machine? Same OS? Same user? Does the user have "logon as service" or "logon interactively" rights? Personally, I am not familiar with this method of specifying the user for a service. I would have thought you would have to restart the service, but I guess not if it works 90% of the time.

Related

LDAP search fails on server, not in Visual Studio

I'm creating a service to search for users in LDAP. This should be fairly straightforward and probably done a thousand times, but I cannot seem to break through properly. I thought I had it, but then I deployed this to IIS and it all fell apart.
The following is setup as environment variables:
ldapController
ldapPort
adminUsername 🡒 Definitely a different user than the error reports
adminPassword
baseDn
And read in through my Startup.Configure method.
EDIT I know they are available to IIS, because I returned them in a REST endpoint.
This is my code:
// Connect to LDAP
LdapConnection conn = new LdapConnection();
conn.Connect(ldapController, ldapPort);
conn.Bind(adminUsername, adminPassword);
// Run search
LdapSearchResults lsc = conn.Search(
baseDn,
LdapConnection.SCOPE_SUB,
lFilter,
new string[] { /* lots of attributes to fetch */ },
false
);
// List out entries
var entries = new List<UserDto>();
while (lsc.hasMore() && entries.Count < 10) {
LdapEntry ent = lsc.next(); // <--- THIS FAILS!
// ...
}
return entries;
As I said, when debugging this in visual studio, it all works fine. When deployed to IIS, the error is;
Login failed for user 'DOMAIN\IIS_SERVER$'
Why? The user specified in adminUsername should be the user used to login (through conn.Bind(adminUsername, adminPassword);), right? So why does it explode stating that the IIS user is the one doing the login?
EDIT I'm using Novell.Directory.Ldap.NETStandard
EDIT The 'user' specified in the error above, is actually NOT a user at all. It is the AD registered name of the computer running IIS... If that makes any difference at all.
UPDATE After consulting with colleagues, I set up a new application pool on IIS, and tried to run the application as a specified user instead of the default passthrough. Exactly the same error message regardless of which user I set.
Try going via Network credentials that allows you to specify domain:
var networkCredential = new NetworkCredential(userName, password, domain);
conn.Bind(networkCredential);
If that does not work, specify auth type basic (not sure that the default is) before the call to bind.
conn.AuthType = AuthType.Basic;

SSRS Report Portal Management

I am working on an C# utility that would help clients publishing SSRS reports on their sites. Here my code:
public void createFolder()
{
ReportingService2010 rs = new ReportingService2010();
rs.Credentials = System.Net.CredentialCache.DefaultCredentials;
// Create a custom property for the folder.
Property newProp = new Property();
newProp.Name = "Department";
newProp.Value = "Finance";
Property[] props = new Property[1];
props[0] = newProp;
string folderName = "Budget";
try
{
rs.CreateFolder(folderName, "/", props);
Console.WriteLine("Folder created: {0}", folderName);
}
catch (SoapException e)
{
Console.WriteLine(e.Detail.InnerXml);
}
}
I am getting the following error:
ErrorCode xmlns="http://www.microsoft.com/sql/reportingservices">rsAccessDenied</ErrorCode><HttpStatus xmlns="http://www.microsoft.com/sql/reportingservices">400</HttpStatus><Message xmlns="http://www.microsoft.com/sql/reportingservices">The permissions granted to user 'NT AUTHORITY\NETWORK SERVICE' are insufficient for performing this operation.</Message><HelpLink xmlns="http://www.microsoft.com/sql/reportingservices">https://go.microsoft.com/fwlink/?LinkId=20476&EvtSrc=Microsoft.ReportingServices.Diagnostics.Utilities.ErrorStrings&EvtID=rsAccessDenied&ProdName=Microsoft%20SQL%20Server%20Reporting%20Services&ProdVer=13.0.1601.5</HelpLink><ProductName xmlns="http://www.microsoft.com/sql/reportingservices">Microsoft SQL Server Reporting Services</ProductName><ProductVersion xmlns="http://www.microsoft.com/sql/reportingservices">13.0.1601.5</ProductVersion><ProductLocaleId xmlns="http://www.microsoft.com/sql/reportingservices">127</ProductLocaleId><OperatingSystem xmlns="http://www.microsoft.com/sql/reportingservices">OsIndependent</OperatingSystem><CountryLocaleId xmlns="http://www.microsoft.com/sql/reportingservices">1033</CountryLocaleId><MoreInformation xmlns="http://www.microsoft.com/sql/reportingservices"><Source>ReportingServicesLibrary</Source><Message msrs:ErrorCode="rsAccessDenied" msrs:HelpLink="https://go.microsoft.com/fwlink/?LinkId=20476&EvtSrc=Microsoft.ReportingServices.Diagnostics.Utilities.ErrorStrings&EvtID=rsAccessDenied&ProdName=Microsoft%20SQL%20Server%20Reporting%20Services&ProdVer=13.0.1601.5" xmlns:msrs="http://www.microsoft.com/sql/reportingservices">The permissions granted to user 'NT AUTHORITY\NETWORK SERVICE' are insufficient for performing this operation.</Message></MoreInformation><Warnings xmlns="http://www.microsoft.com/sql/reportingservices" />
Any idea?
Please note that a point of this exercise is not just get it work but knowing that it is going to run on a client side ideally without any tweaking on their part.
Thanks for help.
Your question is a little unclear. It sounds like you're not sure why you're getting a permissions error. The answer is that you need to create a new role. I assume (after your comment) that your question is how you can do that using C#.
You can use the CreateRole() method, which will only work with SSRS Native Mode. If SSRS is installed in SharePoint Integrated mode, there is no supported method other than using the UI.
This method throws an OperationNotSupportedSharePointMode exception
when invoked in SharePoint mode

Do Win32_Printer methods work from server against local machine?

I recently made a c# printer management tool that uses a WCF service which contains WMI Win32_Printer methods like AddPrinterConnection and SetDefaultPrinter.
Everything works fine if both the client and the WCF service run on the same machine. But if i try to move the WCF service on another machine, the methods stop working but nothing crashes.
The AddPrinterConnection for example returns 0 which means success but no printer is really added on the local machine. The default printer even if is set to default returns false, etc. The printers list is somehow accesible.
The WCF service behaves the same even if is hosted in IIS with an administrator identity for the application pool or hosted in ASP development server on the server.
But with the same identity on the local machine all is fine. The printer name and local machine address are sent as parameters to the WCF methods.
Is this a rights, delegation or impersonation issue? Is this a limitation of the WMI? Does anyone encountered this problem? I really need a solution for this. Thank you in advance.
EDIT: Here is some code representing the function called on the WCF service that adds a printer.
Note: On both local instalation (client and service local) and remote (client local, service remote) the function returns 0, which means "success" and no error but in the second case nothing is really added.
I tried all kind of impersonations too, and as i said the identity under which the WCF service runs is the same as the user that uses the client. I supose is a thing related to the user context, because it doesn't make much sense. I tried to find some information from Microsoft regarding this but with no luck.
public static string AddPhysicalPrinter(string sPrinterName, string address)
{
try
{
ConnectionOptions options = new ConnectionOptions();
options.Impersonation = ImpersonationLevel.Impersonate;
options.Authentication = AuthenticationLevel.PacketPrivacy;
options.EnablePrivileges = true;
options.Username = "username";
options.Password = "password";
oManagementScope = new ManagementScope(new ManagementPath("\\\\" + address + "\\root" + "\\cimv2"), options);
oManagementScope.Connect();
ManagementClass oPrinterClass = new ManagementClass (new ManagementPath("Win32_Printer"), null);
ManagementBaseObject oInputParameters = oPrinterClass.GetMethodParameters("AddPrinterConnection");
oInputParameters.SetPropertyValue("Name", sPrinterName);
ManagementBaseObject x = oPrinterClass.InvokeMethod("AddPrinterConnection", oInputParameters, null);
foreach(PropertyData p in x.Properties)
{
switch ((UInt32)p.Value)
{
case 0:
return "has been added successfuly";//success
case 5:
return "access denied";//access denied
case 1801:
return "invalid printer name";//invalid printer name
case 1930:
return "incompatible printer driver";//incompatible printer driver
default:
return "unknown error";
}
}
return "unknown error";
}
catch (Exception ex)
{
return "exception caught";
}
}
After some research i found out that it is not possible to do that from a third machine. It must be done with a local logon.

WCF Data Service Update returns 401- Unauthorized: Access is denied due to invalid credentials

I have a WCF data service and I am trying to use the UpdateObject method on the DataServiceContext client. When I call the SaveChanges method, I get the following error page:
Unauthorized: Access is denied due to invalid credentials
You do not have permission to view this directory or page using the credentials that you supplied.
Does anyone have any ideas of how I can fix this? I found this, which would theoretically fix the problem, but setting this disk access is not a realistic solution for a production service. Keep in mind, when running this WCF Data service on my local machine, it works just fine. The C# code for my call is below:
public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
{
var userName = ( string ) context[ "UserName" ];
var isAuthenticated = ( bool ) context[ "IsAuthenticated" ];
if (userName != null && userName.Length >= 1 && collection.Count >= 1)
{
var allNames = string.Empty;
var allValues = string.Empty;
byte[] buf = null;
PrepareDataForSaving( ref allNames, ref allValues, ref buf, true, collection, isAuthenticated );
if (allNames.Length != 0)
{
var client = GetDataContext( );
var profile = client.ProfileViews.Where(p => p.UserName == userName).FirstOrDefault();
if (profile == null)
{
profile = new ProfileView() { UserName = userName };
client.AddToProfileViews(profile);
}
profile.PropertyNames = allNames;
profile.PropertyValuesString = allValues;
profile.PropertyValuesBinary = buf;
profile.LastUpdateDate = DateTime.UtcNow;
client.UpdateObject(profile);
client.SaveChanges( );
}
}
}
I had a similiar issue with a Silverlight application that was using WCF Data Services. The users could view data in the application---SELECTS from the database were functioning---but they were receiving the same "401 - Unauthorized: Access is denied du to invalid credentials." you are describing when they attempted to save changes to the database (i.e., at the point when SaveChanges was executed in code). The underlying problem ended up being a permissions issue with the IIS application folder itself. To fix, we had to grant the Application Pool being used by the WCF service Write permissions to the folder.
Go to IIS and right click on the virtual directory containing the WCF service and choose Manage Application -> Advanced Settings.... Make note of the Application Pool name.
Within same right-click menu, choose Edit Permissions..... On Security tab, check group and user names.
If the name of the Application Pool is missing. Add it using the name format "IIS APPPOOL\MyApplicationPoolName". See this helpful link: http://www.bluevalleytech.com/techtalk/blog/assigning-ntfs-folder-permission-to-iis7-application-pools/
Make sure MyApplicationPoolName has Write permissions (we actually gave it Full control)
In my case I had Windows authentication with impersonation using kerberos (useAppPoolCredentials=true) on a WCF Data Service hosted on IIS 7.5. The weird thing was that I could successfully select and insert data, but when I tried to update I got a 401.3 access denied error. The solution was to give the AD group specified in the web config (myRole):
<authorization>
<allow roles="myRole"/>
<deny users="*" />
</authorization>
Read and write access to the application folder like Dan Sabin said . The error message was:
Error message 401.3: You do not have permission to view this directory or page using the credentials you supplied (access denied due to Access Control Lists). Ask the Web server's administrator to give you access

Updating Active Directory from Web Application Error

I am receiving an error a web based application that allows corporate intranet users to update their active directory details (phone numbers, etc).
The web application is hosted on IIS6 running Windows Server 2003 (SP1). The IIS website is using NTLM Authentication and the website has integrated security enabled. The IIS application pool runs using the “Network Service” account.
The web.config contains the following elements
<LdapConfigurations server="xxx.internal" root="OU=Staff Accounts,DC=xxx,DC=internal" domain="xxx" />
<identify impersonate=”true” />
Active Directory delegation is not needed as the following C# (.NET 3.5) code should pass on the correct impersonation details (including security token) onto Active Directory.
public void UpdateData(string bus, string bus2, string fax, string home, string home2, string mob, string pager, string notes)
{
WindowsIdentity windId = (WindowsIdentity)HttpContext.Current.User.Identity;
WindowsImpersonationContext ctx = null;
try
{
ctx = windId.Impersonate();
DirectorySearcher ds = new DirectorySearcher();
DirectoryEntry de = new DirectoryEntry();
ds.Filter = m_LdapUserFilter;
// i think this is the line causing the error
de.Path = ds.FindOne().Path;
this.AssignPropertyValue(bus, ADProperties.Business, ref de);
this.AssignPropertyValue(bus2, ADProperties.Business2, ref de);
this.AssignPropertyValue(fax, ADProperties.Fax, ref de);
this.AssignPropertyValue(home, ADProperties.Home, ref de);
this.AssignPropertyValue(home2, ADProperties.Home2, ref de);
this.AssignPropertyValue(mob, ADProperties.Mobile, ref de);
this.AssignPropertyValue(pager, ADProperties.Pager, ref de);
this.AssignPropertyValue(notes, ADProperties.Notes, ref de);
// this may also be causing the error?
de.CommitChanges();
}
finally
{
if (ctx != null)
{
ctx.Undo();
}
}
}
private void AssignPropertyValue(string number, string propertyName, ref DirectoryEntry de)
{
if (number.Length == 0 && de.Properties[propertyName].Value != null)
{
de.Properties[propertyName].Remove(de.Properties[propertyName].Value);
}
else if (number.Length != 0)
{
de.Properties[propertyName].Value = number;
}
}
User details can be retrieved from Active Directory without a problem however the issue arises when updating the users AD details. The following exception message is displayed.
System.Runtime.InteropServices.COMException (0x80072020): An operations error occurred.
at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_AdsObject()
at System.DirectoryServices.DirectorySearcher.FindAll(Boolean findMoreThanOne)
at System.DirectoryServices.DirectorySearcher.FindOne()
at xxx.UpdateData(String bus, String bus2, String fax, String home, String home2, String mob, String pager, String notes)
at xxx._Default.btnUpdate_Click(Object sender, EventArgs e)
The code works fine in our development domain but not in our production domain. Can anyone please assist in helping resolving this problem?
This is more than likely a permissions problem - there are numerous articles regards impersonation and delegation and the vagaries thereof here: http://support.microsoft.com/default.aspx?scid=kb;en-us;329986 and here: http://support.microsoft.com/default.aspx?scid=kb;en-us;810572.
It sounds like you might have a duplicate SPN issue?
This is why I think it might be a problem:
It works in your dev enviroment (assuming it is also using network service, and on the same domain)
You have impersonate on, in your web config.
When there is a duplicate SPN, it invalidates the security token, so even though you have created it correctly in code, AD does not "trust" that server to impersonate, so the server that receives the request to make a change to AD (on of your DC's) receives the request but then discards it because Delagation permission has not been applied on the machine account in AD, or SPN issue (either duplicate or incorrect machine name / domain name)
Or at least in my expereince that is 9 out of 10 times the problem.
I guess the problem is that it works on the development environment because when you're launching your webapp there, you run it with your personal account which probably has the rights to write to AD.
On the production environment, you have to assure that the process running your webapp (Network Service Account) has also the rights to update the AD. It sounds to me like that could be the problem since I had a similar issue once.
The problem wasn't with the code but how the server was setup on the domain. For some reason the network administrator did not select the "Trust Computer for Delegation" option in active directory.
Happily the problem was not a "double-hop" issue :)

Categories