Skip to content

Instantly share code, notes, and snippets.

Created September 10, 2015 21:25
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save anonymous/927bf967fc0902556ae5 to your computer and use it in GitHub Desktop.
Save anonymous/927bf967fc0902556ae5 to your computer and use it in GitHub Desktop.
How to run Linux desktop in a corporate environment

How to run Linux desktop in a corporate environment

DISCLAIMER

Some of the practices described in this HOWTO are considered to be illegal as they often break internal corporate policies. Anything you do, you do at your own risk.

Description

This HOWTO will show how to make running most of the tools used in a corporate environment under Linux. This HOWTO assumes that the company is using Windows as their main operating system and Cisco-based infrastructure (WiFi, VPN, communication tools).

Why to use Linux?

Just to name few reasons:

  • No license fees
  • Stability, security and privacy
  • No need to run antivirus software
  • Better package management
  • Better automation of tasks
  • Better window management
  • Freedom

Distribution

Big corporations are either not supporting Linux desktops at all or are thinking to support the same Linux distribution for their production servers as well as for desktop. The second option is definitely more optimistic although still not very good. The Linux distribution used on servers is usually either RedHat Enterprise Linux or one of its derivatives (CentOS, Oracle Linux, ...). Those are good choice for servers but definitely not for desktop. More suitable distribution for desktop are Ubuntu, Fedora, Arch Linux or any other cutting edge distribution.

User's certificates

Almost every corporation is using Microsoft Windows as their main desktop operating system. Windows desktops are usually secured with an user-specific certificate. This certificate is usually not possible to export and the only access to the certificate is usually provided only through a Public Key Infrastructure (e.g. Cisco PKI client) which normally doesn't support Linux. This is the root of all problems to run Linux desktop in the corporate environment. In order to make things working on Linux, we must get the user's certificate from Windows.

User's certificate extraction

For this we can use Mimikatz. Mimikatz allows to extract user's certificate directly from the Windows memory. In order to run Mimikatz, the Windows firewall/antivirus must be disabled (ask some friendly desktop support guy to help you with that). As Mimikatz extracts all certificates by default, we need to identify which of them is actually the user's certificate. Once the certificates are exported, you can delete all the installed certificates (run certmgt.msc command on Windows). Then import certificates one by one and try to connect to the corporate WiFi (and/or VPN). Once the connection is established, you found the user's certificate.

Mimikatz secures all the exported certificates with a default password so it's recommended to change it:

$ openssl pkcs12 -des3 -in CURRENT_USER_My_1_<username>.pfx -out user_cert.pfx

Now we can delete the original certificate:

$ rm -f ./CURRENT_USER_My_1_<username>.pfx

Public certificate and private key extraction (optional)

The user's certificate has two parts - the public certificate and the private key. You can extract each of them from the .pfx file like this:

$ openssl pkcs12 -in user_cert.pfx -nodes -nocerts -out Key.pem
$ openssl pkcs12 -in user_cert.pfx -nodes -nokeys -clcerts -out ClientCert.pem

And set password on the private key:

$ openssl rsa -aes256 -in Key.pem -out KeyP.pem

Now we can delete the unsecured key:

$ rm -f ./Key.pem

Using YubiKey NEO

Although we can secure the private key and with a password, it is still not very secure as anybody who get access to our PC can steal them. We can extend the security by encrypting the filesystem (eg. ecryptfs, dm-crypt) and allow to decrypt it only when we login.

This is still not very secure as the private key is still available as a file. But if we use Yubikey NEO, we can upload the public certificate and the private key on it and nobody, including us, can ever extract the private key from it. That makes it a very secure solution. That's possible only thanks to the NEO's Privilege and Identification Card (PIV) interface used to provide a platform-independent API to cryptographic tokens via PKCS#11 standard. That makes the Yubikey to work like a smart card. Access to the PIV interface is secured by a PIN - only person who knows the password can use the Yubikey to authenticate.

To load the user's certificate on the Yubikey, we can either use GUI or CLI. Go ahead and use the GUI to set the PIN/PUK and import the user's certificate (user_cert.pfx) into the Authenticate slot (9a). I will show here how to do it with the CLI as it allow us to do more things (e.g. change the number of PIN/PUK attempts).

First we block the PIN and PUK to be able to reset the PIV application (both must be blocked before we can reset the application). Bear in mind that this action will destroy all keys stored in PIV application:

$ for N in $(seq 3); do yubico-piv-tool -a verify-pin -P wrongpin; yubico-piv-tool -a change-puk -P wrongpuk -N wrongpuk; done
$ yubico-piv-tool -a reset

