Update: Mac Enrol using SshNet
SshNet supports required cryptographic algorithms. Simplified data gathering using plist/xml format from system_profiler. Fixes #88 and #92
This commit is contained in:
@@ -3,6 +3,8 @@
|
|||||||
## Ignore Visual Studio temporary files, build results, and
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
## files generated by popular Visual Studio add-ons.
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
.vs/
|
||||||
|
|
||||||
# User-specific files
|
# User-specific files
|
||||||
*.suo
|
*.suo
|
||||||
*.user
|
*.user
|
||||||
|
|||||||
+96
-126
@@ -10,7 +10,11 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Tamir.SharpSsh;
|
using Renci.SshNet;
|
||||||
|
using PList;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
namespace Disco.BI.DeviceBI
|
namespace Disco.BI.DeviceBI
|
||||||
{
|
{
|
||||||
@@ -24,7 +28,6 @@ namespace Disco.BI.DeviceBI
|
|||||||
Register = 30
|
Register = 30
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Regex SshPromptRegEx = new Regex("[\\$,\\#]", RegexOptions.Multiline);
|
|
||||||
public static MacSecureEnrolResponse MacSecureEnrol(DiscoDataContext Database, string Host)
|
public static MacSecureEnrolResponse MacSecureEnrol(DiscoDataContext Database, string Host)
|
||||||
{
|
{
|
||||||
MacEnrol trustedRequest = new MacEnrol();
|
MacEnrol trustedRequest = new MacEnrol();
|
||||||
@@ -33,96 +36,120 @@ namespace Disco.BI.DeviceBI
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
EnrolmentLog.LogSessionStarting(sessionId, Host, EnrolmentTypes.MacSecure);
|
EnrolmentLog.LogSessionStarting(sessionId, Host, EnrolmentTypes.MacSecure);
|
||||||
EnrolmentLog.LogSessionProgress(sessionId, 0, string.Format("Connecting to '{0}' as '{1}'", Host, Database.DiscoConfiguration.Bootstrapper.MacSshUsername));
|
EnrolmentLog.LogSessionProgress(sessionId, 0, $"Connecting to '{Host}' as '{Database.DiscoConfiguration.Bootstrapper.MacSshUsername}'");
|
||||||
SshShell shell = new SshShell(Host, Database.DiscoConfiguration.Bootstrapper.MacSshUsername, Database.DiscoConfiguration.Bootstrapper.MacSshPassword);
|
|
||||||
|
var sshConnectionInfo = new KeyboardInteractiveConnectionInfo(Host, Database.DiscoConfiguration.Bootstrapper.MacSshUsername);
|
||||||
|
sshConnectionInfo.AuthenticationPrompt += (sender, e) =>
|
||||||
|
{
|
||||||
|
foreach (var prompt in e.Prompts)
|
||||||
|
{
|
||||||
|
if (prompt.Request.StartsWith("Password", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
EnrolmentLog.LogSessionProgress(sessionId, 10, $"Authenticating at '{Host}' as '{Database.DiscoConfiguration.Bootstrapper.MacSshUsername}'");
|
||||||
|
prompt.Response = Database.DiscoConfiguration.Bootstrapper.MacSshPassword;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var sshClient = new SshClient(sshConnectionInfo))
|
||||||
|
{
|
||||||
|
sshClient.Connect();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
shell.ExpectPattern = "#";
|
EnrolmentLog.LogSessionProgress(sessionId, 30, "Retrieving System Profile Information");
|
||||||
shell.Connect();
|
var sshResult = sshClient.RunCommand("system_profiler -xml SPHardwareDataType SPNetworkDataType SPSoftwareDataType");
|
||||||
EnrolmentLog.LogSessionProgress(sessionId, 10, "Connected, Authenticating");
|
PListRoot profilerData;
|
||||||
var output = shell.Expect(SshPromptRegEx);
|
using (var reader = new StringReader(sshResult.Result))
|
||||||
bool sessionElevated = false;
|
|
||||||
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, output);
|
|
||||||
if (!output.TrimEnd(new char[0]).EndsWith("#"))
|
|
||||||
{
|
{
|
||||||
EnrolmentLog.LogSessionProgress(sessionId, 22, "Connected, Elevating Credentials");
|
using (var xmlReader = XmlReader.Create(reader, new XmlReaderSettings() { DtdProcessing = DtdProcessing.Ignore }))
|
||||||
shell.WriteLine("sudo -k");
|
|
||||||
System.Threading.Thread.Sleep(250);
|
|
||||||
output = shell.Expect(SshPromptRegEx);
|
|
||||||
EnrolmentLog.LogSessionProgress(sessionId, 25, "Connected, Elevating Credentials");
|
|
||||||
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, output);
|
|
||||||
shell.WriteLine("sudo -s -S");
|
|
||||||
System.Threading.Thread.Sleep(250);
|
|
||||||
output = shell.Expect(":");
|
|
||||||
EnrolmentLog.LogSessionProgress(sessionId, 27, "Connected, Elevating Credentials");
|
|
||||||
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, output);
|
|
||||||
shell.WriteLine(Database.DiscoConfiguration.Bootstrapper.MacSshPassword);
|
|
||||||
System.Threading.Thread.Sleep(250);
|
|
||||||
output = shell.Expect(SshPromptRegEx);
|
|
||||||
sessionElevated = true;
|
|
||||||
EnrolmentLog.LogSessionDiagnosticInformation(sessionId, output);
|
|
||||||
}
|
|
||||||
EnrolmentLog.LogSessionProgress(sessionId, 20, "Retrieving Serial Number");
|
|
||||||
trustedRequest.DeviceSerialNumber = ParseMacShellCommand(shell, "system_profiler SPHardwareDataType | grep \"Serial Number\" | cut -d \":\" -f 2-", sessionId);
|
|
||||||
EnrolmentLog.LogSessionDevice(sessionId, trustedRequest.DeviceSerialNumber, null);
|
|
||||||
EnrolmentLog.LogSessionProgress(sessionId, 30, "Retrieving Hardware UUID");
|
|
||||||
trustedRequest.DeviceUUID = ParseMacShellCommand(shell, "system_profiler SPHardwareDataType | grep \"Hardware UUID:\" | cut -d \":\" -f 2-", sessionId);
|
|
||||||
EnrolmentLog.LogSessionProgress(sessionId, 40, "Retrieving Computer Name");
|
|
||||||
trustedRequest.DeviceComputerName = ParseMacShellCommand(shell, "scutil --get ComputerName", sessionId);
|
|
||||||
EnrolmentLog.LogSessionProgress(sessionId, 50, "Retrieving Ethernet MAC Address");
|
|
||||||
string lanNicId = ParseMacShellCommand(shell, "system_profiler SPEthernetDataType | egrep -o \"en0|en1|en2|en3|en4|en5|en6\"", sessionId);
|
|
||||||
if (!string.IsNullOrWhiteSpace(lanNicId))
|
|
||||||
{
|
{
|
||||||
trustedRequest.DeviceLanMacAddress = ParseMacShellCommand(shell, string.Format("ifconfig {0} | grep ether | cut -d \" \" -f 2-", lanNicId), sessionId);
|
XmlSerializer serializer = new XmlSerializer(typeof(PListRoot));
|
||||||
|
profilerData = (PListRoot)serializer.Deserialize(xmlReader);
|
||||||
}
|
}
|
||||||
EnrolmentLog.LogSessionProgress(sessionId, 65, "Retrieving Wireless MAC Address");
|
}
|
||||||
string wlanNicId = ParseMacShellCommand(shell, "system_profiler SPAirPortDataType | egrep -o \"en0|en1|en2|en3|en4|en5|en6\"", sessionId);
|
|
||||||
if (!string.IsNullOrWhiteSpace(wlanNicId))
|
EnrolmentLog.LogSessionProgress(sessionId, 90, "Processing System Profile Information");
|
||||||
|
|
||||||
|
PListDict profilerDataHardware = null;
|
||||||
|
PListArray profilerDataNetwork = null;
|
||||||
|
PListDict profilerDataSoftware = null;
|
||||||
|
|
||||||
|
foreach (PListDict node in (profilerData.Root as PListArray))
|
||||||
{
|
{
|
||||||
trustedRequest.DeviceWlanMacAddress = ParseMacShellCommand(shell, string.Format("ifconfig {0} | grep ether | cut -d \" \" -f 2-", wlanNicId), sessionId);
|
var nodeItems = ((PListArray)node["_items"]);
|
||||||
|
|
||||||
|
switch (((PListString)node["_dataType"]).Value)
|
||||||
|
{
|
||||||
|
case "SPHardwareDataType":
|
||||||
|
profilerDataHardware = (PListDict)nodeItems[0];
|
||||||
|
break;
|
||||||
|
case "SPNetworkDataType":
|
||||||
|
profilerDataNetwork = nodeItems;
|
||||||
|
break;
|
||||||
|
case "SPSoftwareDataType":
|
||||||
|
profilerDataSoftware = (PListDict)nodeItems[0];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profilerDataHardware == null || profilerDataNetwork == null || profilerDataSoftware == null)
|
||||||
|
throw new InvalidOperationException("System Profiler didn't return information for a requested data type");
|
||||||
|
|
||||||
|
trustedRequest.DeviceSerialNumber = (profilerDataHardware["serial_number"] as PListString).Value;
|
||||||
|
trustedRequest.DeviceUUID = (profilerDataHardware["platform_UUID"] as PListString).Value;
|
||||||
|
trustedRequest.DeviceComputerName = (profilerDataSoftware["local_host_name"] as PListString).Value;
|
||||||
|
|
||||||
|
var profilerDataNetworkEthernet = profilerDataNetwork.Cast<PListDict>().FirstOrDefault(e => ((PListString)e["_name"]).Value == "Ethernet");
|
||||||
|
if (profilerDataNetworkEthernet != null)
|
||||||
|
{
|
||||||
|
trustedRequest.DeviceLanMacAddress = ((PListString)(profilerDataNetworkEthernet["Ethernet"] as PListDict)["MAC Address"]).Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var profilerDataNetworkWiFi = profilerDataNetwork.Cast<PListDict>().FirstOrDefault(e => ((PListString)e["_name"]).Value == "Wi-Fi");
|
||||||
|
if (profilerDataNetworkWiFi != null)
|
||||||
|
{
|
||||||
|
trustedRequest.DeviceWlanMacAddress = ((PListString)(profilerDataNetworkWiFi["Ethernet"] as PListDict)["MAC Address"]).Value;
|
||||||
|
}
|
||||||
|
|
||||||
trustedRequest.DeviceManufacturer = "Apple Inc.";
|
trustedRequest.DeviceManufacturer = "Apple Inc.";
|
||||||
EnrolmentLog.LogSessionProgress(sessionId, 80, "Retrieving Model");
|
trustedRequest.DeviceModel = (profilerDataHardware["machine_model"] as PListString).Value;
|
||||||
trustedRequest.DeviceModel = ParseMacShellCommand(shell, "system_profiler SPHardwareDataType | grep \"Model Identifier:\" | cut -d \":\" -f 2-", sessionId);
|
|
||||||
EnrolmentLog.LogSessionProgress(sessionId, 90, "Retrieving Model Type");
|
trustedRequest.DeviceModelType = ParseMacModelType((profilerDataHardware["machine_name"] as PListString).Value);
|
||||||
trustedRequest.DeviceModelType = ParseMacModelType(ParseMacShellCommand(shell, "system_profiler SPHardwareDataType | grep \"Model Name:\" | cut -d \":\" -f 2-", sessionId));
|
|
||||||
EnrolmentLog.LogSessionProgress(sessionId, 99, "Disconnecting");
|
EnrolmentLog.LogSessionProgress(sessionId, 99, "Disconnecting");
|
||||||
output = ParseMacModelType(ParseMacShellCommand(shell, "exit", sessionId));
|
|
||||||
if (sessionElevated)
|
sshClient.Disconnect();
|
||||||
{
|
|
||||||
output = ParseMacModelType(ParseMacShellCommand(shell, "exit", sessionId));
|
|
||||||
}
|
}
|
||||||
if (shell.Connected)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
shell.Close();
|
throw;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (sshClient != null)
|
||||||
|
{
|
||||||
|
bool connected = sshClient.IsConnected;
|
||||||
|
if (connected)
|
||||||
|
{
|
||||||
|
sshClient.Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EnrolmentLog.LogSessionProgress(sessionId, 100, "Disconnected, Starting Disco Enrolment");
|
EnrolmentLog.LogSessionProgress(sessionId, 100, "Disconnected, Starting Disco Enrolment");
|
||||||
MacSecureEnrolResponse response = MacSecureEnrolResponse.FromMacEnrolResponse(MacEnrol(Database, trustedRequest, true, sessionId));
|
MacSecureEnrolResponse response = MacSecureEnrolResponse.FromMacEnrolResponse(MacEnrol(Database, trustedRequest, true, sessionId));
|
||||||
EnrolmentLog.LogSessionFinished(sessionId);
|
EnrolmentLog.LogSessionFinished(sessionId);
|
||||||
MacSecureEnrol = response;
|
MacSecureEnrol = response;
|
||||||
}
|
}
|
||||||
catch (System.Exception ex)
|
catch (System.Exception ex)
|
||||||
{
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (shell != null)
|
|
||||||
{
|
|
||||||
bool connected = shell.Connected;
|
|
||||||
if (connected)
|
|
||||||
{
|
|
||||||
shell.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (System.Exception ex)
|
|
||||||
{
|
{
|
||||||
ex.ToExceptionless().Submit();
|
ex.ToExceptionless().Submit();
|
||||||
EnrolmentLog.LogSessionError(sessionId, ex);
|
EnrolmentLog.LogSessionError(sessionId, ex);
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
return MacSecureEnrol;
|
return MacSecureEnrol;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,63 +181,6 @@ namespace Disco.BI.DeviceBI
|
|||||||
return ParseMacModelType;
|
return ParseMacModelType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ParseMacShellCommand(SshShell Shell, string Command, string LogSessionId)
|
|
||||||
{
|
|
||||||
Shell.WriteLine(Command);
|
|
||||||
System.Threading.Thread.Sleep(250);
|
|
||||||
string Response = Shell.Expect(SshPromptRegEx);
|
|
||||||
Response = Response.Replace("\r", string.Empty);
|
|
||||||
EnrolmentLog.LogSessionDiagnosticInformation(LogSessionId, Response);
|
|
||||||
bool flag = Response.Contains("\n");
|
|
||||||
string ParseMacShellCommand;
|
|
||||||
if (flag)
|
|
||||||
{
|
|
||||||
string[] ResponseLines = Response.Split(new char[]
|
|
||||||
{
|
|
||||||
'\n'
|
|
||||||
});
|
|
||||||
switch (ResponseLines.Length)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
{
|
|
||||||
ParseMacShellCommand = string.Empty;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
{
|
|
||||||
ParseMacShellCommand = ResponseLines[1].Trim();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
System.Text.StringBuilder ResponseBuilder = new System.Text.StringBuilder();
|
|
||||||
int num = ResponseLines.Length - 2;
|
|
||||||
int lineIndex = 1;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
int arg_111_0 = lineIndex;
|
|
||||||
int num2 = num;
|
|
||||||
if (arg_111_0 > num2)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ResponseBuilder.AppendLine(ResponseLines[lineIndex]);
|
|
||||||
lineIndex++;
|
|
||||||
}
|
|
||||||
ParseMacShellCommand = ResponseBuilder.ToString().Trim();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ParseMacShellCommand = Response;
|
|
||||||
}
|
|
||||||
return ParseMacShellCommand;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public static MacEnrolResponse MacEnrol(DiscoDataContext Database, MacEnrol Request, bool Trusted, string OpenSessionId = null)
|
public static MacEnrolResponse MacEnrol(DiscoDataContext Database, MacEnrol Request, bool Trusted, string OpenSessionId = null)
|
||||||
|
|||||||
@@ -54,9 +54,16 @@
|
|||||||
<Reference Include="itextsharp">
|
<Reference Include="itextsharp">
|
||||||
<HintPath>..\Resources\Libraries\iTextSharp\itextsharp.dll</HintPath>
|
<HintPath>..\Resources\Libraries\iTextSharp\itextsharp.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="PList, Version=0.1.4109.38751, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\packages\plist.net.1.0\lib\Net35\PList.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Quartz">
|
<Reference Include="Quartz">
|
||||||
<HintPath>..\Resources\Libraries\Quartz\Quartz.dll</HintPath>
|
<HintPath>..\Resources\Libraries\Quartz\Quartz.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Renci.SshNet">
|
||||||
|
<HintPath>..\..\..\Resources\Libraries\SshNet\Renci.SshNet.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Spring.Core">
|
<Reference Include="Spring.Core">
|
||||||
<HintPath>..\Resources\Libraries\Spring.NET\Spring.Core.dll</HintPath>
|
<HintPath>..\Resources\Libraries\Spring.NET\Spring.Core.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
@@ -99,9 +106,6 @@
|
|||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="Tamir.SharpSSH">
|
|
||||||
<HintPath>..\Resources\Libraries\SharpSSH\Tamir.SharpSSH.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="zxing">
|
<Reference Include="zxing">
|
||||||
<HintPath>..\Resources\Libraries\ZXing\zxing.dll</HintPath>
|
<HintPath>..\Resources\Libraries\ZXing\zxing.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<package id="Microsoft.Bcl" version="1.1.9" targetFramework="net45" />
|
<package id="Microsoft.Bcl" version="1.1.9" targetFramework="net45" />
|
||||||
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net45" />
|
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net45" />
|
||||||
<package id="Microsoft.Net.Http" version="2.2.22" targetFramework="net45" />
|
<package id="Microsoft.Net.Http" version="2.2.22" targetFramework="net45" />
|
||||||
|
<package id="plist.net" version="1.0" targetFramework="net45" />
|
||||||
<package id="Rx-Core" version="2.2.5" targetFramework="net45" />
|
<package id="Rx-Core" version="2.2.5" targetFramework="net45" />
|
||||||
<package id="Rx-Interfaces" version="2.2.5" targetFramework="net45" />
|
<package id="Rx-Interfaces" version="2.2.5" targetFramework="net45" />
|
||||||
<package id="Rx-Linq" version="2.2.5" targetFramework="net45" />
|
<package id="Rx-Linq" version="2.2.5" targetFramework="net45" />
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// <auto-generated>
|
// <auto-generated>
|
||||||
// This code was generated by a tool.
|
// This code was generated by a tool.
|
||||||
// Runtime Version:4.0.30319.34014
|
// Runtime Version:4.0.30319.42000
|
||||||
//
|
//
|
||||||
// Changes to this file may cause incorrect behavior and will be lost if
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
// the code is regenerated.
|
// the code is regenerated.
|
||||||
@@ -37,9 +37,9 @@ namespace Disco.Web.Views.Shared
|
|||||||
|
|
||||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "2.0.0.0")]
|
[System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "2.0.0.0")]
|
||||||
[System.Web.WebPages.PageVirtualPathAttribute("~/Views/Shared/_Layout.cshtml")]
|
[System.Web.WebPages.PageVirtualPathAttribute("~/Views/Shared/_Layout.cshtml")]
|
||||||
public partial class Layout : Disco.Services.Web.WebViewPage<dynamic>
|
public partial class _Layout : Disco.Services.Web.WebViewPage<dynamic>
|
||||||
{
|
{
|
||||||
public Layout()
|
public _Layout()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
public override void Execute()
|
public override void Execute()
|
||||||
|
|||||||
Reference in New Issue
Block a user