Schlagwort-Archive: Windows

YubiKey: GPG-Kartenfehler / Sharing Violation unter Windows

Ugh – Windows und Sicherheitsfunktionen passt irgendwie immer noch nicht zusammen. Schon länger nutze ich einen YubiKey als GPG-Smartcard um E-Mails zu signieren und entschlüsseln. Das funktionierte mit GPG4Win und Thunderbird bisher auch recht brauchbar – also bis auf den Windows-GPG-Agent-Bug.

Wie gesagt: Bisher. Heute dann ein etwas seltsamer Bug:

# gpg --card-status
gpg: selecting openpgp failed: Card error
gpg: OpenPGP card not available: Card error

Wat. Schnell nochmal den Daemon durchstarten: Nix. Im Log findet man folgende Info:

scdaemon: pcsc_connect failed: sharing violation (0x8010000b)
scdaemon: reader slot 0: not connected

Bild: https://www.adlerweb.info/blog/wp-content/uploads/2017/02/wp-1485952629803-300×225.jpgWait. Sharing violation? Greift sonst noch wer zu? Jepp, natürlich tut das Jemand. Ich hatte zwischenzeitlich auf dem YubiKey für ein anderes System X.509-Zertifikate (aka PIV) eingerichtet. Diese kommen z.B. zur Authentifizierung zum Einsatz und können unter Windows auch z.B. für Remote-Logins und das entschlüsseln von Bitlocker-Festplatten verwendet werden. Entsprechend hat nun also auch Windows die PIV-Smartcard gefunden und belagert das Device dauerhaft – somit ist der Zugriff für gpg nicht mehr möglich. Abhilfe schafft hier den Dienst „CertPropSvc“ (aka „Zertifikatverteilung“) zu beenden bzw. neuzustarten. In letzterem Fall bleibt das Gerät frei bis man die nächste Software mit PIV-Zugriff (z.B. Remotedesktop) startet.

Also als weiterer Punkt auf dem nicht enden wollenden Wunschzettel für einen würdigen Nachfolger für die leider sicherheitstechnisch bedenklich gewordenen YubiKeys: Parallelität der SC-Reader…

Windows/GPG/Smartcard – GPG mit Powershell bei SC-Verbindung neu starten

Beim Thema E-Mail-Verschlüsselung ist GPG weit verbreitet. Die Gundfunktionen sind z.B. mit Thunderbird und Enigmail oder gar eine Appliance auch für Einsteiger nutzbar und durch das WoT erstpart man sich die Abhängigkeit von – teils Zweifelhaften – zentralen Zertifizierungsstellen. Ich selbst nutze seit einiger Zeit keinen Softwareschlüssel sondern ein dediziertes HSM (Hardware Security Module) in Form eines YubiKey. Hiermit befindet sich der Key nicht mehr auf dem PC und auch die Verschlüsselung/Signierung läuft in Teilen auf dem Prozessor des YubiKey. Was unter Linux ohne große Änderungen funktioniert macht bei Windows eher wenig Spaß. Zwar ist auch GPG4Win in der Lage mit CryptoCards & Co umzugehen, häufiges Ein- und Ausstecken führt jedoch schnell zu einem hängenden GPG-Agent oder Absturzen der scdaemon.exe (GPG SmartCard Daemon). Einzige Abhilfe: Alle GPG-Prozesse beenden und neu starten. Da Verschlüsselung und Windows ohnehin eher selten zu finden ist sind auch Informationen dazu dünn gesät. Da ich teilweise um das System aus Redmond nicht herum komme und ein Fix in GPG4Win nicht in Sichtweite ist muss also wieder einmal der Holzhammer her: Irgendwie muss beim Einstecken des USB-Gerätes das GPG-System zurückgesetzt werden.

Also frisch ans Werk. Als Sprache soll mal wieder Powershell herhalten – direkt verfügbar, halbwegs systemnah, sollte passen. Über WMI kann man sich auch über ab- und angesteckte Geräte informieren lassen. Leider konnte ich keine Möglichkeit finden weitere Infos wie z.B. Hersteller oder Treiber gleich mit zu erhalten. Da sonst eher wenig Gerätefluktuation zu erwarten ist ziehe ich die „passt scho“-Karte und werde das GPG-System bei jedem neu auftauchenden Gerät einmal zurücksetzen.

