Certificate/wireless plugins; major refactoring
Migrate much of BI to Services. Added Wireless Profile Provider plugin feature. Added Certificate Authority Provider plugin feature. Modified Certificate Provider plugin feature. Database migration v17, for Device Profiles. Enrolment Client Updated to support CA Certificates, Wireless Profiles and Hardware Info. New Client Enrolment Protocol to support new features. Plugin Manifest Generator added to main solution. Improved AD search performance.
This commit is contained in:
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.Client.Extensions
|
||||
{
|
||||
|
||||
@@ -1,55 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Disco.Models.ClientServices;
|
||||
using Newtonsoft.Json;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Disco.Models.ClientServices;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Disco.Client.Extensions
|
||||
{
|
||||
public static class ClientServicesExtensions
|
||||
{
|
||||
#if DEBUG
|
||||
public const string ServicePathAuthenticatedTemplate = "http://WS-GSHARP:57252/Services/Client/Authenticated/{0}";
|
||||
public const string ServicePathUnauthenticatedTemplate = "http://WS-GSHARP:57252/Services/Client/Unauthenticated/{0}";
|
||||
#else
|
||||
public const string ServicePathAuthenticatedTemplate = "http://DISCO:9292/Services/Client/Authenticated/{0}";
|
||||
public const string ServicePathUnauthenticatedTemplate = "http://DISCO:9292/Services/Client/Unauthenticated/{0}";
|
||||
#endif
|
||||
|
||||
public static ResponseType Post<ResponseType>(this ServiceBase<ResponseType> Service, bool Authenticated)
|
||||
{
|
||||
string jsonResponse;
|
||||
ResponseType serviceResponse;
|
||||
string serviceUrl;
|
||||
|
||||
if (Authenticated)
|
||||
serviceUrl = string.Format(ServicePathAuthenticatedTemplate, Service.Feature);
|
||||
else
|
||||
serviceUrl = string.Format(ServicePathUnauthenticatedTemplate, Service.Feature);
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(serviceUrl);
|
||||
request.UserAgent = string.Format("Disco-Client/{0}", Assembly.GetExecutingAssembly().GetName().Version.ToString(3));
|
||||
request.UserAgent = $"Disco-Client/{Assembly.GetExecutingAssembly().GetName().Version.ToString(3)}";
|
||||
request.ContentType = "application/json";
|
||||
request.Method = WebRequestMethods.Http.Post;
|
||||
request.UseDefaultCredentials = true;
|
||||
request.Timeout = 300000; // 5 Minutes
|
||||
string jsonRequest = JsonConvert.SerializeObject(Service);
|
||||
|
||||
using (StreamWriter requestWriter = new StreamWriter(request.GetRequestStream()))
|
||||
var jsonSerializer = new JsonSerializer();
|
||||
|
||||
using (var requestWriter = new StreamWriter(request.GetRequestStream()))
|
||||
{
|
||||
requestWriter.Write(jsonRequest);
|
||||
using (var jsonWriter = new JsonTextWriter(requestWriter))
|
||||
{
|
||||
jsonSerializer.Serialize(jsonWriter, Service);
|
||||
}
|
||||
}
|
||||
|
||||
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||
{
|
||||
using (StreamReader responseReader = new StreamReader(response.GetResponseStream()))
|
||||
using (var responseReader = new StreamReader(response.GetResponseStream()))
|
||||
{
|
||||
jsonResponse = responseReader.ReadToEnd();
|
||||
using (var jsonReader = new JsonTextReader(responseReader))
|
||||
{
|
||||
serviceResponse = jsonSerializer.Deserialize<ResponseType>(jsonReader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(jsonResponse))
|
||||
return default(ResponseType);
|
||||
else
|
||||
return JsonConvert.DeserializeObject<ResponseType>(jsonResponse);
|
||||
return serviceResponse;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Disco.Client.Interop;
|
||||
using Disco.Models.ClientServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Win32;
|
||||
using System.Text.RegularExpressions;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace Disco.Client.Extensions
|
||||
{
|
||||
@@ -16,26 +12,23 @@ namespace Disco.Client.Extensions
|
||||
|
||||
public static void Build(this Enrol enrol)
|
||||
{
|
||||
enrol.DeviceUUID = Interop.SystemAudit.DeviceUUID;
|
||||
enrol.DeviceSerialNumber = Interop.SystemAudit.DeviceSerialNumber;
|
||||
enrol.ComputerName = Environment.MachineName;
|
||||
enrol.RunningUserDomain = Environment.UserDomainName;
|
||||
enrol.RunningUserName = Environment.UserName;
|
||||
enrol.RunningInteractively = Environment.UserInteractive;
|
||||
|
||||
enrol.DeviceComputerName = Interop.LocalAuthentication.ComputerName;
|
||||
// Hardware Audit
|
||||
enrol.Hardware = Hardware.Information;
|
||||
enrol.SerialNumber = enrol.Hardware.SerialNumber;
|
||||
|
||||
enrol.DeviceManufacturer = Interop.SystemAudit.DeviceManufacturer;
|
||||
enrol.DeviceModel = Interop.SystemAudit.DeviceModel;
|
||||
enrol.DeviceModelType = Interop.SystemAudit.DeviceType;
|
||||
|
||||
enrol.DeviceIsPartOfDomain = Interop.SystemAudit.DeviceIsPartOfDomain;
|
||||
enrol.DeviceDNSDomainName = Interop.SystemAudit.DeviceDNSDomainName;
|
||||
|
||||
// LAN
|
||||
enrol.DeviceLanMacAddress = Interop.Network.PrimaryLanMacAddress;
|
||||
|
||||
// WAN
|
||||
enrol.DeviceWlanMacAddress = Interop.Network.PrimaryWlanMacAddress;
|
||||
// Apply System Information
|
||||
enrol.ApplySystemInformation();
|
||||
|
||||
// Certificates
|
||||
enrol.DeviceCertificates = Interop.Certificates.GetCertificateSubjects(StoreName.My, StoreLocation.LocalMachine);
|
||||
enrol.Certificates = Certificates.GetAllCertificates();
|
||||
|
||||
// Wireless Profiles
|
||||
enrol.WirelessProfiles = WirelessNetwork.GetWirelessProfiles();
|
||||
}
|
||||
|
||||
public static void Process(this EnrolResponse enrolResponse)
|
||||
@@ -51,12 +44,14 @@ namespace Disco.Client.Extensions
|
||||
// Offline Domain Join
|
||||
bool requireReboot = enrolResponse.ApplyOfflineDomainJoin();
|
||||
|
||||
// Certificates
|
||||
enrolResponse.ApplyDeviceCertificates();
|
||||
|
||||
// Device Owner
|
||||
enrolResponse.ApplyDeviceAssignedUser();
|
||||
|
||||
// Certificates
|
||||
enrolResponse.ApplyDeviceCertificates();
|
||||
|
||||
// Wireless Profiles
|
||||
enrolResponse.ApplyWirelessProfiles();
|
||||
|
||||
Presentation.UpdateStatus("Enrolling Device", "Device Enrolment Successfully Completed", false, 0, 1500);
|
||||
|
||||
@@ -71,15 +66,15 @@ namespace Disco.Client.Extensions
|
||||
/// <returns>Boolean indicating whether a reboot is required.</returns>
|
||||
private static bool ApplyOfflineDomainJoin(this EnrolResponse enrolResponse)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(enrolResponse.OfflineDomainJoin))
|
||||
if (!string.IsNullOrWhiteSpace(enrolResponse.OfflineDomainJoinManifest))
|
||||
{
|
||||
Presentation.UpdateStatus("Enrolling Device", string.Format("Performing Offline Domain Join:{0}Renaming Computer: {1} -> {2}", Environment.NewLine, Interop.LocalAuthentication.ComputerName, enrolResponse.DeviceComputerName), true, -1, 1500);
|
||||
Presentation.UpdateStatus("Enrolling Device", $"Performing Offline Domain Join:\r\nRenaming Computer: {Environment.MachineName} -> {enrolResponse.ComputerName}", true, -1, 1500);
|
||||
|
||||
string odjFile = Path.GetTempFileName();
|
||||
File.WriteAllBytes(odjFile, Convert.FromBase64String(enrolResponse.OfflineDomainJoin));
|
||||
File.WriteAllBytes(odjFile, Convert.FromBase64String(enrolResponse.OfflineDomainJoinManifest));
|
||||
|
||||
string odjWindowsPath = Environment.GetEnvironmentVariable("SystemRoot");
|
||||
string odjProcessArguments = string.Format("/REQUESTODJ /LOADFILE \"{0}\" /WINDOWSPATH \"{1}\" /LOCALOS", odjFile, odjWindowsPath);
|
||||
string odjProcessArguments = $"/REQUESTODJ /LOADFILE \"{odjFile}\" /WINDOWSPATH \"{odjWindowsPath}\" /LOCALOS";
|
||||
|
||||
ProcessStartInfo odjProcessStartInfo = new ProcessStartInfo("DJOIN.EXE", odjProcessArguments)
|
||||
{
|
||||
@@ -95,17 +90,17 @@ namespace Disco.Client.Extensions
|
||||
odjResult = odjProcess.StandardOutput.ReadToEnd();
|
||||
odjProcess.WaitForExit(20000); // 20 Seconds
|
||||
}
|
||||
Presentation.UpdateStatus("Enrolling Device", string.Format("Offline Domain Join Result:{0}{1}", Environment.NewLine, odjResult), true, -1, 3000);
|
||||
Presentation.UpdateStatus("Enrolling Device", $"Offline Domain Join Result:\r\n{odjResult}", true, -1, 3000);
|
||||
|
||||
if (File.Exists(odjFile))
|
||||
File.Delete(odjFile);
|
||||
|
||||
// Flush Logged-On History
|
||||
if (!string.IsNullOrEmpty(enrolResponse.DeviceDomainName))
|
||||
if (!string.IsNullOrEmpty(enrolResponse.DomainName))
|
||||
{
|
||||
using (RegistryKey regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
|
||||
{
|
||||
regWinlogon.SetValue("DefaultDomainName", enrolResponse.DeviceDomainName, RegistryValueKind.String);
|
||||
regWinlogon.SetValue("DefaultDomainName", enrolResponse.DomainName, RegistryValueKind.String);
|
||||
regWinlogon.SetValue("DefaultUserName", String.Empty, RegistryValueKind.String);
|
||||
}
|
||||
using (RegistryKey regLogonUI = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI", true))
|
||||
@@ -130,62 +125,44 @@ namespace Disco.Client.Extensions
|
||||
private static void ApplyDeviceAssignedUser(this EnrolResponse enrolResponse)
|
||||
{
|
||||
// Only run task if Assigned User was specified
|
||||
if (!string.IsNullOrWhiteSpace(enrolResponse.DeviceAssignedUserSID))
|
||||
if (!string.IsNullOrWhiteSpace(enrolResponse.AssignedUserSID))
|
||||
{
|
||||
Presentation.UpdateStatus("Enrolling Device", string.Format(@"Configuring the device owner:{0}{1} ({2}\{3})", Environment.NewLine, enrolResponse.DeviceAssignedUserName, enrolResponse.DeviceAssignedUserDomain, enrolResponse.DeviceAssignedUserUsername), true, -1, 3000);
|
||||
Presentation.UpdateStatus("Enrolling Device", $"Configuring the device owner:\r\n{enrolResponse.AssignedUserDescription} ({enrolResponse.AssignedUserDomain}\\{enrolResponse.AssignedUserUsername})", true, -1, 3000);
|
||||
|
||||
if (enrolResponse.DeviceAssignedUserIsLocalAdmin)
|
||||
Interop.LocalAuthentication.AddLocalGroupMembership("Administrators", enrolResponse.DeviceAssignedUserSID, enrolResponse.DeviceAssignedUserUsername, enrolResponse.DeviceAssignedUserDomain);
|
||||
if (enrolResponse.AssignedUserIsLocalAdmin)
|
||||
Interop.LocalAuthentication.AddLocalGroupMembership("Administrators", enrolResponse.AssignedUserSID, enrolResponse.AssignedUserUsername, enrolResponse.AssignedUserDomain);
|
||||
|
||||
// Make Windows think this user was the last to logon
|
||||
using (RegistryKey regWinlogon = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon", true))
|
||||
{
|
||||
regWinlogon.SetValue("DefaultDomainName", enrolResponse.DeviceAssignedUserDomain, RegistryValueKind.String);
|
||||
regWinlogon.SetValue("DefaultUserName", enrolResponse.DeviceAssignedUserUsername, RegistryValueKind.String);
|
||||
regWinlogon.SetValue("DefaultDomainName", enrolResponse.AssignedUserDomain, RegistryValueKind.String);
|
||||
regWinlogon.SetValue("DefaultUserName", enrolResponse.AssignedUserUsername, RegistryValueKind.String);
|
||||
}
|
||||
using (RegistryKey regLogonUI = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI", true))
|
||||
{
|
||||
regLogonUI.SetValue("LastLoggedOnUser", string.Format(@"{0}\{1}", enrolResponse.DeviceAssignedUserDomain, enrolResponse.DeviceAssignedUserUsername), RegistryValueKind.String);
|
||||
regLogonUI.SetValue("LastLoggedOnUser", $@"{enrolResponse.AssignedUserDomain}\{enrolResponse.AssignedUserUsername}", RegistryValueKind.String);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a Client Service Enrol Response for Device Certificate Actions
|
||||
/// </summary>
|
||||
/// <param name="enrolResponse"></param>
|
||||
private static void ApplyDeviceCertificates(this EnrolResponse enrolResponse)
|
||||
{
|
||||
// Only run if a Certificate was supplied
|
||||
if (!string.IsNullOrEmpty(enrolResponse.DeviceCertificate))
|
||||
if (enrolResponse.Certificates != null)
|
||||
{
|
||||
Presentation.UpdateStatus("Enrolling Device", "Configuring Wireless Certificates", true, -1, 1000);
|
||||
Presentation.UpdateStatus("Enrolling Device", "Configuring Certificates", true, -1, 1000);
|
||||
|
||||
var certPersonalBytes = Convert.FromBase64String(enrolResponse.DeviceCertificate);
|
||||
var certPersonal = new X509Certificate2(certPersonalBytes, "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
|
||||
if (certPersonal == null)
|
||||
throw new ClientServiceException("Enrolment > Device Certificate", "Unable to Import Device Certificate Provided, possibly check password.");
|
||||
|
||||
// Certificate Removal
|
||||
if (enrolResponse.DeviceCertificateRemoveExisting != null && enrolResponse.DeviceCertificateRemoveExisting.Count > 0)
|
||||
{
|
||||
List<Regex> regExMatchesSubject = new List<Regex>();
|
||||
foreach (var subjectRegEx in enrolResponse.DeviceCertificateRemoveExisting)
|
||||
regExMatchesSubject.Add(new Regex(subjectRegEx, RegexOptions.IgnoreCase));
|
||||
|
||||
// Remove from 'My' Store
|
||||
Interop.Certificates.RemoveCertificates(StoreName.My, StoreLocation.LocalMachine, regExMatchesSubject, certPersonal);
|
||||
// Remove from 'Root' Store
|
||||
Interop.Certificates.RemoveCertificates(StoreName.Root, StoreLocation.LocalMachine, regExMatchesSubject, certPersonal);
|
||||
// Remove from 'CertificateAuthority' Store
|
||||
Interop.Certificates.RemoveCertificates(StoreName.CertificateAuthority, StoreLocation.LocalMachine, regExMatchesSubject, certPersonal);
|
||||
}
|
||||
|
||||
// Add Certificate
|
||||
Presentation.UpdateStatus("Enrolling Device", string.Format("Configuring Wireless Certificates{0}Installing Certificate: {1}", Environment.NewLine, Interop.Certificates.GetCertificateFriendlyName(certPersonal)), true, -1);
|
||||
Interop.Certificates.AddCertificate(StoreName.My, StoreLocation.LocalMachine, certPersonal);
|
||||
enrolResponse.Certificates.Apply();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplyWirelessProfiles(this EnrolResponse enrolResponse)
|
||||
{
|
||||
if (enrolResponse.WirelessProfiles != null)
|
||||
{
|
||||
Presentation.UpdateStatus("Enrolling Device", "Configuring Wireless Profiles", true, -1, 1000);
|
||||
|
||||
enrolResponse.WirelessProfiles.Apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user