"; $app .= "Android: Google Play 'FreeOTP Authenticator' by Red Hat
"; $app .= "Apple: App Store 'OTP Auth' by Roland Moers


"; return $app; } # function app_time() { $app = "Apps to check that your device time is accurate:
"; $app .= ""; $app .= "Android: Google Play 'ClockSync' by Sergey Baranov
"; $app .= "

"; return $app; } # function set_2fa($data, $user, $tfa, $ans, $err, $msg) { $draw = false; $pg = '

Two Factor Authentication Settings

'; if ($err !== null and $err != '') $pg .= "$err

"; if ($msg !== null and $msg != '') $pg .= "$msg

"; $pg .= ''; $pg .= ''; $pg .= ''; $pg .= '
'; $pg .= ''; $pg .= ''; break; case 'test': $pg .= ''; $pg .= ''; $pg .= ''; break; case 'ok': $pg .= ''; $pg .= ''; break; } $pg .= '
'; switch ($tfa) { case '': $pg .= '
'; $pg .= "You don't have Two Factor Authentication (2FA) setup yet

"; $pg .= 'To use 2FA you need an App on your phone/tablet
'; $pg .= app_txt('ones'); $pg .= makeForm('2fa'); $pg .= 'Click here to begin the setup process for 2FA: '; $pg .= ''; $pg .= '
'; $pg .= '2FA is not yet enabled.
'; $pg .= 'Your 2FA key has been created but needs testing.

'; if (isset($ans['2fa_key'])) { $key = $ans['2fa_key']; $sfainfo = $ans['2fa_issuer'].': '.$ans['2fa_auth'].' '. $ans['2fa_hash'].' '.$ans['2fa_time'].'s'; $who = substr($user, 0, 8); $sfaurl = 'otpauth://'.$ans['2fa_auth'].'/'.$ans['2fa_issuer']. ':'.htmlspecialchars($who).'?secret='.$ans['2fa_key']. '&algorithm='.$ans['2fa_hash'].'&issuer='.$ans['2fa_issuer']; $draw = true; addQR(); } else { $key = 'unavailable'; $sfainfo = 'unavailable'; $sfaurl = 'unavailable'; } $pg .= "Your 2FA Secret Key is: $key
"; $pg .= "2FA Settings are $sfainfo

"; $pg .= "To setup 2FA in your App: Click here
"; $pg .= "or scan the qrcode/barcode below with your App:

"; $pg .= '
'; $pg .= 'A qrcode will show here if your browser supports html5/canvas'; $pg .= "

"; $pg .= makeForm('2fa'); $pg .= 'Then enter your App 2FA Value: '; $pg .= '
'; $pg .= 'The 2FA Value is always a 6 digit number
'; $pg .= app_txt('2FA apps'); $pg .= 'N.B. if you wish to setup 2FA on more than one device,
'; $pg .= 'you should setup all devices before testing one of them.
'; $pg .= 'If you have an old 2FA Secret Key in your device for this web site,
'; $pg .= 'delete it before scanning in the new 2FA Secret Key.

'; $pg .= 'WARNING: if you lose your 2FA device you will need to know
'; $pg .= 'the 2FA Secret Key to manually setup a new device,
'; $pg .= 'so your should copy it and store it somewhere securely.
'; $pg .= 'For security reasons, the site will not show you an active 2FA Secret Key.
'; $pg .= '
'; $pg .= makeForm('2fa'); $pg .= '
If you wish to cancel setting up 2FA, click here: '; $pg .= '
'; $pg .= '2FA is enabled on your account.

'; $pg .= 'If you wish to replace your Secret Key with a new one:

'; $pg .= makeForm('2fa'); $pg .= 'Current 2FA Value: '; $pg .= '*'; $pg .= '

'; $pg .= '*WARNING: replacing the Secret Key will disable 2FA
'; $pg .= 'until you successfully test the new key,
'; $pg .= 'thus getting a new key is effectively the same as disabling 2FA.

'; $pg .= '
'; $pg .= makeForm('2fa'); $pg .= 'If you wish to remove 2FA from your account,
'; $pg .= 'enter your App 2FA Value:
'; $pg .= 'then click remove: '; $pg .= '
'; $pg .= '
'; $pg .= '2FA means that you need 2 codes to login to your account.
'; $pg .= 'You will also need the 2FA code to modify any important settings in your account.
'; $pg .= 'The 1st code is your current password.
'; $pg .= 'The 2nd code is a number that your 2FA device will generate each time.
'; $pg .= 'Your 2FA device would be, for example, your phone or tablet.

'; $pg .= 'Each time you need a 2FA code, you use your device to generate a number
'; $pg .= 'that you type into the "*2nd Authentication:" field on any page that has it.

'; $pg .= 'IMPORTANT: the TOTP algorithm uses the time on your device,
'; $pg .= "so it is important that your device's clock is accurate within a few seconds.

"; $pg .= app_time(); $pg .= 'IMPORTANT: you enter the value from your App at the time you submit data.
'; $pg .= "The value is valid only once for a maximum of 30 seconds.
"; $pg .= "In both the Apps it has a 'dial' that shows the 30 seconds running out.
"; $pg .= "If you are close to running out, you can wait for the 30 seconds to run out
"; $pg .= "and then enter the new value it will come up with.
"; $pg .= "The pool checks your value using the time at the pool when you submit the data,
"; $pg .= "it doesn't matter when you loaded the web page,
"; $pg .= "it only matters when you clicked on the web page button to send the data to the pool.

"; $pg .= 'WARNING: once you have successfully tested and enabled 2FA,
'; $pg .= 'you will be unable to access or even reset your account without 2FA.
'; $pg .= 'There is no option to recover your 2FA from the web site,
'; $pg .= 'and you must know your 2FA code in order to be able to disable 2FA.

'; $pg .= 'WARNING: it is important to not store your login password in your 2FA device.
'; $pg .= 'These 2 together will give full access to your account.'; $pg .= '
'; if ($draw !== false) { $qr = shell_exec("../pool/myqr.sh '$sfaurl'"); if ($qr !== null and strlen($qr) > 30) { $pg .= "\n"; if (strpos($qr, 'var tw=1,fa=0,qrx=') === false) error_log("QR error for '$user' res='$qr'"); } else { if ($qr === null) $qr = 'null'; error_log("QR failed for '$user' res='$qr'"); } } return $pg; } # function do2fa($data, $user) { $mailmode = ''; $tfa = null; $err = ''; $msg = ''; $res = emailcheck($user); if ($res != null) { $msg = $res; $ans = get2fa($user, '', 0, 0); goto skipo; } $setup = getparam('Setup', false); if ($setup === 'Setup') { // rand() included as part of the entropy $ans = get2fa($user, 'setup', rand(1073741824,2147483647), 0); $mailmode = 'Setup'; } else { $can = getparam('Cancel', false); if ($can === 'Cancel') { $ans = get2fa($user, 'untest', 0, 0); $mailmode = 'Cancel'; } else { $value = getparam('Value', false); $test = getparam('Test', false); if ($test === 'Test' and $value !== null) { $ans = get2fa($user, 'test', 0, $value); $mailmode = 'Test'; } else { $nw = getparam('New', false); if ($nw === 'New' and $value !== null) { $ans = get2fa($user, 'new', rand(1073741824,2147483647), $value); $mailmode = 'New'; } else { $rem = getparam('Remove', false); if ($rem === 'Remove' and $value !== null) { $ans = get2fa($user, 'remove', 0, $value); $mailmode = 'Remove'; } else $ans = get2fa($user, '', 0, 0); } } } } skipo: if ($ans['STATUS'] != 'ok') $err = 'DBERR'; else { if (isset($ans['2fa_error'])) $err = $ans['2fa_error']; if ($mailmode != '' and $err == '') { $ans2 = userSettings($user); if ($ans2['STATUS'] != 'ok') dbdown(); // Should be no other reason? if (!isset($ans2['email'])) $err = 'An error occurred, check your details below'; else { $email = $ans2['email']; $emailinfo = getOpts($user, emailOptList()); if ($emailinfo['STATUS'] != 'ok') $err = 'An error occurred, check your details below'; else { if ($mailmode === 'Setup') twofaSetup($email, zeip(), $emailinfo); else if ($mailmode === 'Test') twofaEnabled($email, zeip(), $emailinfo); else if ($mailmode === 'New') twofaSetup($email, zeip(), $emailinfo); else if ($mailmode === 'Cancel') twofaCancel($email, zeip(), $emailinfo); else if ($mailmode === 'Remove') twofaRemove($email, zeip(), $emailinfo); } } } } if (isset($ans['2fa_status'])) $tfa = $ans['2fa_status']; if ($msg == '' && isset($ans['2fa_msg'])) $msg = $ans['2fa_msg']; $pg = set_2fa($data, $user, $tfa, $ans, $err, $msg); return $pg; } # function show_2fa($info, $page, $menu, $name, $user) { gopage($info, NULL, 'do2fa', $page, $menu, $name, $user); } # ?>