; ============================================================================ ; Machine Authentication 3.0 Network Configuration ; Configures shop floor PCs for 802.1x/ISE Machine VLAN connectivity ; ============================================================================ ; ; This installer configures: ; - Wired network: 802.1x using Corporate Holdings RADIUS servers ; - Wireless network: AESFMA SSID using Aerospace FreeRADIUS servers ; ; Silent Installation: ; MachineAuthSetup.exe /VERYSILENT /SUPPRESSMSGBOXES /LOG="C:\ma3.log" ; ; ============================================================================ [Setup] AppId={{8A3B4C5D-6E7F-8901-2345-6789ABCDEF01}} AppName=Machine Authentication 3.0 AppVersion=3.4 AppPublisher=GE Aerospace DefaultDirName={tmp}\MachineAuth CreateAppDir=no PrivilegesRequired=admin OutputDir=Output OutputBaseFilename=MachineAuthSetup_v3.4 SolidCompression=yes Compression=lzma2 WizardStyle=modern SetupIconFile=gea-logo.ico WizardImageFile=banner.bmp WizardSmallImageFile=banner-sm.bmp DisableWelcomePage=no DisableDirPage=yes DisableProgramGroupPage=yes DisableReadyPage=no DisableFinishedPage=no Uninstallable=no [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" [Messages] WelcomeLabel2=This will configure your PC for Machine Authentication 3.0 VLAN connectivity.%n%nThis configures 802.1x authentication for both wired and wireless network interfaces.%n%nYour network connection will be briefly interrupted during configuration.%n%nClick Next to continue. [Files] ; Include the XML profile files - extract to temp Source: "8021x.xml"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall Source: "AESFMA.xml"; DestDir: "{tmp}"; Flags: ignoreversion deleteafterinstall [Code] const // Network interface names by Windows version WIN7_WIRED = 'Local Area Connection'; WIN7_WIRELESS = 'Wireless Network Connection'; WIN10_WIRED = 'Ethernet'; WIN10_WIRELESS = 'Wi-Fi'; // Network types NET_WIRED = 1; NET_WIRELESS = 2; NET_UNKNOWN = 0; var ProgressPage: TOutputProgressWizardPage; ResultsMemo: TNewMemo; LogMessages: String; // ============================================================================ // LOGGING FUNCTIONS // ============================================================================ procedure LogMsg(const Msg: String); begin LogMessages := LogMessages + Msg + #13#10; Log(Msg); end; // ============================================================================ // UTILITY FUNCTIONS // ============================================================================ function IsWindows10OrLater: Boolean; var Version: TWindowsVersion; begin GetWindowsVersionEx(Version); Result := (Version.Major >= 10); end; function GetWiredInterfaceName: String; begin if IsWindows10OrLater then Result := WIN10_WIRED else Result := WIN7_WIRED; end; function GetWirelessInterfaceName: String; begin if IsWindows10OrLater then Result := WIN10_WIRELESS else Result := WIN7_WIRELESS; end; // Execute a command and return the exit code function ExecCmd(const Executable, Params: String): Integer; var ResultCode: Integer; begin LogMsg('Executing: ' + Executable + ' ' + Params); if Exec(Executable, Params, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then Result := ResultCode else Result := -1; LogMsg(' Result: ' + IntToStr(Result)); end; // Execute a command via cmd.exe function ExecShell(const Command: String): Integer; begin Result := ExecCmd('cmd.exe', '/c ' + Command); end; // Execute netsh command function ExecNetsh(const Params: String): Integer; begin Result := ExecCmd('netsh.exe', Params); end; // Execute sc command for service control function ExecSC(const Params: String): Integer; begin Result := ExecCmd('sc.exe', Params); end; // Execute net command function ExecNet(const Params: String): Integer; begin Result := ExecCmd('net.exe', Params); end; // Check if a network interface exists and is connected function IsInterfaceConnected(const InterfaceName: String; IsWireless: Boolean): Boolean; var TempFile: String; OutputLines: TArrayOfString; I: Integer; Line: String; ResultCode: Integer; begin Result := False; TempFile := ExpandConstant('{tmp}\netcheck_') + IntToStr(Random(99999)) + '.txt'; if IsWireless then Exec('cmd.exe', '/c netsh wlan show interfaces > "' + TempFile + '" 2>&1', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) else Exec('cmd.exe', '/c netsh lan show interfaces > "' + TempFile + '" 2>&1', '', SW_HIDE, ewWaitUntilTerminated, ResultCode); if FileExists(TempFile) then begin if LoadStringsFromFile(TempFile, OutputLines) then begin for I := 0 to GetArrayLength(OutputLines) - 1 do begin Line := OutputLines[I]; // Check for interface name and connected state if (Pos(InterfaceName, Line) > 0) then begin Result := True; Break; end; // Also check for "State" line showing "connected" if (Pos('connected', LowerCase(Line)) > 0) and (Pos('disconnected', LowerCase(Line)) = 0) then begin Result := True; end; end; end; DeleteFile(TempFile); end; end; // Detect which network type is currently active function DetectActiveNetwork: Integer; var WiredInterface, WirelessInterface: String; begin Result := NET_UNKNOWN; WiredInterface := GetWiredInterfaceName; WirelessInterface := GetWirelessInterfaceName; LogMsg('Detecting active network interface...'); LogMsg(' Wired interface name: ' + WiredInterface); LogMsg(' Wireless interface name: ' + WirelessInterface); // Check wired first (preferred) if IsInterfaceConnected(WiredInterface, False) then begin LogMsg(' Active network: WIRED'); Result := NET_WIRED; end else if IsInterfaceConnected(WirelessInterface, True) then begin LogMsg(' Active network: WIRELESS'); Result := NET_WIRELESS; end else begin // Default to wired if can't detect LogMsg(' Could not detect active network, defaulting to WIRED'); Result := NET_WIRED; end; end; // ============================================================================ // SERVICE MANAGEMENT // ============================================================================ procedure StopService(const ServiceName: String); begin LogMsg('Stopping service: ' + ServiceName); ExecNet('stop "' + ServiceName + '"'); end; procedure StartService(const ServiceName: String); begin LogMsg('Starting service: ' + ServiceName); ExecNet('start "' + ServiceName + '"'); end; procedure SetServiceAutoStart(const ServiceName: String); begin LogMsg('Setting service to auto-start: ' + ServiceName); ExecSC('config ' + ServiceName + ' start= auto'); end; // ============================================================================ // NETWORK CONFIGURATION // ============================================================================ function ConfigureWiredNetwork: Boolean; var InterfaceName, ProfilePath: String; begin Result := True; InterfaceName := GetWiredInterfaceName; ProfilePath := ExpandConstant('{tmp}\8021x.xml'); LogMsg(''); LogMsg('=== Configuring Wired Network ==='); LogMsg('Interface: ' + InterfaceName); LogMsg('Profile: ' + ProfilePath); // Enable and start Wired AutoConfig service SetServiceAutoStart('dot3svc'); StartService('Wired AutoConfig'); // Small delay for service to start Sleep(1000); // Import 802.1x profile to wired interface LogMsg('Importing 802.1x profile...'); if ExecNetsh('lan add profile filename="' + ProfilePath + '" interface="' + InterfaceName + '"') <> 0 then begin // Try without interface specification as fallback LogMsg('Retrying without interface specification...'); ExecNetsh('lan add profile filename="' + ProfilePath + '"'); end; LogMsg('Wired network configuration complete.'); end; function ConfigureWirelessNetwork: Boolean; var InterfaceName, ProfilePath: String; begin Result := True; InterfaceName := GetWirelessInterfaceName; ProfilePath := ExpandConstant('{tmp}\AESFMA.xml'); LogMsg(''); LogMsg('=== Configuring Wireless Network ==='); LogMsg('Interface: ' + InterfaceName); LogMsg('Profile: ' + ProfilePath); // Enable and start WLAN AutoConfig service SetServiceAutoStart('Wlansvc'); StartService('WLAN AutoConfig'); // Small delay for service to start Sleep(1000); // Import AESFMA profile to wireless interface LogMsg('Importing AESFMA profile...'); if ExecNetsh('wlan add profile filename="' + ProfilePath + '" interface="' + InterfaceName + '"') <> 0 then begin // Try without interface specification as fallback LogMsg('Retrying without interface specification...'); ExecNetsh('wlan add profile filename="' + ProfilePath + '"'); end; LogMsg('Wireless network configuration complete.'); end; procedure ReconnectNetwork(NetworkType: Integer); var WiredInterface, WirelessInterface: String; begin WiredInterface := GetWiredInterfaceName; WirelessInterface := GetWirelessInterfaceName; LogMsg(''); LogMsg('=== Reconnecting Network ==='); if NetworkType = NET_WIRED then begin LogMsg('Reconnecting wired interface...'); ExecNetsh('lan reconnect interface="' + WiredInterface + '"'); end else if NetworkType = NET_WIRELESS then begin LogMsg('Connecting to AESFMA wireless network...'); ExecNetsh('wlan connect name=AESFMA ssid=AESFMA interface="' + WirelessInterface + '"'); end; end; // ============================================================================ // MAIN CONFIGURATION PROCEDURE // ============================================================================ function PerformConfiguration: Boolean; var ActiveNetwork: Integer; WindowsVersion: String; begin Result := True; LogMessages := ''; // Log Windows version if IsWindows10OrLater then WindowsVersion := 'Windows 10/11' else WindowsVersion := 'Windows 7/8'; LogMsg('Machine Authentication 3.0 Configuration'); LogMsg('========================================'); LogMsg('Windows Version: ' + WindowsVersion); LogMsg(''); // Stop NetworkAdapterManager if it exists (may not exist on all machines) LogMsg('Stopping NetworkAdapterManager (if present)...'); StopService('NetworkAdapterManager'); // Detect active network ActiveNetwork := DetectActiveNetwork; // Configure wired network ProgressPage.SetText('Configuring wired network...', ''); ProgressPage.SetProgress(1, 5); ConfigureWiredNetwork; // Configure wireless network ProgressPage.SetText('Configuring wireless network...', ''); ProgressPage.SetProgress(2, 5); ConfigureWirelessNetwork; // Reconnect active network ProgressPage.SetText('Reconnecting network...', ''); ProgressPage.SetProgress(3, 5); ReconnectNetwork(ActiveNetwork); // Start NetworkAdapterManager if it was stopped LogMsg(''); LogMsg('Starting NetworkAdapterManager (if present)...'); StartService('NetworkAdapterManager'); // Wait for network to stabilize ProgressPage.SetText('Waiting for network to stabilize...', ''); ProgressPage.SetProgress(4, 5); LogMsg(''); LogMsg('Waiting 10 seconds for network to stabilize...'); Sleep(10000); ProgressPage.SetProgress(5, 5); LogMsg(''); LogMsg('========================================'); LogMsg('Configuration complete!'); LogMsg(''); LogMsg('The machine should now have access to the Machine VLAN.'); LogMsg('Test vault access to confirm connectivity.'); end; // ============================================================================ // WIZARD EVENTS // ============================================================================ procedure InitializeWizard; begin // Create progress page ProgressPage := CreateOutputProgressPage('Configuring Network', 'Please wait while Machine Authentication 3.0 is configured...'); end; function InitializeSetup: Boolean; begin Result := True; // Verify admin privileges if not IsAdmin then begin MsgBox('This installer requires administrator privileges.' + #13#10 + 'Please right-click and select "Run as administrator".', mbError, MB_OK); Result := False; end; end; procedure CurStepChanged(CurStep: TSetupStep); var ResultForm: TSetupForm; Memo: TNewMemo; OKButton: TNewButton; begin if CurStep = ssPostInstall then begin // Show progress page and perform configuration ProgressPage.Show; try PerformConfiguration; finally ProgressPage.Hide; end; // Show results in non-silent mode if not WizardSilent then begin ResultForm := CreateCustomForm; ResultForm.Caption := 'Configuration Results'; ResultForm.ClientWidth := 600; ResultForm.ClientHeight := 450; ResultForm.Position := poScreenCenter; Memo := TNewMemo.Create(ResultForm); Memo.Parent := ResultForm; Memo.Left := 10; Memo.Top := 10; Memo.Width := ResultForm.ClientWidth - 20; Memo.Height := ResultForm.ClientHeight - 60; Memo.ScrollBars := ssVertical; Memo.ReadOnly := True; Memo.Text := LogMessages; Memo.Font.Name := 'Consolas'; Memo.Font.Size := 9; OKButton := TNewButton.Create(ResultForm); OKButton.Parent := ResultForm; OKButton.Caption := 'OK'; OKButton.Width := 80; OKButton.Height := 30; OKButton.Left := (ResultForm.ClientWidth - OKButton.Width) div 2; OKButton.Top := ResultForm.ClientHeight - 45; OKButton.ModalResult := mrOK; OKButton.Default := True; ResultForm.ActiveControl := OKButton; ResultForm.ShowModal; ResultForm.Free; end; end; end; function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String; var WinVer, WiredInt, WirelessInt: String; begin if IsWindows10OrLater then WinVer := 'Windows 10/11' else WinVer := 'Windows 7/8'; WiredInt := GetWiredInterfaceName; WirelessInt := GetWirelessInterfaceName; Result := 'Machine Authentication 3.0 Configuration' + NewLine + NewLine + 'The following will be configured:' + NewLine + NewLine + Space + 'Windows Version: ' + WinVer + NewLine + Space + 'Wired Interface: ' + WiredInt + NewLine + Space + 'Wireless Interface: ' + WirelessInt + NewLine + NewLine + 'Actions:' + NewLine + Space + '1. Enable Wired AutoConfig service (dot3svc)' + NewLine + Space + '2. Import 802.1x profile for wired authentication' + NewLine + Space + '3. Enable WLAN AutoConfig service (Wlansvc)' + NewLine + Space + '4. Import AESFMA profile for wireless authentication' + NewLine + Space + '5. Reconnect active network interface' + NewLine + NewLine + 'Note: Network will be briefly interrupted during configuration.'; end;