Win service to install another win service (programmatically in C#?) - windows

I have a win service running under the system session. I want this main service to control (install/remove) another winservice to be installed under another user (we main service have the user/password).
How to do that?

What you are intending to do sounds a bit hackish, but if you really want to do it then you can by using the sc.exe utility that is bundled with Windows. All you have to do is make sure the correct service files are in place on the file system (for example under %PROGRAMFILES%\[CompanyName]\[ServiceName]), then use Process.Start to invoke sc.exe with the right command line arguments.
To specify the name and password for the account the service should run under, use the obj= <account name> and password= <password> options. Note the space between the option and its value - without that space the command will fail.
Another option is to use Process.Start() to invoke installutil.exe (which is part of the .Net framework). A quick example of this is:
var installutil = Environment.GetFolderPath(Environment.SpecialFolder.Windows)
+ "\\Microsoft.Net\\Framework\\v4.0.30319\\installutil.exe";
var arguments = string.Format( " /ServiceName=\"{0}\" /DisplayName=\"{1}\" \"{2}\" ",
serviceName,
displayName,
servicePath);
var psi = new ProcessStartInfo(installutil, arguments) {
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardInput = false,
RedirectStandardError = false,
ErrorDialog = false,
UseShellExecute = false
};
var p = new Process { StartInfo = psi, EnableRaisingEvents = true };
p.Start();
p.WaitForExit();

Seems like all I need in my main win service to call:
//Installs and starts the service
ServiceInstaller.InstallAndStart("MyServiceName", "MyServiceDisplayName", "C:\PathToServiceFile.exe");
And make sure inside the target (the one I want to control/install) Service code (ServiceInstaller) the right Installer:
public class MyServiceInstaller : Installer
{
private ServiceInstaller serviceInstaller;
private ServiceProcessInstaller processInstaller;
public MyServiceInstaller()
{
// instantiate installers for process and service
processInstaller = new ServiceProcessInstaller();
serviceInstaller = new ServiceInstaller();
// run under the system service account
processInstaller.Account = ServiceAccount.User;
processInstaller.Username = ".\\UserName";
processInstaller.Password = "password";
// service to start automatically.
serviceInstaller.StartType = ServiceStartMode.Automatic;
serviceInstaller.Description = "MyServiceDescription";
// name
string SvcName = "MyServiceNae";
// ServiceName must equal those on ServiceBase derived classes
serviceInstaller.ServiceName = SvcName;
// displayed in list
serviceInstaller.DisplayName = "My Service DisplayName ";
// add installers to collection
Installers.Add(serviceInstaller);
Installers.Add(processInstaller);
Committed += new InstallEventHandler((sender, args) =>
{
var controller = new System.ServiceProcess.ServiceController(SvcName);
controller.Start();
});
}
}
The installandstart:
public static void InstallAndStart(
string serviceName,
string displayName,
string fileName,
string username=null,
string password = null)
{
IntPtr scm = OpenSCManager(null, null, ScmAccessRights.AllAccess);
try
{
IntPtr service = OpenService(
scm,
serviceName,
ServiceAccessRights.AllAccess);
if (service == IntPtr.Zero)
{
service = CreateService(
scm,
serviceName,
displayName,
ServiceAccessRights.AllAccess,
SERVICE_WIN32_OWN_PROCESS,
ServiceBootFlag.AutoStart,
ServiceError.Normal,
fileName,
null,
IntPtr.Zero,
null,
username,
password);
if (service == IntPtr.Zero)
throw new ApplicationException("Failed to install service.");
try
{
StartService(service, 0, 0);
}
finally
{
CloseServiceHandle(service);
}
}
}
finally
{
CloseServiceHandle(scm);
}
}

Related

Owin SSL firefox authenticity of the received data could not be verified error

I'm wondering whether anybody can run the code below and see why firefox cannot talk to the owin SSL server correctly? It worked well in Chrome & IE.
I've tried to manually import the pfx into firefox but it still cannot talk to the owin server correctly.
Refer to the code below, I created an owin http server with SSL support and it can generate & install the certificate & bind the certificate to port automatically.
However, I noticed an issue when I test in different browsers. The firefox browser always display an error message indicating that:
The connection to the server was reset while the page was loading.
The page you are trying to view cannot be shown because the
authenticity of the received data could not be verified. Please
contact the website owners to inform them of this problem.
Chrome & IE can display the page correctly.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using CERTENROLLLib;
using Microsoft.Owin.Hosting;
using AppFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>;
using System.Security.Permissions;
using System.Security.Policy;
namespace Owin.Startup
{
class Program
{
static void Main(string[] args)
{
int port = 7878;
var cert = GetCert("localhost", TimeSpan.FromDays(3650), "devpwd", AppDomain.CurrentDomain.BaseDirectory + "cert.pfx");
ActivateCert((X509Certificate2)cert, port, GetAppId());
StartOptions so = new StartOptions();
so.Urls.Add($"https://+:{port}/");
using (WebApp.Start<Startup>(so))
{
Console.WriteLine($"Hosted on port: {port}");
Console.ReadLine();
}
}
static private string GetAppId()
{
Assembly assembly = Assembly.GetExecutingAssembly();
//The following line (part of the original answer) is misleading.
//**Do not** use it unless you want to return the System.Reflection.Assembly type's GUID.
//Console.WriteLine(assembly.GetType().GUID.ToString());
// The following is the correct code.
var attribute = (GuidAttribute)assembly.GetCustomAttributes(typeof(GuidAttribute), true)[0];
var id = attribute.Value;
return id;
}
static public X509Certificate GetCert(string cn, TimeSpan expirationLength, string pwd = "", string filename = null)
{
// http://stackoverflow.com/questions/18339706/how-to-create-self-signed-certificate-programmatically-for-wcf-service
// http://stackoverflow.com/questions/21629395/http-listener-with-https-support-coded-in-c-sharp
// https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.storename(v=vs.110).aspx
// create DN for subject and issuer
System.Security.Cryptography.X509Certificates.X509Certificate cert = null;
if (filename != null && File.Exists(filename))
{
cert = new X509Certificate2(filename, pwd, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
}
else
{
var base64encoded = string.Empty;
base64encoded = CreateCertContent(cn, expirationLength, pwd);
cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(
System.Convert.FromBase64String(base64encoded), pwd,
// mark the private key as exportable (this is usually what you want to do)
// mark private key to go into the Machine store instead of the current users store
X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet
);
File.WriteAllBytes(filename, cert.Export(X509ContentType.Pfx, pwd));
}
// instantiate the target class with the PKCS#12 data (and the empty password)
return cert;
}
private static string CreateCertContent(string cn, TimeSpan expirationLength, string pwd)
{
string base64encoded = string.Empty;
var dn = new CX500DistinguishedName();
dn.Encode("CN=" + cn, X500NameFlags.XCN_CERT_NAME_STR_NONE);
CX509PrivateKey privateKey = new CX509PrivateKey();
privateKey.ProviderName = "Microsoft Strong Cryptographic Provider";
privateKey.Length = 2048;
privateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE;
privateKey.KeyUsage = X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_DECRYPT_FLAG |
X509PrivateKeyUsageFlags.XCN_NCRYPT_ALLOW_KEY_AGREEMENT_FLAG;
privateKey.MachineContext = true;
privateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
privateKey.Create();
// Use the stronger SHA512 hashing algorithm
var hashobj = new CObjectId();
hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID,
ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY,
AlgorithmFlags.AlgorithmFlagsNone, "SHA512");
// Create the self signing request
var cert = new CX509CertificateRequestCertificate();
cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, privateKey, "");
cert.Subject = dn;
cert.Issuer = dn; // the issuer and the subject are the same
cert.NotBefore = DateTime.Now.Date;
// this cert expires immediately. Change to whatever makes sense for you
cert.NotAfter = cert.NotBefore + expirationLength;
cert.HashAlgorithm = hashobj; // Specify the hashing algorithm
cert.Encode(); // encode the certificate
// Do the final enrollment process
var enroll = new CX509Enrollment();
enroll.InitializeFromRequest(cert); // load the certificate
enroll.CertificateFriendlyName = cn; // Optional: add a friendly name
string csr = enroll.CreateRequest(); // Output the request in base64
// and install it back as the response
enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate,
csr, EncodingType.XCN_CRYPT_STRING_BASE64, pwd); // no password
// output a base64 encoded PKCS#12 so we can import it back to the .Net security classes
base64encoded = enroll.CreatePFX(pwd, // no password, this is for internal consumption
PFXExportOptions.PFXExportChainWithRoot);
return base64encoded;
}
private static void ActivateCert(X509Certificate2 rlt, int port, string appId)
{
X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
if (!store.Certificates.Contains(rlt))
{
store.Add(rlt);
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "netsh";
psi.Arguments = $"http delete sslcert ipport=0.0.0.0:{port}";
Process procDel = Process.Start(psi);
procDel.WaitForExit();
psi.Arguments = $"http add sslcert ipport=0.0.0.0:{port} certhash={rlt.Thumbprint} appid={{{appId}}}";
Process proc = Process.Start(psi);
proc.WaitForExit();
psi.Arguments = $"http delete sslcert ipport=[::]:{port}";
Process procDelV6 = Process.Start(psi);
procDelV6.WaitForExit();
psi.Arguments = $"http add sslcert ipport=[::]:{port} certhash={rlt.Thumbprint} appid={{{appId}}}";
Process procV6 = Process.Start(psi);
procV6.WaitForExit();
psi.Arguments = $"http add urlacl url=https://+:{port}/ user={Environment.UserDomainName}\\{Environment.UserName}";
Process procAcl = Process.Start(psi);
procAcl.WaitForExit();
}
store.Close();
}
}
public class Startup
{
private IAppBuilder app;
public void Configuration(IAppBuilder app)
{
#if DEBUG
app.UseErrorPage();
#endif
app.Use(new Func<AppFunc, AppFunc>(next => (async env =>
{
Console.WriteLine("Begin Request");
foreach (var i in env.Keys)
{
Console.WriteLine($"{i}\t={(env[i] == null ? "null" : env[i].ToString())}\t#\t{(env[i] == null ? "null" : env[i].GetType().FullName)}");
}
if (next != null)
{
await next.Invoke(env);
}
else
{
Console.WriteLine("Process Complete");
}
Console.WriteLine("End Request");
})));
app.UseWelcomePage("/");
this.app = app;
}
}
}
In firefox, type about:config and search for dhe.
The list shown all the cipher algorithms that firefox supported.
However, I noticed you used SHA512 which happened to be not supported by the latest firefox.
Please modify the algorithm to sha256 and it should work in firefox :)