#Requires -version 2.0
Register-WmiEvent -Class Win32_DeviceChangeEvent -SourceIdentifier deviceChange
write-host (get-date -format s) " Beginning script..."
do {
    $newEvent = Wait-Event -SourceIdentifier deviceChange
    $eventType = $newEvent.SourceEventArgs.NewEvent.EventType
    $eventTypeName = switch($eventType) {
        1 {"Configuration changed"}
        2 {"Device arrival"}
        3 {"Device removal"}
        4 {"docking"}
    }
    write-host (get-date -format s) " Event detected = " $eventTypeName

    $newEvent.SourceEventArgs.NewEvent | fl

    if ($eventType -eq 2) {
            write-host (get-date -format s) " Starting task in 3 seconds..."
            start-sleep -seconds 3
            taskkill /T /F /IM gpg-agent.exe
            taskkill /T /F /IM scdaemon.exe
            taskkill /T /F /IM kleopatra.exe

            start -FilePath 'C:\Program Files (x86)\GNU\GnuPG\gpg-agent.exe' -ArgumentList '--daemon' -WorkingDirectory 'C:\Program Files (x86)\GNU\GnuPG\' -LoadUserProfile -WindowStyle Minimized
    }
    Remove-Event -SourceIdentifier deviceChange
} while (1-eq1) #Loop until next event
Unregister-Event -SourceIdentifier deviceChange

Das Grundkonstrukt selbst scheint von Andy.L13 zu stammen, die kläglichen versuche von Mass-Storage auf andere USB-Geräte umzustellen stammen von mir. In Zeile 24 kann man für WindowStyle statt Minimized auch Hidden nutzen um das Ganze im Hintergrund laufen zu lassen – ich bevorzuge ein optisches Feedback. Das Script kann passend in Autostart oder als geplanter Task verstaut werden.

Sicher keine saubere Lösung, aber „works for me“.


Edit: Stackoverflow (bzw warriorpostman) liefert

Das folgende Script reagiert nur noch auf den vom YubiKey emulierten Smartcard-Reader. Das Objekt $Event.SourceEventArgs.NewEvent.TargetInstance sieht wie folgt aus – die Informationen für’s Query sollten sich im Gerätemanager finden lassen:

__GENUS                     : 2
__CLASS                     : Win32_PnPEntity
__SUPERCLASS                : CIM_LogicalDevice
__DYNASTY                   : CIM_ManagedSystemElement
__RELPATH                   : Win32_PnPEntity.DeviceID="USB\\VID_1050&PID_0116&MI_02\\6&***&0&0002"
__PROPERTY_COUNT            : 24
__DERIVATION                : {CIM_LogicalDevice, CIM_LogicalElement, CIM_ManagedSystemElement}
__SERVER                    : TESTRECHNER
__NAMESPACE                 : root\CIMV2
__PATH                      : \\TESTRECHNER\root\CIMV2:Win32_PnPEntity.DeviceID="USB\\VID_1050&PID_0116&MI_02\\6&***&0&0002"
Availability                : 
Caption                     : Microsoft Usbccid-Smartcard-Leser (WUDF)
ClassGuid                   : {50dd5230-ba8a-11d1-bf5d-0000f805f530}
CompatibleID                : {USB\Class_0b&SubClass_00&Prot_00, USB\Class_0b&SubClass_00, USB\Class_0b}
ConfigManagerErrorCode      : 0
ConfigManagerUserConfig     : False
CreationClassName           : Win32_PnPEntity
Description                 : Microsoft Usbccid-Smartcard-Leser (WUDF)
DeviceID                    : USB\VID_1050&PID_0116&MI_02\6&***&0&0002
ErrorCleared                : 
ErrorDescription            : 
HardwareID                  : {USB\VID_1050&PID_0116&REV_***&MI_02, USB\VID_1050&PID_0116&MI_02}
InstallDate                 : 
LastErrorCode               : 
Manufacturer                : Microsoft
Name                        : Microsoft Usbccid-Smartcard-Leser (WUDF)
PNPDeviceID                 : USB\VID_1050&PID_0116&MI_02\6&***&0&0002
PowerManagementCapabilities : 
PowerManagementSupported    : 
Service                     : WUDFRd
Status                      : OK
StatusInfo                  : 
SystemCreationClassName     : Win32_ComputerSystem
SystemName                  : TESTRECHNER
PSComputerName              : TESTRECHNER

