[[!meta date="2015-02-08 09:58"]] [[!tag linux qt webservices usability owncloud]] [[!summary adding client SSL certificates two the qt owncloud client]] [[!img media/595px-OwnCloud2-Logo.svg.png width="200px" alt="" style="float: right" caption=""]] # 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: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.bash} 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: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.bash} 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 ErrorLog /var/log/apache2/ssl_engine.log LogLevel debug # 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 SSLOptions +StdEnvVars SSLOptions +StdEnvVars # 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 Options Indexes FollowSymLinks MultiViews AllowOverride All Order allow,deny Allow from all Satisfy Any ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## 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: ~~~~~~~~ { .bash } 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 :35: trailing whitespace. }; :36: trailing whitespace. :104: trailing whitespace. :178: trailing whitespace. :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: ~~~~~~~~~ {.bash} 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 ~~~~~~~~~ [[!img media/owncloud_meld_merge.jpg caption=""]] 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: [[!img media/owncloud_kdevelop.jpg caption="kdevelop showing a complicated code path"]] 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()" [[!img media/owncloud_kcachegrind.jpg caption="kcachegrind shows which code path was taken and how often"]] **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. # links * [1] * [2] * [3] * [4] * [5] * [6]