Batch file v PowerShell script

Is there any advantage to using a PowerShell script over a batch file?
By which I mean, if I have something which will run in either, is there anything to warrant detecting if PowerShell is installed and using it over a batch file if it is?
SCHTASKS works in exactly the same manner on both. Given that, is there any differentiator between them?
The following code executes the same code via both processes:
internal class Program
{
private static void Main()
{
const string COMMAND = #"SCHTASKS /QUERY /XML ONE";
Collection<PSObject> psObjects;
using (var ps = PowerShell.Create())
{
ps.AddScript(COMMAND);
psObjects = ps.Invoke();
}
var process = new Process
{
StartInfo = new ProcessStartInfo
{
UseShellExecute = false,
RedirectStandardOutput = true,
FileName = "cmd.exe",
Arguments = "/C " + COMMAND
}
};
process.Start();
string cmdTasks;
using (var reader = process.StandardOutput)
{
cmdTasks = reader.ReadToEnd();
}
}
}
The response object from the two approaches differs. The Process.Start() approach returns a string, which I would have to parse whereas invoking PowerShell gives me a collection of PSObject objects.

How to programmatically query if an SCCM 2012 Application is Active or Retired

We have an application that integrates with SCCM 2012 and saves custom SCCM applications to SCCM.
The problem I am having is that attempting to save one of our custom applications when the SCCM administrator has set the application to be in the retired state causes our application to fail the saving process.
I'd like to be able to query the SCCM application state in order to determine before we attempt the save operation whether the given application is Active or Retired.
I can find no reference to "retired" status in the SMS_Application Server WMI help or any of the other pages:
http://msdn.microsoft.com/en-us/library/hh949251.aspx
I have noticed that there is a Restore() method which looks like it will change the status of a Retired package back to Active, however that's not quite what I want to do.
Can anyone help me determine how to find an applications current status?
Thanks.
There's a method in the SCCM 2012 PowerShell cmdlets that appears to be retrieving the expired status. Here's the c# code (decompiled from the dll AppUI.PS.AppMan.dll on the SCCM server)
private bool IsApplicationRetired(IResultObject applicaction)
{
IResultObject[] resultObjectArray = null;
int integerValue = applicaction["CI_ID"].IntegerValue;
object[] objArray = new object[] { integerValue };
resultObjectArray = base.ExecuteQuery(string.Format(CultureInfo.InvariantCulture, "SELECT * FROM SMS_Application WHERE CI_ID = {0}", objArray));
IResultObject[] resultObjectArray1 = resultObjectArray;
int num = 0;
if (num < (int)resultObjectArray1.Length)
{
IResultObject resultObject = resultObjectArray1[num];
this.isApplicationRetired = resultObject["IsExpired"].BooleanValue;
}
if (this.isApplicationRetired)
{
object[] objArray1 = new object[] { integerValue };
IResultObject instance = base.ConnectionManager.GetInstance(string.Format(CultureInfo.InvariantCulture, "SMS_Application.CI_ID={0}", objArray1));
if (instance != null)
{
string stringValue = instance["ModelName"].StringValue;
instance.Dispose();
object[] objArray2 = new object[] { base.ConnectionManager.EscapeQueryString(stringValue, ConnectionManagerBase.EscapeQuoteType.SingleQuote) };
resultObjectArray = base.ExecuteQuery(string.Format(CultureInfo.InvariantCulture, "SELECT CI_ID FROM SMS_Application WHERE ModelName = '{0}' AND IsLatest = 1 AND IsExpired = 0", objArray2));
IResultObject[] resultObjectArray2 = resultObjectArray;
int num1 = 0;
if (num1 < (int)resultObjectArray2.Length)
{
IResultObject resultObject1 = resultObjectArray2[num1];
if (resultObject1["CI_ID"].IntegerValue != integerValue)
{
this.isApplicationRetired = false;
}
}
}
}
return this.isApplicationRetired;
}