Ich werde mich am Service WUDFRd (aka Gerät mit Windows SmartCard-Treber) orientieren. Prinzipiell könne man aber auch auf die USB-ID triggern uns so Scripte bauen, welche bei einstecken eines USB-Sticks ein Backup durchführen oder bei verbinden einer unbekannten USB-Tastatur entsprechend reagieren (böse Ente). Das zugehörige Script:

$query =  "Select * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_PnPEntity' AND TargetInstance.Service = 'WUDFRd'"

if( Get-EventSubscriber | Where-Object {$_.SourceIdentifier -eq "YubiHammer"}) {
    Unregister-Event YubiHammer
}

Register-WmiEvent -Query $query -SourceIdentifier YubiHammer -Action{
    $Global:RemoteProcMon=$event 

    start-sleep -seconds 3
    taskkill /T /F /IM gpg-agent.exe
    taskkill /T /F /IM scdaemon.exe
    taskkill /T /F /IM kleopatra.exe

    start -FilePath 'C:\Program Files (x86)\GNU\GnuPG\gpg-agent.exe' -ArgumentList '--daemon' -WorkingDirectory 'C:\Program Files (x86)\GNU\GnuPG\' -LoadUserProfile -WindowStyle Minimized
}

 

BitNotice #102 – Windows Server für’s Klonen vorbereiten (SYSPREP)

BitNotice #102 - Windows Server für's Klonen vorbereiten (SYSPREP)

(5 MB) 00:04:15

2016-07-19 19:34 🛈

Spätestens wenn man einen Haufen Server benötigt und eine Virtualisierung einsetzt möchte man nicht dutzende male das Windows-Setup durchführen und seine wichtigsten Tools per Hand installieren. Einfach die Festplatte zu kopieren ist aber auch keine wirkliche Lösung, denn Windows hat intern IDs, welche im lokalen Netzwerk besser nicht mehrmals vorkommen sollten. Über das Tool „Sysprep“, welches praktischerweise in der der Standardinstallation schon dabei ist, kann man eine Windows-Installation schnell von eindeutigen Merkmalen befreien und so für das Klonen vorzubereiten. Das Tool steht in allen aktuellen Versionen zur Verfügung.

Bitte beachtet, dass je nach Windows-Lizenz nur eine gewisse Anzahl, Art und Hostkonfiguration abgedeckt ist, ein genauer Blick in die Lizenzvereinbarung ist entsprechend Pflicht.

Ergänzungen:

  • 2:14 – …159265… (SCNR)
  • 3:27 – Neu im Sinne von Windows-Einstellungen wurden zurückgesetzt, installierte Programme, Updates, etc bleiben erhalten

BitBastelei #195 – Windows 2012R2 Failovercluster mit VMware

BitBastelei #195 - Windows 2012R2 Failovercluster mit VMware

(42 MB) 00:26:35

2016-05-01 10:00 🛈

Wieder mal eine Runde „Serverzeugs“: Windows Server 2012 R2 kann über „Failoverclustering“ eine Hochverfügbarkeit bereitstellen. Hierzu müssen alle Clusterknoten auf einen gemeinsamen Speicher direkt zugreifen können. Kommt eine Virtualisierungsinfrastruktur wie VMWare hinzu muss man etwas basteln um eine stabile Funktion zu erreichen.

VMWare KB1037959: Microsoft Clustering on VMware vSphere: Guidelines for supported configurations

Ranttime #10 – Übertriebene Usability: FreeDownloadManager