First we generate and set a new Management KEY (24 bits in hex):

$ dd if=/dev/random bs=1 count=24 2>/dev/null | hexdump -v -e '/1 "%02X"' | gpg -c -a -o MgmtKey.gpg
$ KEY=$(gpg -d MgmtKey.gpg 2>/dev/null)
$ yubico-piv-tool -a set-mgm-key -n $KEY

Keep the MgmtKey.gpg file secure for later use. Then we set PIN (4-8 chars) and PUK (4-8 chars):

$ read -s -p "Type your new PIN: " PIN
$ read -s -p "Type your new PUK: " PUK
$ yubico-piv-tool -a change-pin -P 123456 -N $PIN
$ yubico-piv-tool -a change-puk -P 12345678 -N $PUK

Upload the user's certificate onto the Yubikey:

$ read -s -p "Type the user's certificate password: " PASS
$ yubico-piv-tool -s 9a -a import-key -a import-cert -a set-chuid -i user_cert.pfx -K PKCS12 -p $PASS -k $KEY

Verify that the user's certificate is installed:

$ yubico-piv-tool -a status

Clean the KEY, PIN, PUK and PASS from the environment (or simply exit the shell):

$ unset KEY PIN PUK PASS

The following commands are showing other potentially useful tasks. If we by accident block the PIN (3 unsuccessful attempts), we can unblock it with the PUK:

$ yubico-piv-tool -a unblock-pin -N $PIN --pin $PUK

If the PUK is blocked (3 unsuccessful attempts), the PIV applet will be blocked and we need to reset it (see above).

We can change the number of attempts for PIN/PUK:

$ yubico-piv-tool -a pin-retries --pin-retries=10 --puk-retries=5 -k $KEY

We can change the PIN/PUK:

$ yubico-piv-tool -a change-pin -P $PIN -N $NEW_PIN
$ yubico-piv-tool -a change-puk -P $PUK -N $NEW_PUK

We can extract the public certificate (not the private key) from the Yubikey:

$ yubico-piv-tool -a read-certificate -s 9a > ClientCert.pem

WiFi

In order to connect to the WiFi from Linux, we need to have the user's certificate (see above how to get it). Then we can use either the .pfx file directly (not recomended):

# Path to the control interface
ctrl_interface=/var/run/wpa_supplicant

# Allow users from the group wheel to connect to the control interface
ctrl_interface_group=wheel

# Some more params
eapol_version=1
ap_scan=1
fast_reauth=1
update_config=1

# Using the .pfx file (not recomended)
network={
	ssid="CORPORATE"
	key_mgmt=WPA-EAP
	eap=TLS
	identity="none"

	# Cert and key from the .pfx file
	private_key="/path/to/your/user_cert.pfx"

	# Run wpa_gui or wpa_cli to be asked for the password
	#private_key_passwd="password"
}

Or we can use the user's certificate stored on the Yubikey (change the value of disabled=1 to 0 for the relevant block):

# Path to the control interface
ctrl_interface=/var/run/wpa_supplicant

# Allow users of the group wheel to connect to the interface
ctrl_interface_group=wheel

# Some more params
eapol_version=1
ap_scan=1
fast_reauth=1
update_config=1

# Make the pkcs11 engine available (requires engine_pkcs11 > v0.1.8 and libp11 > v2.8)
pkcs11_engine_path=/usr/lib/engines/engine_pkcs11.so

# Configure the path to the pkcs11 module required by the pkcs11 engine
pkcs11_module_path=/usr/lib/pkcs11/opensc-pkcs11.so

# Using Yubikey - for wpa_supplicant < v2.4
network={
	disabled=1
	ssid="CORPORATE"
	key_mgmt=WPA-EAP
	eap=TLS
	identity="none"

	# Use yubico-piv-tool to read the certificate
	client_cert="/path/to/your/ClientCert.pem"

	# Select the right engine
	engine=1
	engine_id="pkcs11"

	# Use "pkcs11-tool -L" to get the slot number
	# Use "pkcs11-tool -O" or "pkcs15-tool --list-keys" to get the ID
	key_id="slot_1-id_01"

	# Run wpa_gui or wpa_cli to be asked for the PIN - requires patch
	#pin="password"
}

