Skip to content

Instantly share code, notes, and snippets.

Revisions

  1. @invalid-email-address Anonymous created this gist Sep 10, 2015.
    623 changes: 623 additions & 0 deletions corporate-linux-desktop-howto.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,623 @@
    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](https://github.com/gentilkiwi/mimikatz).
    Mimikatz allows to
    [extract](http://theunixtips.com/export-nonexporteable-private-certificate-from-symantec-pki/)
    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](https://www.yubico.com/products/yubikey-hardware/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](https://developers.yubico.com/yubikey-piv-manager/) or
    [CLI](https://developers.yubico.com/PIV/Tools/Yubico_PIV_Tool.html). 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](http://www.infradead.org/openconnect/) client. The best way
    how to connect to this device is to use
    [OpenConnect](http://www.infradead.org/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](https://svn.unix-ag.uni-kl.de/vpnc/branches/vpnc-nortel/) 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](https://github.com/samm-git/jvpn). The installation
    and configuration of `jvpn` is straightforward.

    There is certain support support for Juniper VPN in
    [OpenConnect](http://www.infradead.org/openconnect/juniper.html). 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](http://davmail.sourceforge.net).


    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](http://csh.us/2012/07/09/pidgin-support-for-cisco-webex-im/).
    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](https://developer.cisco.com/media/AJAX-XMPP-Library-Index/index.html)
    and then sniff the communication.

    Then try to implement the protocol into the `libpurple`.