Der FreeDownloadManager war immer meine Software der Wahl wenn es darum ging unter Windows größere Mengen an Dateien aus dem Internet zu laden. Mit der neuen Version hat man aber „die Usability verbessert“. Oder anders gesagt: Alle Funktionen entfernt um die Oberfläche Idiotensicher zu machen.

BitNotice #95 – Windows 10 Shenanigans | Edge vs. Firefox | Cortana-Spaß

BitNotice #95 - Windows 10 Shenanigans | Edge vs. Firefox | Cortana-Spaß

(37 MB) 00:34:43

2015-08-21 15:36 🛈

Irgnedwo zwischen BitNotice und Ranttime: Ein Linux-User installiert Windows 10.
16:42 Spaß mit Cortana
27:50 Microsoft Edge vs. Mozilla Firefox

Software als Windows-Service

Programme starten ist toll. Einige müssen aber immer wieder gestartet werden, hierzu nutzt man unter Windows so genannte „Dienste“. Leider sind das unter Windows NT (also auch z.B. Windows 7/8/10, Windows Server 2008/2012) keine einfachen Programme, sondern erfordern spezielle Registrierungen. Leider gent so etwas (soweit mir bekannt) nicht mit Hausmitteln.

Abhilfe schafft ein kleines Tool: winserv verwandelt ein beliebiges Programm in einen passenden Dienst. Hierbei gibt es folgende Modi:

install richtet ein Programm als Dienst ein
configure ändert einen vorhandenen Dienst
uninstall entfernt den Dienst

Zum Anlegen gibt man folgende Parameter:

-displayname Angezeigter Name des Dienstes
-description Beschreibung (das, was in der Diensteverwaltung in der zweiten Spalte steht)
-start Start-Art des Dienstes – „auto“ „demand“ oder „disabled“ (Bedeutung selbstredend, oder?)

Beispiel:

winserv install testdienst -displayname "Test-Dienst" -description "Test 123" -start auto c:\BLA.EXE

Alternativ kann man auch einen Geplanten Task mit dem Trigger „Bei Systemstart“ verwenden, hier muss man jedoch auf die komfortableren Prüf- und Verwaltungsfunktionen des Dienst-Managers verzichten.

Q’n’D Windows-Shutdown-Daemon in PHP

Alles in Deckung, ich mache wieder unschönes Zeugs. Ich hatte ja bereits erklärt wie man über RPC/Samba einen Windows-PC von Linux herunterfahren kann. Leider ist die Installation von Samba aus Embedded-Systemen eine eher ungünstige Sache und ist in meinem Fall wegen Speichermangel nicht drin. Allerdings ist sowohl auf dem Embedded-System als auch dem betroffenen Rechner PHP verfügbar. I aim to misbehave.

Auf dem herunterzufahrenden Windows-Rechner wird per PHP ein TCP-Socket geöffnet. Dieser Code basiert auf einem Beispiel von Michael Kliewe. Um Scriptkiddies nicht direkt Zugang zu geben ist etwas (sehr schlechte) Challenge-Response-Authentifizierung drin – nunja, ist kein kritisches System. Wird der Client bestätigt folgt ein simples Shutdown per exec. Nicht viel, aber schnell Fertig und funktioniert:

<?php
class SocketChatServer {
    private $address = '0.0.0.0';   // 0.0.0.0 means all available interfaces
    private $port = 12345;          // the TCP port that should be used
    private $maxClients = 10;
 
    private $clients;
    private $socket;
    
    private $salt = 'YourSalt';
    private $pass = 'YourSecret';
 
    public function __construct() {
        // Set time limit to indefinite execution
        set_time_limit(0);
        error_reporting(E_ALL ^ E_NOTICE);
    }
 
    public function start() {
        // Create a TCP Stream socket
        $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        // Bind the socket to an address/port
        socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, 1);
        socket_bind($this->socket, $this->address, $this->port);
        // Start listening for connections
        socket_listen($this->socket, $this->maxClients);
 
 
        $this->clients = array('0' => array('socket' => $this->socket));
 
        while (true) {
            // Setup clients listen socket for reading
            $read[0] = $this->socket;
            for($i=1; $i<count ($this->clients)+1; ++$i) {
                if($this->clients[$i] != NULL) {
                    $read[$i+1] = $this->clients[$i]['socket'];
                }
            }
 
            // Set up a blocking call to socket_select()
            $ready = socket_select($read, $write = NULL, $except = NULL, $tv_sec = NULL);
 
            /* if a new connection is being made add it to the client array */
            if(in_array($this->socket, $read)) {
                for($i=1; $i < $this->maxClients+1; ++$i) {
                    if(!isset($this->clients[$i])) {
                        $this->clients[$i]['socket'] = socket_accept($this->socket);
                        socket_getpeername($this->clients[$i]['socket'], $ip);
                        $this->clients[$i]['ipaddy'] = $ip;
 
                        socket_write($this->clients[$i]['socket'], 'Welcome to the Shutdown Control System'."\r\n");
                        socket_write($this->clients[$i]['socket'], 'Your personal code is '.md5($this->salt.strftime('%Y-%m-%d').$ip)."\r\n");
 
                        $this->log("New client #$i connected: " . $this->clients[$i]['ipaddy']);
                        break;
                    } elseif($i == $this->maxClients - 1) {
                        $this->log('Too many Clients connected!');
                    }
 
                    if($ready < 1) {
                        continue;
                    }
                }
            }
 
            // If a client is trying to write - handle it now
            for($i=1; $i<$this->maxClients+1; ++$i) {
                if(in_array($this->clients[$i]['socket'], $read)) {
                    $data = @socket_read($this->clients[$i]['socket'], 1024, PHP_NORMAL_READ);
 
                    if($data === FALSE) {
                        unset($this->clients[$i]);
                        $this->log('Client disconnected!');
                        continue;
                    }
 
                    $data = trim($data);
 
                    if(!empty($data)) {
                        switch ($data) {
                            case 'exit':
                            case 'quit':
                                socket_write($this->clients[$i]['socket'], "Disconnecting without any action, Goodbye.\r\n");
                                $this->log("Client #$i is exiting");
                                unset($this->clients[$i]);
                                continue;
                            case 'shutdown-'.md5($this->pass.md5($this->salt.strftime('%Y-%m-%d').$ip)):
                                // first write a message to all connected clients
                                for($j=1; $j < $this->maxClients+1; ++$j) {
                                    if(isset($this->clients[$j]['socket'])) {
                                        if($this->clients[$j]['socket'] != $this->socket) {
                                            socket_write($this->clients[$j]['socket'], "System will be shut down now...\r\n");
                                        }
                                    }
                                }
                                // Close the master sockets, server termination requested
                                exec('shutdown /s /t 15 /c "System wurde per Fernbedienung ausgeschaltet"');
                                $this->log("Terminated server (requested by client #$i)");
                                exit;
                            case 'whoami':
                                // first write a message to all connected clients
                                socket_write($this->clients[$i]['socket'], "You are registred with the source IP ".$this->clients[$i]['ipaddy']." - your Code was ".md5($this->salt.strftime('%Y-%m-%d').$ip)." \r\n");
                                
                                break(2);
                            default:
                                for($j=1; $j < $this->maxClients+1; ++$j) {
                                    if(isset($this->clients[$j]['socket'])) {
                                        if(($this->clients[$j]['socket'] != $this->socket)) {
                                            $this->log($this->clients[$i]['ipaddy'] . ' is sending a message to ' . $this->clients[$j]['ipaddy'] . '!');
                                            socket_write($this->clients[$j]['socket'], '[' . $this->clients[$i]['ipaddy'] . '] says: ' . $data . "\r\n");
                                        }
                                    }
                                }
                                break(2);
                        }
                    }
                }
            }
        } // end while
    }
 
    private function log($msg) {
        // instead of echoing to console we could write this to a database or a textfile
        echo "[".date('Y-m-d H:i:s')."] " . $msg . "\r\n";
    }
}

$srv = new SocketChatServer;
$srv->start();
?>

Der Client bastelt sich eine passende Authentifizierung und spielt Zündknopf:

function pc_shutdown() {                                                                                                                                                                                                              
                $pass = 'YoutSecret';                                                                                                                                                                                                         
                                                                                                                                                                                                                                              
                $fp = fsockopen('pc.name', 12345, $errno, $errstr, 2);                                                                                                                                                                              
                if(!$fp) return false;                                                                                                                                                                                                        
                                                                                                                                                                                                                                              
                $line = fgets($fp); //Greeting                                                                                                                                                                                                
                $line = fgets($fp); //Hash                                                                                                                                                                                                    
                                                                                                                                                                                                                                              
                preg_match('/code is ([\w\d]{32})/', $line, $match);                                                                                                                                                                          
                $seed = $match[1];                                                                                                                                                                                                            
                                                                                                                                                                                                                                              
                $cmd = 'shutdown-'.md5($pass.$seed)."\n";                                                                                                                                                                                     
                                                                                                                                                                                                                                              
                fwrite($fp, $cmd);                                                                                                                                                                                                            
                                                                                                                                                                                                                                              
                var_dump(fgets($fp));                                                                                                                                                                                                         
                                                                                                                                                                                                                                              
                fclose($fp);                                                                                                                                                                                                                  
        }

Hoffen wir, dass es läuft und ich nie wieder drauf schauen muss, denn irgendwo tut sowas selbst mir weh 😉

Windows Shutdown per Linux

Ab und an möchte man entfernte Rechner mal runterfahren. Mit Linux-Zielen ist das per ssh üblicherweise kein Problem, möchte man jedoch von einem Linux-PC den Shutdown-Befehl an einen Windows-Rechner geben muss man schon einmal suchen. Abhilfe schafft wie üblich Samba, welches den „net“-Befehl mitbringt. Ein Shutdown sieht dann wie folgt aus:

net rpc SHUTDOWN -C "Grund für den Shutdown" -f -I 127.0.0.1 -U username%password

Logrotate für Windows-Webserver IIS

Seriously, Microsoft? Der in Windows integrierte Webserver IIS kann problemlos vernünftige Log-Dateien erzeugen und besitzt auch eine Funktion diese bei Erreichen einer Zeitspanne oder eines Größenlimits zu schließen und eine neue beginnen. Was leider fehlt: Alte Dateien löschen. Es kommt was kommen musste: Auf einem Produktivserver durfte ich mehr als 100GB an logtechischen Textkauderwelsch finden. Platte voll.

Zwar kann man hier mit der in NTFS verfügbaren Ordnerkompression gegensteuern, eine Dauerlösung ist das aber sicher nicht. Am Ende habe ich auf Basis eines Powershell-Scriptes von Daniel Schroeder aka deadlydog etwas passendes gebastelt um hier Abhilfe zu schaffen. Regelmäßig über die Aufgabenplanung aufgerufen bleibt so der Ordner auf einem erträglichen Maß.

$limit = 15 #Delete Logs after 15 Days
$path = "C:\inetpub\logs\LogFiles\W3SVC1"

# Function to remove all files in the given Path that have not been modified after the given date.
# original written by deadlydog / Daniel Schroeder, http://blog.danskingdom.com/powershell-functions-to-delete-old-files-and-empty-directories/
# Requires Powershell >=2.0
function Remove-FilesNotModifiedAfterDate([parameter(Mandatory)][ValidateScript({Test-Path $_})][string] $Path, [parameter(Mandatory)][DateTime] $DateTime)
{
    Get-ChildItem -Path $Path -Recurse -Force -File | Where-Object { $_.LastWriteTime -lt $DateTime } | Remove-Item
    #Get-ChildItem -Path $Path -Recurse -Force -File | Where-Object { $_.LastWriteTime -lt $DateTime } | Format-List BaseName,LastWriteTime
}

Remove-FilesNotModifiedAfterDate -Path "$path" -DateTime ((Get-Date).AddDays(1-$limit))

Zum Ausführen wird mindestens PowerShell 2.0 benötigt. Ältere Betriebssysteme wie z.B. 2008R2 bringen nur PowerShell 1 mit, hier muss manuell über das Windows Management Framework aktualisiert werden.