Unlist a package from Nuget with all it's history versions

I'm looking for a simple solution, on how to Unlist (delete) a package from Nuget library with all of it's versions.
I'm asking this, because manually I can do this 1 version at a time for 1 package. And I have lots of packages with ~20+ versions each ..
Please help.
I know this is old, but the nuget "delete" command is still not implemented, so I needed a solution. Perhaps someone can still benefit from this. I created a Command-line program and added the Nuget package Headless. Then, the code is:
class Program
{
static void Main(string[] args)
{
List<PostEntry> parameters = new List<PostEntry>();
using (var browser = new Browser())
{
var loginPage = browser.GoTo<LoginPage>();
parameters.Clear();
parameters.Add(new PostEntry("SignIn.UserNameOrEmail", "yournugetusername"));
parameters.Add(new PostEntry("SignIn.Password", "yournugetpassword"));
parameters.Add(new PostEntry("ReturnUrl", "/"));
parameters.Add(new PostEntry("LinkingAccount", "false"));
parameters.Add(new PostEntry("__RequestVerificationToken", loginPage.Find<HtmlElement>().ById("signIn").Find<HtmlInput>().ByName("__RequestVerificationToken").Value));
browser.PostTo(parameters, new Uri("https://www.nuget.org/users/account/SignIn"));
Console.WriteLine("logged in");
var cont = true;
while (cont)
{
var packagesPage = browser.GoTo<PackagesPage>();
Console.WriteLine("at packages page");
var links = packagesPage.Find<HtmlElement>()
.ById("published")
.Find<HtmlLink>()
.AllByPredicate(o => o.GetAttribute("title") == "Delete"
&& (o.Href.Contains("/IdOfPackageIWantToDelete/")
|| o.Href.Contains("/IdOfAnotherPackageIWantToDelete/")));
cont = links.Count() >= 1;
if (links.Any())
{
var htmlLink = links.First();
var linkPage = htmlLink.Href;
Console.WriteLine(linkPage);
var deletePackagePage = htmlLink.Click();
parameters.Clear();
parameters.Add(new PostEntry("Listed", "false"));
parameters.Add(new PostEntry("__RequestVerificationToken", deletePackagePage.Find<HtmlElement>().ById("body").Find<HtmlInput>().ByName("__RequestVerificationToken").Value));
browser.PostTo(parameters, new Uri("https://www.nuget.org" + linkPage));
Console.WriteLine("delete submitted");
}
}
}
Console.WriteLine("Hit any key to quit");
Console.ReadKey();
}
public class LoginPage : HtmlPage
{
public override Uri TargetLocation
{
get { return new Uri("https://www.nuget.org/users/account/LogOn?returnUrl=%2F"); }
}
}
public class PackagesPage : HtmlPage
{
public override Uri TargetLocation
{
get { return new Uri("https://www.nuget.org/account/Packages"); }
}
}
}
It will take a little while to run, but it does the trick.
You could write a batch script to call nuget delete, iterating over all your packages - see http://docs.nuget.org/docs/reference/command-line-reference#Delete_Command
e.g.
nuget delete MyPackage 1.1 -NoPrompt
nuget delete MyPackage 1.2 -NoPrompt
etc. Pretty straightforward with grep or a search/replace in a text editor.
You can do this with PowerShell, here is a function:
function Remove-AllNuGetPackageVersions($PackageId, $ApiKey)
{
$lower = $PackageId.ToLowerInvariant();
$json = Invoke-WebRequest -Uri "https://api.nuget.org/v3-flatcontainer/$lower/index.json" | ConvertFrom-Json
foreach($version in $json.versions)
{
Write-Host "Unlisting $PackageId, Ver $version"
Invoke-Expression "nuget delete $PackageId $version $ApiKey -source https://api.nuget.org/v3/index.json -NonInteractive"
}
}
If you are hosting your own nuget gallery, you could execute the following SQL query against the nuget database:
select 'nuget delete ' + pr.Id + ' ' + p.Version + ' {ApiKey} -source {Source} -noninteractive' from Packages p
join PackageRegistrations pr
on p.PackageRegistrationKey = pr.[Key]
where pr.Id = '{PackageId}' AND
p.Listed = 1
Replace {ApiKey}, {Source} and {PackageId} with your own values, I then placed the results in a batch file adjacent to nuget.exe and executed.
You can use a combination of tools by creating a simple console application.
Create a new Console application and install the package "Nuget.Core".
Add the following method to get a list of all package versions:
private static IEnumerable<IPackage> GetListedPackages(string packageID)
{
var repo = PackageRepositoryFactory.Default.CreateRepository("https://packages.nuget.org/api/v2");
var packages = from package in repo.FindPackagesById(packageID)
where package.IsListed()
select package;
return packages;
}
Then copy nuget.exe from the ".nuget" folder in one of your projects into the console application project (add it in the solution explorer and make sure that it's copied to the output directory)
Next create a new method which uses nuget.exe to unlist the package version:
private static string UnlistPackage(IPackage package, string apiKey)
{
var arguments = $"delete {package.Id} {package.Version} -ApiKey {apiKey} -NonInteractive";
var psi = new ProcessStartInfo("nuget.exe", arguments)
{
RedirectStandardOutput = true,
WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory,
UseShellExecute = false
};
var process = Process.Start(psi);
process.WaitForExit();
return process.StandardOutput.ReadToEnd();
}
Finally change Main method so it gets a list and removes all versions:
private static void Main(string[] args)
{
var packageID = "yourPackageName";
var apiKey = "yourKeyFromNugetOrg";
var packages = GetListedPackages(packageID);
foreach (var package in packages)
{
Console.WriteLine("Unlisting package " + package);
var output = UnlistPackage(package, apiKey);
Console.WriteLine(output);
}
Console.Write("Completed. Press ENTER to quit.");
Console.ReadLine();
}
source: http://blog.gauffin.org/2016/09/how-to-remove-a-package-from-nuget-org/

Why I am getting blank pdf Inside CRM2011(CRM 2011 Blank PDF Problem)?

The code working fine when run out side the CRM2011 application. When I embedded my Code Inside the CRM it will return blank pdf file with no data.
following is my code
public static Byte[] RenderReport(string ServiceURL, string ReportName, ParameterValue[] parameters)
{
byte[] result;
string encoding;
string mimeType;
Warning[] warnings = null;
string[] streamids;
string extension;
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
binding.Security.Transport.Realm = string.Empty;
binding.MaxReceivedMessageSize = 2147483647;
string deviceInfo = #"<DeviceInfo><Toolbar>False</Toolbar></DeviceInfo>";
EndpointAddress endpoint = new EndpointAddress(ServiceURL);
ReportExecutionServiceSoapClient client = new ReportExecutionServiceSoapClient(binding, endpoint);
client.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
ExecutionInfo execInfo = new ExecutionInfo();
ExecutionHeader execHeader = new ExecutionHeader();
ServerInfoHeader serverInfoHeader;
ExecutionInfo executionInfo;
ExecutionHeader executionHeader = client.LoadReport(null, ReportName, null, out serverInfoHeader, out executionInfo);
// Attach Report Parameters
//executionHeader.ExecutionID = executionInfo.ExecutionID;
if (parameters != null)
client.SetExecutionParameters(executionHeader, null, parameters, null, out executionInfo);
ServerInfoHeader Info= client.Render(executionHeader, null, "PDF", deviceInfo, out result, out extension, out mimeType, out encoding, out warnings, out streamids);
return result;
}
Thanks
I changed the Async processor to run under a user that has access to the Report Server database and it worked instantly.it was set to use network service so I changed it to Administrator in services.

Resources