# Using Yubikey - for wpa_supplicant >= v2.4
network={
	disabled=1
	ssid="CORPORATE"
	key_mgmt=WPA-EAP
	eap=TLS
	identity="none"

	# Use "p11tool --list-tokens | grep =piv" to get the value
	client_cert="pkcs11:manufacturer=piv_II;id=%01"
	private_key="pkcs11:manufacturer=piv_II;id=%01"

	# There is no password so define is as an empty string
	private_key_passwd=""

	# Run wpa_gui or wpa_cli to be asked for the PIN - requires patch
	#pin="password"
}

It's recommended to keep running wpa_gui or wpa_cli in order to be asked for the password or PIN interactively. It's more secure than keep the password or PIN written in the wpa_supplicant.conf file.

In the case of using Yubikey, it's necessary to apply the following patch and re-build the wpa_supplicant:

diff -u -r a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
--- a/src/eap_peer/eap_tls_common.c	2015-03-15 18:30:39.000000000 +0100
+++ b/src/eap_peer/eap_tls_common.c	2015-08-17 14:29:52.000000000 +0200
@@ -204,6 +204,11 @@
 		 */
 		os_free(config->pin);
 		config->pin = NULL;
+		eap_sm_request_pin(sm);
+		sm->ignore = TRUE;
+		tls_connection_deinit(data->ssl_ctx, data->conn);
+		data->conn = NULL;
+		return -1;
 	} else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) {
 		wpa_printf(MSG_INFO, "TLS: Failed to load private key");
 		/*

That will make sure that the PIN is requested via wpa_gui or wpa_cli.

VPN

VPN is normally used to secure connection to the corporate network. Corporat solution for that are usually supported only for Windows desktops. The only tolerated solution for Linux desktop is by using virtualized Windows guest and tunnel the VPN connection to Linux host. Such solution is usually slow, impractical and mainly huge waste of resources. Therefore it's desirable to have native Linux client.

Cisco AnyConnect

Connection to Cisco AnyConnect VPN is possible with OpenConnect client. The best way how to connect to this device is to use OpenConnect client. The connection is usually secured by an user's certificate and the Cisco Secure Desktop (CSD) policy. CSD is a program which is stored on the VPN device and runs before every connection to verify certain facts about the client device. Full list of parameters sent by the CSD check back to the server can be found on Windows in the AnyConnect client log file (C:\Users\<username>\AppData\Local\Cisco\Cisco HostScan\log\cscan.log).

Just for information, the proprietary CSD for Linux is stored on the VPN device and can be downloaded like this:

# Change i386 to x64 for 64-bit binaries
$ for N in $(wget -q -O - https://vpn.company.com/CACHE/sdesktop/hostscan/linux_i386/manifest | sed -r 's/.*\(//;s/\).*//'); do wget -q https://vpn.company.com/CACHE/sdesktop/hostscan/linux_i386/$N; done

CSD policy can be configured to deny connections from Linux clients. This can be verified in the policy file:

$ wget -q -O - https://vpn.company.com/CACHE/sdesktop/data.xml | grep -A1 'os_check.*linux' | grep denied 1>/dev/null && echo "Linux is denied" || echo "Linux is enabled"

If the Linux connection is denied, OpenConnect allows to fake CSD output with a script so the VPN server thinks that the connection was established from a client which is trusted:

$ cat <<SCRIPT > /path/to/the/csd-wrapper.sh
#!/bin/bash

function run_curl {
  curl \\
  --insecure \\
  --user-agent "AnyConnect Windows \$ver" \\
  --header "X-Transcend-Version: 1" \\
  --header "X-Aggregate-Auth: 1" \\
  --header "X-AnyConnect-Platform: \$plat" \\
  --cookie "sdesktop=\$token" \\
  "\$@"
}

set -e

host=https://\$CSD_HOSTNAME
plat=win
ver=3.1.02043
token=\$CSD_TOKEN

###
# Here goes the list of parameters which should be sent back to the server.
# By default, only the endpoint.policy.location parameter is required.
###
# Get the value of the <policy_location> on Windows from the cscan.log file
# OR
# get it from the policy file:
# wget -O - -q https://vpn.company.com/CACHE/sdesktop/data.xml | grep 'location name='
###
run_curl --data-ascii @- "\$host/+CSCOE+/sdesktop/scan.xml?reusebrowser=1" <<-END
endpoint.policy.location="<policy_location>";
END

exit 0
SCRIPT

Make it executable:

$ chmod +x /path/to/the/csd-wrapper.sh

The CSD script can be used either on the command line or in the configuration file:

$ cat <<END > /path/to/the/openconnect.conf
# Silence the output
quiet

# Prevent error message in the syslog
no-dtls

# Run the process on background after connection is established
background

# Send all logs to syslog after the connection is established
syslog

# Where to store the process ID
pid-file=/var/run/openconnect.pid

# User's certificate
certificate=/path/to/your/ClientCert.pem

# Users's private key
sslkey=/path/to/your/Key.pem

# CSD wrapper used to fake the output of the desktop check script
csd-wrapper=/path/to/the/csd-wrapper.sh

# User's name
user=<username>

# User's password (will be prompted on the command line if commented out)
#password=<password>
END

The connection can then be established with this command:

$ openconnect --config=/path/to/the/openconnect.conf vpn.company.com

And the connection can be closed by this command:

$ pkill -F /var/run/openconnect.pid

OpenConnect supports PKCS#11 which makes it possible to use user's certificate stored on the Yubikey NEO. To make it working only requires replace the certificate and sslkey parameters in the configuration file with the following like:

# Search for both the cert and the key on the Yubikey NEO
# (http://www.infradead.org/openconnect/pkcs11.html)
certificate=pkcs11:manufacturer=piv_II;id=%01

Nortel

Use vpnc to connect to Nortel VPN. It requires special branch of vpnc which supports Nortel devices. The vpnc configuration (e.g. /etc/vpnc/company.conf) is then like the following:

IPSec gateway <CompanyVpnServer>
IPSec ID <CompanyVpnID>
IPSec secret <CompanyVpnSecret>
Xauth username <username>
#Xauth password <password>
Vendor nortel
Nortel Client ID V07_01

Then use the following command to connect to the VPN:

$ vpnc company

Juniper

Juniper VPN requires proprietary client (ncsvc) which is stored on the device itself. There is many scripts which automate download of the proprietary client and establishment of the connection but probably the best one is jvpn. The installation and configuration of jvpn is straightforward.

There is certain support support for Juniper VPN in OpenConnect. If the Juniper VPN requires user's certificate, then this is probably preffered solution as OpenConnect supports PKCS#11 interface used by Yubikey NEO.

Check Point

TODO

Email

Use Webmail or Davmail.

WebEx

The WebEx plugin is a Java Application which only works in web browsers which support NPAPI. Google Chrome removed this support in the version 42. Fortunately Firefox still supports it. Other problem is that WebEx is 32-bit application which requires 32-bin Oracle Java (JRE) which must run in 32-bit Firefox.

Screen sharing, chat and computer audio works just fine. Just make sure no other application (e.g web browser) is using the soundcard while trying to use the computer audio in WebEx.

Instant Messaging

Microsoft Communicator

If the company is using Microsoft Communicator, then Pidgin with SIPE plugin should work.

Jabber

If the company is using Cisco Jabber, then alternative Jabber client (e.g. Pidgin) should work. There is also Cisco WebEx Web IM which works from any web browser. If run from the 32-bit Firefox then even the WebEx meeting integration works. If the company is using some sort of Single Sign ON (SSO) system, then alternative Jabber clients probably won't work.

Problem with SSO

Pidgin (libpurple) doesn't understand the authentication method WEBEX-TOKEN (run pidgin -d):

jabber: Sending (ssl) (anonymous@company.com): <stream:stream to='company.com' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>
jabber: Recv (ssl)(183): <stream:stream xmlns='jabber:client' xml:lang='en-US.UTF-8' xmlns:stream='http://etherx.jabber.org/streams' from='company.com'   id='lsh4avD9N5Wxeb3b7TtvwQ45878' version='1.0'>
jabber: Recv (ssl)(163): <stream:features><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>WEBEX-TOKEN</mechanism><mechanism>PLAIN</mechanism></mechanisms></stream:features>
sasl: Mechs found: WEBEX-TOKEN PLAIN
jabber: Sending (ssl) (anonymous@company.com): <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN' xmlns:ga='http://www.google.com/talk/protocol/auth' ga:client-uses-full-bind-result='true'>password removed</auth>
jabber: Recv (ssl)(77): <failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>
sasl: Mechs found: WEBEX-TOKEN
sasl: No worthy mechs found

I believe that the server sends an ID which should be used on the SSO web page. The SSO web page should then return some other hash which should be send to the Jabber server.

Questions

  • Can the Jabber server provide the URL for the SSO web page?
  • How to pass the ID to the SSO web page (which Jabber message)?
  • How to get the hash from the SSO web page and how to pass it back to the Jabber server?

Possible solution

Configure hosts file on Windows to send request for the Jabber server via a Linux machine where we run stunnel and tcpflow to sniff the communication.

OR

Try to establish connection via AJAX XMPP and then sniff the communication.

Then try to implement the protocol into the libpurple.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment