IPv6 für Google2FA - Zwei-Faktor Authentifizierung für Zarafa/Kopano WebApp | Zarafa/Kopano WebApp Plugin allows the use of Google two-factor authentication

Entwicklung

Code-Repository

Bitbucket

Build new version

  • Change version number in manifest.xml and file version
    Add changes in readme
    Tag version – example:
git tag -a v0.3 -m 'v0.3' f4e1e90
git push origin v0.3

The checksum of the commit is optional.

  • Zip files without the git files – example:
cd ../
zip -r google2fa_v0.3.zip google2fa/config_default.php google2fa/js google2fa/languages google2fa/manifest.xml google2fa/php google2fa/readme google2fa/resources
  • Download the file and upload it to with WordPress Download Plugin.

Idea: Use the Zarafa build with js optimizing

Update from repository (f.e. after merging pull request)
git pull
Commit changes to the repository
git diff
git status
git add {file}
git commit -m "{comment}"
git push origin master
git log

Create or update language files

Export texts from files
xgettext --no-wrap php/plugin.google2fa.php js/settings/SettingsGoogle2FAWidget.js js/settings/SettingsGoogle2FACategory.js -o languages/messages.pot

Don’t forget to edit header (CHARSET -> UTF-8)

Take english texts from ids
msgen --no-wrap languages/messages.pot -o languages/en_US.UTF-8/LC_MESSAGES/plugin_google2fa.po
Merge german translation
msgmerge --no-wrap languages/de_DE.UTF-8/LC_MESSAGES/plugin_google2fa.po languages/messages.pot -o languages/de_DE.UTF-8/LC_MESSAGES/plugin_google2fa.po

Search for fuzzy, correct translation, delete fuzzy

Merge french translation
msgmerge --no-wrap languages/fr_FR.UTF-8/LC_MESSAGES/plugin_google2fa.po languages/messages.pot -o languages/fr_FR.UTF-8/LC_MESSAGES/plugin_google2fa.po

Search for fuzzy, correct translation, delete fuzzy

Create binaries
msgfmt languages/de_DE.UTF-8/LC_MESSAGES/plugin_google2fa.po -o languages/de_DE.UTF-8/LC_MESSAGES/plugin_google2fa.mo
msgfmt languages/en_US.UTF-8/LC_MESSAGES/plugin_google2fa.po -o languages/en_US.UTF-8/LC_MESSAGES/plugin_google2fa.mo
msgfmt languages/fr_FR.UTF-8/LC_MESSAGES/plugin_google2fa.po -o languages/fr_FR.UTF-8/LC_MESSAGES/plugin_google2fa.mo

Integration des Plugins in die WebApp

Integration in den Login-Prozess

Grundsätzlich ist das Plugin so entwickelt, dass es sich an die dokumentierten Standards der WebApp-Entwicklung hält – so zum Beispiel die Anwender-Konfiguration in den WebApp-Einstellungen inkl. Speicherung. Das Besondere an dem Plugin ist allerdings, dass es sich in den WebApp-Login-Mechanismus einhängen muss, um die 2-Faktor-Authentifizierung erreichen zu können.

Ziel bei der Entwicklung war es, hierbei den WebApp-Quellcode nicht zu verändern. Hierzu wird zum einen ein in der WebApp definierter Hook verwendet. Zum anderen wird bei Bedarf mit Hilfe einer zweiten Login-Seite der Token erfragt.

WebApp-Integration und Bedarfsprüfung Token-Abfrage

Verwendet wird der in der index.php definierte Hook “server.index.load.main.before”. Nach der Angabe von Benutzername und Passwort und deren Prüfung wird das Plugin für die Bedarfprüfung bzgl. einer Token-Prüfung aufgerufen. Der Logon-Hook kann nicht verwendet werden, da zu diesem Zeitpunkt die Settings noch nicht geladen wurden.

Die Hook-Registierung befindet sich in der Datei php/plugin.google2fa.php in der Methode init().

/normanth/google2fa/src/master/php/plugin.google2fa.php
$this->registerHook('server.index.load.main.before');

In selbiger Datei in der Methode execute() findet dann die Bedarfsprüfung zur Eingabe eines Tokens statt.

/normanth/google2fa/src/master/php/plugin.google2fa.php
case 'server.index.load.main.before' :

Hier ist der Ablauf wie folgt:

  • Wenn laut Plugin-Konfiguration das Plugin immer “enabled” sein soll, dann wird das Plugin aktiviert.
/normanth/google2fa/src/master/php/plugin.google2fa.php
if (PLUGIN_GOOGLE2FA_ALWAYS_ENABLED) {
   $GLOBALS["settings"]->set('zarafa/v1/plugins/google2fa/enable', true);
   $GLOBALS["settings"]->saveSettings();
}
  • Wenn laut Plugin-Konfiguration die Zwei-Faktor-Authentifizierung “activated” sein soll, dann wird diese aktiviert.
/normanth/google2fa/src/master/php/plugin.google2fa.php
if (PLUGIN_GOOGLE2FA_ALWAYS_ACTIVATED)
   Google2FAData::setActivate(true);
  • Wenn das Plugin nicht “enabled” oder die Zwei-Faktor-Authentifizierung nicht “activated” ist, dann wird aus der Methode gesprungen, um den normalen Login-Vorgang fortzusetzen. Eine Token-Abfrage findet nicht statt.
/normanth/google2fa/src/master/php/plugin.google2fa.php
if (!$GLOBALS["settings"]->get('zarafa/v1/plugins/google2fa/enable')
      || !Google2FAData::isActivated())
   break;
  • Es wird geprüft, ob die IP-Adresse des Anwenders in der in der Plugin-Konfiguration definierten Whitelist steht. Ist dies der Fall, wird aus der Methode gesprungen, um ebenfalls den normalen Login-Vorgang fortzusetzen.
/normanth/google2fa/src/master/php/plugin.google2fa.php
if (PLUGIN_GOOGLE2FA_WHITELIST !== "") {
   foreach (explode (",", PLUGIN_GOOGLE2FA_WHITELIST) as $range) {
      if (self::ip_in_range($_SERVER['REMOTE_ADDR'], $range))
         break 2;
   }
}
  • Es wird geprüft, ob die Token-Authorisierung schon stattgefunden hat. Das ist der Fall, wenn der Token gerade abgefragt und erfolgreich geprüft wurde oder wenn die WebApp neu geladen wird. Im ersten Fall wird der verwendete Token gespeichert oder aus der Liste der zeitunabhängigen Codes gelöscht, damit er nicht erneut verwendet werden kann.
/normanth/google2fa/src/master/php/plugin.google2fa.php
if (array_key_exists('google2FALoggedOn', $_SESSION) && $_SESSION['google2FALoggedOn']) {
   // Login successful - save or remove code
   if (isset($_SESSION['google2FACode'])) {
      if (isset($_SESSION['google2FACodeTimeless'])) {
         Google2FAData::rmTimelessCode($_SESSION['google2FACode']);
         unset($_SESSION['google2FACodeTimeless']);
      } else {
         Google2FAData::addUsedCode($_SESSION['google2FACode']);
      }
      unset($_SESSION['google2FACode']);
   }
   break;
}
  • Die Session wird gelöscht – also der Login rückgängig gemacht. Die für einen späteren Login und die Token-Abfrage benötigten Informationen werden in einer neuen Session gespeichert.
/normanth/google2fa/src/master/php/plugin.google2fa.php
$encryptionStore = EncryptionStore::getInstance();
$username = $encryptionStore->get('username');
$password = $encryptionStore->get('password');
$encryptionStore->add('username', '');
$encryptionStore->add('password', '');
...
$_SESSION = array(); // clear session to logoff and don't loose session
$_SESSION['google2FAUsername'] = $username; // or from $_POST/$GLOBALS
$_SESSION['google2FAPassword'] = $password;
[...]
  • Zusätzlich müssen die beiden Fingerprints für Frontend und Backend zwischengespeichert werden.
/normanth/google2fa/src/master/php/plugin.google2fa.php
$_SESSION['google2FAFingerprint'] = $_SESSION['fingerprint'];
$_SESSION['google2FAFrontendFingerprint'] = $_SESSION['frontend-fingerprint'];
  • Eine Umleitung auf die zweite Login-Seite mit der Token-Abfrage php/login.php findet statt und die weitere Ausführung der index.php wird abgebrochen.
/normanth/google2fa/src/master/php/plugin.google2fa.php
header('Location: plugins/google2fa/php/login.php', true, 303); // delete GLOBALS, go to token page
exit; // don't execute header-function in index.php
Token-Abfrage und -Prüfung

Da der WebApp-Quellcode der WebApp nicht verändert werden sollte, wurde eine zweite Login-Seite zur Token-Abfrage php/login.php auf Basis der Login-Seite entwickelt. Ab hier befindet wir uns außerhalb des offiziellen Plugin-Mechanismus.

Klickt der Anwender auf den Login-Button der zweiten Login-Seite, so findet die Token-Prüfung in der Datei php/logon.php statt.

/normanth/google2fa/src/master/php/logon.php
form action="logon.php" method="post"

Bei einer erfolgreichen Token-Authentifizierung wird die Session mit den benötigten Anmeldeinformationen wiederhergestellt und die index.php der WebApp aufgerufen. Der Startprozess wird erneut durchlaufen. Da die Token-Authentifizierung erfolgt ist, ruft das Plugin diesmal nicht die zweite Login-Seite auf.

/normanth/google2fa/src/master/php/logon.php
$_SESSION['username'] = $_SESSION['google2FAUsername'];
$_SESSION['password'] = $_SESSION['google2FAPassword'];
$_SESSION['google2FACode'] = $code; // to disable code
$_SESSION['google2FALoggedOn'] = TRUE; // 2FA successful
$_SESSION['fingerprint'] = $_SESSION['google2FAFingerprint'];
$_SESSION['frontend-fingerprint'] = $_SESSION['google2FAFrontendFingerprint'];
header('Location: ../../../index.php', true, 303);

Ist die Token-Authentifizierung nicht erfolgreich, wird die zweite Login-Seite zur Token-Abfrage erneut aufgerufen.

/normanth/google2fa/src/master/php/logon.php
$_SESSION['google2FALoggedOn'] = FALSE; // login not successful
header('Location: login.php', true, 303);