Make the Serverconfig request admin rights only if required
This commit is contained in:
1 parent
070777eb68
commit
0a9c6e5cfe
5 files changed
+206
-71
No files matched your search
@@ -142,14 +142,14 @@ namespace ServerLib
|
||||
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
|
||||
smb.HttpGetEnabled = true;
|
||||
host.Description.Behaviors.Add(smb);*/
|
||||
_RegisterUrlAndCert(_BaseAddress.Port);
|
||||
_RegisterUrlAndCert(_BaseAddress.Port, false);
|
||||
_Host.Open();
|
||||
}
|
||||
catch (CommunicationException e)
|
||||
{
|
||||
if (e is AddressAccessDeniedException || e is AddressAlreadyInUseException)
|
||||
{
|
||||
_RegisterUrlAndCert(_BaseAddress.Port);
|
||||
_RegisterUrlAndCert(_BaseAddress.Port, true);
|
||||
try
|
||||
{
|
||||
_Host.Abort();
|
||||
@@ -178,18 +178,18 @@ namespace ServerLib
|
||||
}
|
||||
}
|
||||
|
||||
private void _RegisterUrlAndCert(int port)
|
||||
private void _RegisterUrlAndCert(int port, bool reserve)
|
||||
{
|
||||
#if WIN
|
||||
|
||||
ProcessStartInfo info = new ProcessStartInfo
|
||||
{
|
||||
FileName = "VocaluxeServerConfig.exe",
|
||||
Arguments = port + " " + (_Encrypted ? "true" : "false"),
|
||||
WorkingDirectory = Environment.CurrentDirectory,
|
||||
Arguments = port + " " + (_Encrypted ? "true" : "false") + (reserve ? " reserve" : ""),
|
||||
UseShellExecute = true,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
Verb = "runas"
|
||||
};
|
||||
Process p = Process.Start(info);
|
||||
p.WaitForExit();
|
||||
|
||||
@@ -259,6 +259,7 @@ namespace Vocaluxe
|
||||
Environment.Exit(Environment.ExitCode);
|
||||
}
|
||||
|
||||
#if !DEBUG
|
||||
[HandleProcessCorruptedStateExceptions]
|
||||
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
|
||||
{
|
||||
@@ -273,6 +274,7 @@ namespace Vocaluxe
|
||||
CLog.LogError("Unhandled exception: " + e.Message + stackTrace);
|
||||
_CloseProgram();
|
||||
}
|
||||
#endif
|
||||
|
||||
private static Assembly _AssemblyResolver(Object sender, ResolveEventArgs args)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Principal;
|
||||
using Security.Cryptography;
|
||||
using Security.Cryptography.X509Certificates;
|
||||
using System;
|
||||
@@ -26,34 +29,64 @@ using NetFwTypeLib;
|
||||
|
||||
namespace WebserverInitalConfig
|
||||
{
|
||||
public static class CConfigHttpApi
|
||||
public class CConfigHttpApi
|
||||
{
|
||||
// ReSharper disable InconsistentNaming
|
||||
private const String CLSID_NetFwMgr = "{304CE942-6E39-40D8-943A-B913C40C9CD4}";
|
||||
private const String CLSID_NetAuthApp = "{EC9846B3-2762-4A6B-A214-6ACB603462D2}";
|
||||
private const String CLSID_NetOpenPort = "{0CA545C6-37AD-4A6C-BF92-9F7610067EF5}";
|
||||
private const string _AppGUID = "{7baf41f1-48b7-42cb-b145-f815499cb48f}";
|
||||
// ReSharper restore InconsistentNaming
|
||||
public static void ReserveUrl(string networkString)
|
||||
|
||||
private readonly string _RuleName;
|
||||
private readonly string _ExePath;
|
||||
private readonly string _IP;
|
||||
private readonly int _Port;
|
||||
private readonly bool _IsTCP;
|
||||
|
||||
public CConfigHttpApi(string ruleName, string exePath, string ip, int port, bool isTCP)
|
||||
{
|
||||
_ExePath = exePath;
|
||||
_Port = port;
|
||||
_IsTCP = isTCP;
|
||||
_IP = ip;
|
||||
_RuleName = ruleName;
|
||||
}
|
||||
|
||||
private static void _ReserveUrl(string networkString)
|
||||
{
|
||||
if (!IsAdministrator())
|
||||
throw new AuthenticationException();
|
||||
HttpApi.ReserveURL(networkString, "D:(A;;GX;;;S-1-1-0)");
|
||||
}
|
||||
|
||||
public static void BindCert(string ip, int port, X509Certificate2 cert)
|
||||
public void ReserveUrl()
|
||||
{
|
||||
IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse(ip), port);
|
||||
if (HttpApi.QuerySslCertificateInfo(endpoint) != null)
|
||||
HttpApi.DeleteCertificateBinding(endpoint);
|
||||
|
||||
HttpApi.BindCertificate(endpoint, cert.GetCertHash(), StoreName.My, new Guid("{7baf41f1-48b7-42cb-b145-f815499cb48f}"));
|
||||
_ReserveUrl("https://+:" + _Port + "/");
|
||||
}
|
||||
|
||||
public static void AddCertToStore(X509Certificate2 cert)
|
||||
private void _BindCert(X509Certificate cert)
|
||||
{
|
||||
IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse(_IP), _Port);
|
||||
HttpApi.SslCertificateInfo certificateInfo = HttpApi.QuerySslCertificateInfo(endpoint);
|
||||
if (certificateInfo != null && !certificateInfo.Hash.SequenceEqual(cert.GetCertHash()))
|
||||
{
|
||||
if (!IsAdministrator())
|
||||
throw new AuthenticationException();
|
||||
HttpApi.DeleteCertificateBinding(endpoint);
|
||||
|
||||
HttpApi.BindCertificate(endpoint, cert.GetCertHash(), StoreName.My, new Guid(_AppGUID));
|
||||
}
|
||||
}
|
||||
|
||||
private static void _AddCertToStore(X509Certificate2 cert)
|
||||
{
|
||||
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
|
||||
X509Certificate2Collection oldCerts = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName,
|
||||
cert.SubjectName.Name, false);
|
||||
// ReSharper disable AssignNullToNotNullAttribute
|
||||
X509Certificate2Collection oldCerts = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, cert.SubjectName.Name, false);
|
||||
// ReSharper restore AssignNullToNotNullAttribute
|
||||
|
||||
foreach (X509Certificate2 oldCert in oldCerts)
|
||||
store.Remove(oldCert);
|
||||
@@ -62,7 +95,25 @@ namespace WebserverInitalConfig
|
||||
store.Close();
|
||||
}
|
||||
|
||||
public static X509Certificate2 GetSelfSignedCert(string subjectName)
|
||||
private static X509Certificate2 _GetCert(string name)
|
||||
{
|
||||
X509Certificate2 result = null;
|
||||
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
|
||||
store.Open(OpenFlags.ReadOnly);
|
||||
|
||||
var name2 = new X500DistinguishedName(name);
|
||||
// ReSharper disable AssignNullToNotNullAttribute
|
||||
X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, name2.Name, false);
|
||||
// ReSharper restore AssignNullToNotNullAttribute
|
||||
if (certs.Count > 0)
|
||||
result = certs[0];
|
||||
|
||||
store.Close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private X509Certificate2 _GetSelfSignedCert(string subjectName)
|
||||
{
|
||||
var keyParam = new CngKeyCreationParameters
|
||||
{
|
||||
@@ -91,58 +142,113 @@ namespace WebserverInitalConfig
|
||||
byte[] rawData = key.CreateSelfSignedCertificate(param).Export(X509ContentType.Pfx, "");
|
||||
var cert = new X509Certificate2(rawData, "", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet)
|
||||
{
|
||||
FriendlyName = "Vocaluxe Server Certificate"
|
||||
FriendlyName = _RuleName + " Server Certificate"
|
||||
};
|
||||
return cert;
|
||||
}
|
||||
|
||||
private static void _AddFirewallRuleForProfile(INetFwProfile profile, string exePath, int port, bool isTCP)
|
||||
public void CreateAndAddCert(string hostName)
|
||||
{
|
||||
bool isFirewallEnabled = profile.FirewallEnabled;
|
||||
string certName = "CN=" + hostName + ", C=DE, O=" + _RuleName + ", OU=" + _RuleName + " Server";
|
||||
|
||||
if (!isFirewallEnabled)
|
||||
return;
|
||||
INetFwAuthorizedApplications applications = profile.AuthorizedApplications;
|
||||
if (!(from INetFwAuthorizedApplication a in applications
|
||||
where a.ProcessImageFileName == exePath
|
||||
select a).Any())
|
||||
X509Certificate2 cert = _GetCert(certName);
|
||||
if (cert == null)
|
||||
{
|
||||
INetFwAuthorizedApplication application = (INetFwAuthorizedApplication)Activator.CreateInstance(
|
||||
Type.GetTypeFromCLSID(new Guid(CLSID_NetAuthApp)));
|
||||
|
||||
application.Name = "Vocaluxe";
|
||||
application.ProcessImageFileName = exePath;
|
||||
application.Enabled = true;
|
||||
application.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL;
|
||||
applications.Add(application);
|
||||
}
|
||||
INetFwOpenPorts openPorts = profile.GloballyOpenPorts;
|
||||
NET_FW_IP_PROTOCOL_ protocol = isTCP ? NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP : NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_UDP;
|
||||
if (!(from INetFwOpenPort p in openPorts
|
||||
where (p.Port == port &&
|
||||
(p.Protocol == protocol))
|
||||
select p).Any())
|
||||
{
|
||||
INetFwOpenPort openPort = (INetFwOpenPort)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(CLSID_NetOpenPort)));
|
||||
openPort.Enabled = true;
|
||||
openPort.Port = port;
|
||||
openPort.Protocol = protocol;
|
||||
openPort.Name = "Vocaluxe(" + port + ")";
|
||||
openPort.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL;
|
||||
|
||||
openPorts.Add(openPort);
|
||||
if (!IsAdministrator())
|
||||
throw new AuthenticationException();
|
||||
cert = _GetSelfSignedCert(certName);
|
||||
_AddCertToStore(cert);
|
||||
}
|
||||
_BindCert(cert);
|
||||
}
|
||||
|
||||
public static void AddFirewallRule(string exePath, int port, bool isTCP)
|
||||
private bool _IsAppAuthorized(INetFwProfile profile)
|
||||
{
|
||||
if (!profile.FirewallEnabled)
|
||||
return true;
|
||||
foreach (INetFwAuthorizedApplication a in profile.AuthorizedApplications)
|
||||
{
|
||||
if (a.ProcessImageFileName == _ExePath)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool _IsPortOpen(INetFwProfile profile)
|
||||
{
|
||||
NET_FW_IP_PROTOCOL_ protocol = _GetProtocol();
|
||||
|
||||
if (!profile.FirewallEnabled)
|
||||
return true;
|
||||
foreach (INetFwOpenPort p in profile.GloballyOpenPorts)
|
||||
{
|
||||
if (p.Protocol == protocol && p.Port == _Port)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private NET_FW_IP_PROTOCOL_ _GetProtocol()
|
||||
{
|
||||
return _IsTCP ? NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP : NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_UDP;
|
||||
}
|
||||
|
||||
private void _AddFirewallRuleForProfile(INetFwProfile profile)
|
||||
{
|
||||
bool isAppAuthorized = _IsAppAuthorized(profile);
|
||||
bool isPortOpen = _IsPortOpen(profile);
|
||||
|
||||
if ((!isAppAuthorized || !isPortOpen) && !IsAdministrator())
|
||||
throw new AuthenticationException();
|
||||
if (!isAppAuthorized)
|
||||
_AddAppToFirewall(profile);
|
||||
|
||||
if (!isPortOpen)
|
||||
_AddPortToFirewall(profile);
|
||||
}
|
||||
|
||||
private void _AddPortToFirewall(INetFwProfile profile)
|
||||
{
|
||||
INetFwOpenPort openPort = (INetFwOpenPort)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(CLSID_NetOpenPort)));
|
||||
openPort.Enabled = true;
|
||||
openPort.Port = _Port;
|
||||
openPort.Protocol = _GetProtocol();
|
||||
openPort.Name = _RuleName + "(" + _Port + ")";
|
||||
openPort.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL;
|
||||
|
||||
profile.GloballyOpenPorts.Add(openPort);
|
||||
}
|
||||
|
||||
private void _AddAppToFirewall(INetFwProfile profile)
|
||||
{
|
||||
INetFwAuthorizedApplication application = (INetFwAuthorizedApplication)Activator.CreateInstance(
|
||||
Type.GetTypeFromCLSID(new Guid(CLSID_NetAuthApp)));
|
||||
|
||||
application.Name = _RuleName;
|
||||
application.ProcessImageFileName = _ExePath;
|
||||
application.Enabled = true;
|
||||
application.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL;
|
||||
profile.AuthorizedApplications.Add(application);
|
||||
}
|
||||
|
||||
public static bool IsAdministrator()
|
||||
{
|
||||
WindowsIdentity identity = WindowsIdentity.GetCurrent();
|
||||
// ReSharper disable AssignNullToNotNullAttribute
|
||||
WindowsPrincipal principal = new WindowsPrincipal(identity);
|
||||
// ReSharper restore AssignNullToNotNullAttribute
|
||||
return principal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
|
||||
public void AddFirewallRule()
|
||||
{
|
||||
INetFwMgr manage = (INetFwMgr)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(CLSID_NetFwMgr)));
|
||||
//AddFirewallRuleForProfile(manage.LocalPolicy.CurrentProfile, exePath, port, isTCP);
|
||||
//_AddFirewallRuleForProfile(manage.LocalPolicy.CurrentProfile);
|
||||
//if (manage.CurrentProfileType != NET_FW_PROFILE_TYPE_.NET_FW_PROFILE_STANDARD)
|
||||
//Add to Home/Work(Private) and current net
|
||||
//TODO: This somewhow adds 2 entries each instead of only one that is activated for Home/Work and public nets
|
||||
_AddFirewallRuleForProfile(manage.LocalPolicy.GetProfileByType(NET_FW_PROFILE_TYPE_.NET_FW_PROFILE_STANDARD), exePath, port, isTCP);
|
||||
_AddFirewallRuleForProfile(manage.LocalPolicy.GetProfileByType(NET_FW_PROFILE_TYPE_.NET_FW_PROFILE_CURRENT), exePath, port, isTCP);
|
||||
_AddFirewallRuleForProfile(manage.LocalPolicy.GetProfileByType(NET_FW_PROFILE_TYPE_.NET_FW_PROFILE_STANDARD));
|
||||
_AddFirewallRuleForProfile(manage.LocalPolicy.GetProfileByType(NET_FW_PROFILE_TYPE_.NET_FW_PROFILE_CURRENT));
|
||||
}
|
||||
|
||||
/*public static X509Certificate2 getCert(string subject)
|
||||
|
||||
@@ -16,34 +16,64 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Mime;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace WebserverInitalConfig
|
||||
{
|
||||
class Program
|
||||
static class CProgram
|
||||
{
|
||||
// ReSharper disable InconsistentNaming
|
||||
private static void Main(string[] args)
|
||||
// ReSharper restore InconsistentNaming
|
||||
{
|
||||
if (args.Length != 2)
|
||||
int port;
|
||||
if (args.Length < 2 || !int.TryParse(args[0], out port))
|
||||
{
|
||||
Console.WriteLine("Usage: VocaluxeServerConfig port isHttps");
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
|
||||
CConfigHttpApi.AddFirewallRule(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Vocaluxe.exe"), Int32.Parse(args[0]), true);
|
||||
|
||||
if (args[1].ToLower() == "true")
|
||||
{
|
||||
string hostname = Dns.GetHostName();
|
||||
X509Certificate2 cert = CConfigHttpApi.GetSelfSignedCert("CN=" + hostname + " C=DE O=Vocaluxe OU=Vocaluxe Server");
|
||||
CConfigHttpApi.AddCertToStore(cert);
|
||||
CConfigHttpApi.BindCert("0.0.0.0", Int32.Parse(args[0]), cert);
|
||||
CConfigHttpApi.ReserveUrl("https://+:" + args[0] + "/");
|
||||
}
|
||||
else
|
||||
CConfigHttpApi.ReserveUrl("http://+:" + args[0] + "/");
|
||||
{
|
||||
CConfigHttpApi config = new CConfigHttpApi("Vocaluxe", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Vocaluxe.exe"), "0.0.0.0", port, true);
|
||||
try
|
||||
{
|
||||
config.AddFirewallRule();
|
||||
if (args[1].ToLower() == "true")
|
||||
config.CreateAndAddCert(Dns.GetHostName());
|
||||
|
||||
if ((args.Length >= 3 && args[2] == "reserve") || CConfigHttpApi.IsAdministrator())
|
||||
config.ReserveUrl();
|
||||
}
|
||||
catch (AuthenticationException)
|
||||
{
|
||||
ProcessStartInfo proc = new ProcessStartInfo
|
||||
{
|
||||
UseShellExecute = true,
|
||||
WorkingDirectory = Environment.CurrentDirectory,
|
||||
FileName = Process.GetCurrentProcess().MainModule.FileName,
|
||||
Arguments = args[0] + " " + args[1],
|
||||
Verb = "runas",
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
Process.Start(proc);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// The user refused the elevation.
|
||||
// Do nothing and return directly ...
|
||||
Environment.Exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,9 +32,6 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>Vocaluxe.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
Reference in new issue
Block a user