owncloud ssl client certificates

8 feb 2015

motivation

this posting is about owncloud [1] which can be used to manage file sharing/calendar sharing/address book hosting and more. that said, i’ve ported a patch to enable SSL client certificates from owncloud 1.6 to owncloud master (upcoming 1.8) and here is the documentation how to use it and how i’ve done it. the discussion about this patch can be found at [2].

note: this posting is about the owncloud client, written in qt5 but also covers how to change the apache configuration, that is the owncloud.conf file.

setup

SSL client certificates

first thing you will need is a certificate to sign the client certificates. the manual [3] describes this nicely for openssl/apache usage.

note: it is important to understand that you can use:

  • something like a verisign certificate to authenticate the server to the clients, which is pretty much what everyone does for his webpages,

  • and still have your own CA to issue client certificates, to authenticate clients to the server.

    so you don’t rely on verisign (or similar) to sign your client certificates, which is quite handy.

owncloud vhost

you can use the owncloud.conf coming with examples from the net and then extend it. this basically means that you add these lines:

SSLCertificateFile /etc/apache2/ssl/datenhalde-cert.pem
SSLCertificateKeyFile /etc/apache2/ssl/datenhalde-key.pem
SSLCACertificateFile /etc/apache2/ssl/cacert.pem

SSLVerifyClient require
SSLCACertificateFile /etc/apache2/ssl/cacert.pem
SSLVerifyDepth 1
# might be a good idea to disable SSLv2 SSLv3
SSLProtocol all -SSLv2 -SSLv3

this is the complete configuration:

<VirtualHost 192.168.56.101:443>
    ServerAdmin webmaster@localhost                                                                                                                               
    ServerName owncloud.myhost
    DocumentRoot /var/www/owncloud

    # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
    # error, crit, alert, emerg.
    # It is also possible to configure the loglevel for particular
    # modules, e.g.
    LogLevel debug 
    #LogLevel info ssl:warn

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    <IfModule mod_ssl.c>
      ErrorLog /var/log/apache2/ssl_engine.log
      LogLevel debug
    </IfModule>

    # For most configuration files from conf-available/, which are
    # enabled or disabled at a global level, it is possible to
    # include a line for only one particular virtual host. For example the
    # following line enables the CGI configuration for this host only
    # after it has been globally disabled with "a2disconf".
    #Include conf-available/serve-cgi-bin.conf

    #   SSL Engine Switch:
    #   Enable/Disable SSL for this virtual host.
    SSLEngine on

    #   A self-signed (snakeoil) certificate can be created by installing
    #   the ssl-cert package. See
    #   /usr/share/doc/apache2/README.Debian.gz for more info.
    #   If both key and certificate are stored in the same file, only the
    #   SSLCertificateFile directive is needed.
    SSLCertificateFile /etc/apache2/ssl/datenhalde-cert.pem
    SSLCertificateKeyFile /etc/apache2/ssl/datenhalde-key.pem

    #   Server Certificate Chain:
    #   Point SSLCertificateChainFile at a file containing the
    #   concatenation of PEM encoded CA certificates which form the
    #   certificate chain for the server certificate. Alternatively
    #   the referenced file can be the same as SSLCertificateFile
    #   when the CA certificates are directly appended to the server
    #   certificate for convinience.
    # qknight: normally you would need this as well but since my root CA is self signed, we don't
    #SSLCertificateChainFile /etc/apache2/ssl/bundle.crt

    #   Certificate Authority (CA):
    #   Set the CA certificate verification path where to find CA
    #   certificates for client authentication or alternatively one
    #   huge file containing all of them (file must be PEM encoded)
    #   Note: Inside SSLCACertificatePath you need hash symlinks
    #                to point to the certificate files. Use the provided
    #                Makefile to update the hash symlinks after changes.
    SSLCACertificateFile /etc/apache2/ssl/cacert.pem

    #   Certificate Revocation Lists (CRL):
    #   Set the CA revocation path where to find CA CRLs for client
    #   authentication or alternatively one huge file containing all
    #   of them (file must be PEM encoded)
    #   Note: Inside SSLCARevocationPath you need hash symlinks
    #                to point to the certificate files. Use the provided
    #                Makefile to update the hash symlinks after changes.
    #SSLCARevocationPath /etc/apache2/ssl.crl/
    #SSLCARevocationFile /etc/apache2/ssl.crl/ca-bundle.crl

    #   Client Authentication (Type):
    #   Client certificate verification type and depth.  Types are
    #   none, optional, require and optional_no_ca.  Depth is a
    #   number which specifies how deeply to verify the certificate
    #   issuer chain before deciding the certificate is not valid.
    #SSLVerifyClient require
    #SSLVerifyDepth  10

    # qknight: added client cert requirement
    SSLVerifyClient require
    SSLCACertificateFile /etc/apache2/ssl/cacert.pem
    SSLVerifyDepth 1
    SSLProtocol all -SSLv2 -SSLv3

    #   SSL Engine Options:
    #   Set various options for the SSL engine.
    #   o FakeBasicAuth:
    #        Translate the client X.509 into a Basic Authorisation.  This means that
    #        the standard Auth/DBMAuth methods can be used for access control.  The
    #        user name is the `one line' version of the client's X.509 certificate.
    #        Note that no password is obtained from the user. Every entry in the user
    #        file needs this password: `xxj31ZMTZzkVA'.
    #   o ExportCertData:
    #        This exports two additional environment variables: SSL_CLIENT_CERT and
    #        SSL_SERVER_CERT. These contain the PEM-encoded certificates of the
    #        server (always existing) and the client (only existing when client
    #        authentication is used). This can be used to import the certificates
    #        into CGI scripts.
    #   o StdEnvVars:
    #        This exports the standard SSL/TLS related `SSL_*' environment variables.
    #        Per default this exportation is switched off for performance reasons,
    #        because the extraction step is an expensive operation and is usually
    #        useless for serving static content. So one usually enables the
    #        exportation for CGI and SSI requests only.
    #   o OptRenegotiate:
    #        This enables optimized SSL connection renegotiation handling when SSL
    #        directives are used in per-directory context.
    #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
    <FilesMatch "\.(cgi|shtml|phtml|php)$">
                    SSLOptions +StdEnvVars
    </FilesMatch>
    <Directory /usr/lib/cgi-bin>
                    SSLOptions +StdEnvVars
    </Directory>

    #   SSL Protocol Adjustments:
    #   The safe and default but still SSL/TLS standard compliant shutdown
    #   approach is that mod_ssl sends the close notify alert but doesn't wait for
    #   the close notify alert from client. When you need a different shutdown
    #   approach you can use one of the following variables:
    #   o ssl-unclean-shutdown:
    #        This forces an unclean shutdown when the connection is closed, i.e. no
    #        SSL close notify alert is send or allowed to received.  This violates
    #        the SSL/TLS standard but is needed for some brain-dead browsers. Use
    #        this when you receive I/O errors because of the standard approach where
    #        mod_ssl sends the close notify alert.
    #   o ssl-accurate-shutdown:
    #        This forces an accurate shutdown when the connection is closed, i.e. a
    #        SSL close notify alert is send and mod_ssl waits for the close notify
    #        alert of the client. This is 100% SSL/TLS standard compliant, but in
    #        practice often causes hanging connections with brain-dead browsers. Use
    #        this only for browsers where you know that their SSL implementation
    #        works correctly.
    #   Notice: Most problems of broken clients are also related to the HTTP
    #   keep-alive facility, so you usually additionally want to disable
    #   keep-alive for those clients, too. Use variable "nokeepalive" for this.
    #   Similarly, one has to force some clients to use HTTP/1.0 to workaround
    #   their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and
    #   "force-response-1.0" for this.
    BrowserMatch "MSIE [2-6]" \
                    nokeepalive ssl-unclean-shutdown \
                    downgrade-1.0 force-response-1.0
    # MSIE 7 and newer should be able to use keepalive
    BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown

    <Directory /var/www/owncloud>
            Options Indexes FollowSymLinks MultiViews
            AllowOverride All
            Order allow,deny
            Allow from all
            Satisfy Any
    </Directory>
</VirtualHost>

owncloud client

clone this repo:

git clone https://github.com/owncloud/client.git

then use [4] and install the dependencies:

aptitude install qt5-default
aptitude install qt5keychain-dev
aptitude install libqt5webkit5-dev
aptitude install libqt5xmlpatterns5-dev
aptitude install qttools5-dev qttools5-dev-tools
aptitude install libneon27-dev
aptitude install sqlite3
aptitude install libsqlite3-dev
aptitude install openssl-dev
aptitude install libgnutls-openssl-dev

configure & build:

mkdir build; cd build
cmake -DCMAKE_SKIP_BUILD_RPATH=false -DCMAKE_BUILD_TYPE=Debug -DCMAKE_BUILD_WITH_INSTALL_RPATH=false -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=true  -DCMAKE_INSTALL_PREFIX=`pwd` ..
make

finally, run it:

bin/owncloud --logfile /tmp/log

in another shell run this:

tail -f /tmp/log

what i’ve tested

basically only coping files:

  • i copied many small files (40) and
  • some very large files (3x ~600mb) to the remote server using TLS 1.2 and using the owncloud webgui in firefox, with client certificate (.p12) file installed in the browser. to check that the files were ‘really’ copied to the server i was using the webinterface.

development

git rebase using meld

this is about how to merge the patch from 1.6 to 1.8 using meld:

  1. imported the branch named ‘ssl-client-certs’ on owncloud 1.6

  2. managed to get version 1.6 running with making some values hard-coded

  3. created a new branch: ‘git checkout -b ssl-client-certs-reduced’ in order to not lose track of who made which changes

  4. remove the hard-coded code paths and did ‘rebase -i HEAD~10’ to compress all changes into a single commit. this makes the later rebase easier i was told.

  5. adapted my ~/.gitconfig like discussed in [6], in details:

    excerpt out of ~/.gitconfig

     [merge]
             # http://stackoverflow.com/questions/11133290/git-merging-using-meld   
             tool = mymeld
             conflictstyle = diff3
     [mergetool "mymeld"]
             keepBackup=false
             #trustExitCode=true
             # When there's a collison during git merge, I open a mergetool called Meld. It opens three files LOCAL, BASE and REMOTE. 
             #As I've read 
             # - LOCAL -> what is up2date at master
             # - BASE -> stuff i put together from left and right
             # - REMOTE -> what i did
             cmd = /home/joachim/.nix-profile/bin/meld --diff $BASE $LOCAL --diff $BASE $REMOTE --diff $LOCAL $BASE $REMOTE --output $MERGED
             # this opens meld with 3 tabs, 1st and 2nd tab containing the simple diffs I'm trying to merge, and the 3rd tab, open by default, shows the 3-way merge view.

    note: the meld path is nixos specific /home/joachim/.nix-profile/bin/meld

  6. finally i decided for ‘rebase’ rather than ‘merge’ as the code history looks much prettier then:

    
        git rebase master
        First, rewinding head to replay your work on top of it...
        Applying: adds 'SSL client certificate' support from n.octeau with qknight changes as:
        Using index info to reconstruct a base tree...
        M       csync/src/csync.h
        M       csync/src/csync_owncloud_private.h
        M       src/cmd/cmd.cpp
        M       src/gui/CMakeLists.txt
        M       src/gui/folder.cpp
        M       src/gui/wizard/owncloudhttpcredspage.cpp
        M       src/gui/wizard/owncloudsetuppage.cpp
        M       src/gui/wizard/owncloudsetuppage.h
        M       src/gui/wizard/owncloudwizard.cpp
        M       src/gui/wizard/owncloudwizard.h
        M       src/libsync/CMakeLists.txt
        M       src/libsync/account.cpp
        M       src/libsync/account.h
        M       src/libsync/creds/httpcredentials.cpp
        M       src/libsync/creds/httpcredentials.h
        M       src/libsync/networkjobs.cpp
        <stdin>:35: trailing whitespace.
        };  
        <stdin>:36: trailing whitespace.
        <stdin>:104: trailing whitespace.
        <stdin>:178: trailing whitespace.
        <stdin>:224: trailing whitespace.
        warning: squelched 21 whitespace errors
        warning: 26 lines add whitespace errors.
        Falling back to patching base and 3-way merge...
        Auto-merging src/libsync/networkjobs.cpp
        Auto-merging src/libsync/creds/httpcredentials.h
        CONFLICT (content): Merge conflict in src/libsync/creds/httpcredentials.h
        Auto-merging src/libsync/creds/httpcredentials.cpp
        CONFLICT (content): Merge conflict in src/libsync/creds/httpcredentials.cpp
        Auto-merging src/libsync/account.h
        CONFLICT (content): Merge conflict in src/libsync/account.h
        Auto-merging src/libsync/account.cpp
        CONFLICT (content): Merge conflict in src/libsync/account.cpp
        Auto-merging src/libsync/CMakeLists.txt
        Auto-merging src/gui/wizard/owncloudwizard.h
        Auto-merging src/gui/wizard/owncloudwizard.cpp
        Auto-merging src/gui/wizard/owncloudsetuppage.h
        Auto-merging src/gui/wizard/owncloudsetuppage.cpp
        CONFLICT (content): Merge conflict in src/gui/wizard/owncloudsetuppage.cpp
        Auto-merging src/gui/wizard/owncloudhttpcredspage.cpp
        Auto-merging src/gui/folder.cpp
        CONFLICT (content): Merge conflict in src/gui/folder.cpp
        Auto-merging src/gui/CMakeLists.txt
        CONFLICT (content): Merge conflict in src/gui/CMakeLists.txt
        Auto-merging src/cmd/cmd.cpp
        Auto-merging csync/src/csync_owncloud_private.h
        Auto-merging csync/src/csync.h
        Failed to merge in the changes.
        Patch failed at 0001 adds 'SSL client certificate' support from n.octeau with qknight changes as:
        The copy of the patch that failed is found in:
            /home/joachim/Desktop/projects/owncloud/mirall/.git/rebase-apply/patch
    
        When you have resolved this problem, run "git rebase --continue".
        If you prefer to skip this patch, run "git rebase --skip" instead.
        To check out the original branch and stop rebasing, run "git rebase --abort".

    afterwards i was using the ‘git mergetool’ to make the final merge:

    
        git mergetool
        Merging:
        src/gui/CMakeLists.txt
        src/gui/folder.cpp
        src/gui/wizard/owncloudsetuppage.cpp
        src/libsync/account.cpp
        src/libsync/account.h
        src/libsync/creds/httpcredentials.cpp
        src/libsync/creds/httpcredentials.h
    
        Normal merge conflict for 'src/gui/CMakeLists.txt':
          {local}: modified file
          {remote}: modified file

    as you can see in the screenshot above i’m using the third tab right now and what you can see there is:

    • LOCAL: stuff which happened on master (very recent code)
    • MIDDLE: remixing arena, here you have to take in stuff from the left (LOCAL) and the right (REMOTE)
    • REMOTE: stuff i had been doing in my ‘ssl-client-certs-reduced-merge’ branch

    note: what you have to do now is quite simple: for every file which has a merge conflict a new meld instance will pop up. when you finished editing, just save and quit meld and changes will be written to the respective file.

    after all merge conflicts are solved, do this:

     git status

    for all files which have not been added to the index yet, add them:

     git add src/gui/CMakeLists.txt src/gui/folder.cpp src/gui/wizard/owncloudsetuppage.cpp src/libsync/account.cpp src/libsync/account.h src/libsync/creds/httpcredentials.cpp src/libsync/creds/httpcredentials.h

    afterwards:

     git rebase --continue

    i accidently did a ‘git commit’ instead, and then i had to undo that commit and go for the ‘git rebase –continue’ instead

    finally use ‘git push qknight ssl-client-certs-reduced-merge’ to your github account and create a pull request from the github webpage to the owncloud master and your done.

understanding code using kcachegrind

sometimes you have to extend an application but in order to do that you have to understand how it works. the original patch i had to port from 1.6 to 1.8 didn’t work out of the box so i had to search for the errors and i wanted to know which code-paths are taken and also how often, without having to step through the source code manually using GDB i decided to use valgrind with the callgrind plugin.

first of all, this is the code seen from kdevelop:

now run:

valgrind --tool=callgrind -v  bin/owncloud

note: it is important to use the application in a way that it executes the wanted code paths, in my case, to add a new account and sync files from it.

afterwards you will find one file per task which looks like this:

ls -la | grep callg
-rw-------  1 joachim users 3757878 Feb  3 07:33 callgrind.out.28286
-rw-------  1 joachim users 1719011 Feb  3 07:31 callgrind.out.28321

and finally pick the right one and run this:

kcachegrind callgrind.out.28286 

in kcachegrind use the search field and enter your function name, in my case that was “Folder::init()”

note: interpretation of the graphs and data shown is not as straight forward as it seems, see [4] for more details.

conclusion

i’ve learned a lot about git, especially ‘git rebase’, ‘git mergetool’ and ‘git bisect’ and i think that the valgrind/cachegrind useage to understand execution paths is quite helpful! if you have comments on this, please use the comment field below. i’d love to have feedback.

my motivation to create this feature was to have more security as you can create a vhost now which might even serve a very old apache/php/owncloud version with security exploits but, except a client with a valid SSL client certificate, nobody will ever be able to even issue a basic GET request to the server as the https will return a ‘SSL handshake error’ first!

thanks a lot to the folks at irc.freenode.net#owncloud-client-dev especially to ogoffart, danimo and guruz for their help.

i found the nixos environments quite helpful as i was using gcc and llvm for testing and it is so easy to change between these compilers on nixos.

article source