NOTA BENE - WORK IN PROGRESS - Your Inputs & Thoughts
To Technology Knowledge Base - To Technology Knowledge Base - Overview - To Technology Knowledge Base - Web Application Certificate Login
Ĩesky | english
dotProject Project Management - Certificate Login
by Markus Warg
This document is about integrating client certificate support into dotproject v2.1.2 (http://www.dotproject.net/).
- Dotproject is a package that offers project management functionality completely web based and open source, have a look into the documentation if you're interested. Sadly it seems that development is suspended right now, nevertheless its worth to look at it.
- You will need an web server with enabled ssl mod and an CACert account to create the server and client certificates (though it should work with any self signed certificates as well).
Theory
- As SSLVerifyClient optional is marked as "might not work with every browser" in some documents, I decided to use SSLVerifyClient required. dotproject uses sessions which marked the way quite clear: introduce a new login page, enable SSLVerifyclient required for that page, establish the session and return to the normal web gui.
This method should work for all projects that have a login page and store the login status within a session. It will not work with enabled Apache BasicAuth.
Apache Config
- Just add
- SSLEngine On
- SSLCertificateFile /etc/ssl/private/mykey.pem
- SLCertificateKeyFile /etc/ssl/private/mykey.pem
- SLCACertificateFile /etc/ssl/private/cacert.ca
- to your virtual host configuration, replace mykey by the name of your server certificate file name.
- Next, add a File statement to enable SSLVerifyClient:
<Files "certlogin.php">
- SSLVerifyClient require
- SSLVerifyDepth 2
SSLOptions +StdEnvVars
</Files>
- This will activate REQUIRED SSLVerifyClient support for just one file (certlogin.php).
Code
- You need to patch some source code and translation files to make the html frontend client cert aware:
Edit style/*/login.php and add a link to certlogin.php to the regular login window, for the default style, paste this code between the submit button and the lost password recovery section:
<tr> <td colspan="2"><a href="certlogin.php=loginCRT=1"><?php echo $AppUI->_('certLogin');?></a></td> </tr>
Now add the translation for "certLogin" to locales/<cc>/common.inc, the format should be quite obvious, just add a row for certLogin and enter your preferred text.
- Copy index.php to certlogin.php and apply some patches, see a patch and the modified block as a whole below:
$ diff index.php certlogin.php: 120c120 < if (isset($_REQUEST['login'])) { --- > if (isset($_REQUEST['loginCRT'])) { 122,124c122,124 < $username = dPgetCleanParam( $_POST, 'username', < $password = dPgetCleanParam( $_POST, 'password', < $redirect = dPgetCleanParam( $_REQUEST, 'redirect', --- > $username = dPgetCleanParam( $_SERVER, 'SSL_CLIENT_S_DN_Email', > $signingca = dPgetCleanParam( $_SERVER, 'SSL_CLIENT_I_DN_O', > $redirect = '/'; 128c128 < $ok = $AppUI->login( $username, $password ); --- > $ok = $AppUI->loginCRT( $username, $signingca );
- The patched block as a whole:
if (isset($_REQUEST['loginCRT'])) { $username = dPgetCleanParam( $_SERVER, 'SSL_CLIENT_S_DN_Email', $signingca = dPgetCleanParam( $_SERVER, 'SSL_CLIENT_I_DN_O', $redirect = '/'; $AppUI->setUserLocale(); @include_once( DP_BASE_DIR.'/locales/'.$AppUI->user_locale.'/locales.php' ); @include_once DP_BASE_DIR.'/locales/core.php'; $ok = $AppUI->loginCRT( $username, $signingca ); if (!$ok) { $AppUI->setMsg( 'Login Failed'); } else { //Register login in user_acces_log $AppUI->registerLogin(); } addHistory('login', $AppUI->user_id, 'login', $AppUI->user_first_name . ' ' . $AppUI->user_last_name); $AppUI->redirect($redirect); }
- As you see, a new function is introduced: loginCRT, we need to add this function as well. Edit the file classes/ui.php. Make a copy of the login function and modify the following few lines of code:
function loginCRT($username, $signingca) { ... if (@$_REQUEST['loginCRT'] != 1 && @$_POST['login'] != 'login' ... ... if (!$auth->authenticateCRT($username, $signingca)) { return false; } ... }
- Last, edit classes/authenticator.class.php and add a new method to the class in that file (I assume you use the SQLAuthenticator, code might differ for other authentication methods):
class SQLAuthenticator ... function authenticateCRT($username, $signingca) { $userdata = array('markus' => array('crt_email' => 'markus@cacert.org', 'crt_serial' => 'yourserialnumberhere', 'crt_signingca' => 'CAcert Inc.') ); foreach ($userdata as $key => $value) { if ($value['crt_email'] == $username && $value['crt_signingca'] = $signingca) { $this->username = $key; $q = new DBQuery; $q->addTable('users'); $q->addQuery('user_id, user_password'); $q->addWhere("user_username = '$key'"); if (!$rs = $q->exec()) { $q->clear(); return false; } if (!$row = $q->fetchRow()) { $q->clear(); return false; } $this->user_id = $row["user_id"]; $q->clear(); return true; } } return false; }
- While this looks quite dumb at the first glance, it works as proof of concept. You might add some code to look up crt_email, crt_signca and the serial number in a database, but in principle you're done with the steps described above.
Inputs & Thoughts
YYYYMMDD-YourName
Text / Your Statements, thoughts and e-mail snippets, Please
YYYYMMDD-YourName
Text / Your Statements, thoughts and e-mail snippets, Please
Category or Categories