diff --git a/pool/email.php b/pool/email.php index db4f5540..1e1d36f2 100644 --- a/pool/email.php +++ b/pool/email.php @@ -186,7 +186,7 @@ function twofaSetup($to, $whoip, $emailinfo) return false; $message = "2FA is ready to be tested.$eol"; - $message = "It will be enabled once you test it.$eol$eol"; + $message .= "It will be enabled once you test it.$eol$eol"; $message .= $ret; return sendnoheader($to, "2FA is Ready to be Enabled", $message, $emailinfo); @@ -211,6 +211,46 @@ function twofaEnabled($to, $whoip, $emailinfo) return sendnoheader($to, "2FA is Enabled", $message, $emailinfo); } # +function twofaCancel($to, $whoip, $emailinfo) +{ + global $eol; + + if (!isset($emailinfo['KWebURL'])) + return false; + + $web = $emailinfo['KWebURL']; + + $ret = emailEnd('2fa change', $whoip, $emailinfo); + if ($ret === false) + return false; + + $message = "2FA setup was cancelled on your account.$eol"; + $message .= "You can set it up later if you want.$eol$eol"; + $message .= $ret; + + return sendnoheader($to, "2FA was Cancelled", $message, $emailinfo); +} +# +function twofaRemove($to, $whoip, $emailinfo) +{ + global $eol; + + if (!isset($emailinfo['KWebURL'])) + return false; + + $web = $emailinfo['KWebURL']; + + $ret = emailEnd('2fa change', $whoip, $emailinfo); + if ($ret === false) + return false; + + $message = "2FA was removed from your account.$eol"; + $message .= "You can set it up again later if you want.$eol$eol"; + $message .= $ret; + + return sendnoheader($to, "2FA was Removed", $message, $emailinfo); +} +# # getOpts required for email # If they aren't all setup in the DB then email functions will return false function emailOptList() diff --git a/pool/page_2fa.php b/pool/page_2fa.php index 4ec7330a..37e1ff2d 100644 --- a/pool/page_2fa.php +++ b/pool/page_2fa.php @@ -23,7 +23,6 @@ function set_2fa($data, $user, $tfa, $ans, $err) $pg .= ''; $pg .= '
'; - $pg .= makeForm('2fa'); $pg .= ''; $pg .= ''; + $pg .= ''; break; case 'test': $pg .= ''; + $pg .= ''; $pg .= ''; + $pg .= ''; break; case 'ok': $pg .= ''; + $pg .= ''; break; } - $pg .= '
'; switch ($tfa) @@ -33,9 +32,10 @@ function set_2fa($data, $user, $tfa, $ans, $err) $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 .= '
'; @@ -66,8 +66,9 @@ function set_2fa($data, $user, $tfa, $ans, $err) $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 .= app_txt('2FA apps'); $pg .= 'N.B. if you wish to setup 2FA on more than one device,
'; @@ -79,20 +80,32 @@ function set_2fa($data, $user, $tfa, $ans, $err) $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 .= '

'; $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 .= ''; $pg .= ''; @@ -147,34 +160,52 @@ function set_2fa($data, $user, $tfa, $ans, $err) # function do2fa($data, $user) { + $mailmode = ''; $err = ''; $setup = getparam('Setup', false); - $testemail = false; if ($setup === 'Setup') { // rand() included as part of the entropy $ans = get2fa($user, 'setup', rand(1073741824,2147483647), 0); - $testemail = true; + $mailmode = 'Setup'; } else { - $value = getparam('Value', false); - $test = getparam('Test', false); - if ($test === 'Test' and $value !== null) + $can = getparam('Cancel', false); + if ($can === 'Cancel') { - $ans = get2fa($user, 'test', 0, $value); - $testemail = true; + $ans = get2fa($user, 'untest', 0, 0); + $mailmode = 'Cancel'; } else { - $nw = getparam('New', false); - if ($nw === 'New' and $value !== null) + $value = getparam('Value', false); + $test = getparam('Test', false); + if ($test === 'Test' and $value !== null) { - $ans = get2fa($user, 'new', rand(1073741824,2147483647), $value); - $testemail = true; + $ans = get2fa($user, 'test', 0, $value); + $mailmode = 'Test'; } else - $ans = get2fa($user, '', 0, 0); + { + $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); + } + } } } if ($ans['STATUS'] != 'ok') @@ -184,7 +215,7 @@ function do2fa($data, $user) if (isset($ans['2fa_error'])) $err = $ans['2fa_error']; - if ($testemail and $err == '') + if ($mailmode != '' and $err == '') { $ans2 = userSettings($user); if ($ans2['STATUS'] != 'ok') @@ -199,12 +230,16 @@ function do2fa($data, $user) $err = 'An error occurred, check your details below'; else { - if ($setup === 'Setup') + if ($mailmode === 'Setup') twofaSetup($email, zeip(), $emailinfo); - else if ($test === 'Test') + else if ($mailmode === 'Test') twofaEnabled($email, zeip(), $emailinfo); - else if ($nw === 'New') + else if ($mailmode === 'New') twofaSetup($email, zeip(), $emailinfo); + else if ($mailmode === 'Cancel') + twofaCancel($email, zeip(), $emailinfo); + else if ($mailmode === 'Remove') + twofaRemove($email, zeip(), $emailinfo); } } } diff --git a/src/ckdb.h b/src/ckdb.h index 7a5448be..fcb313c3 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -55,7 +55,7 @@ #define DB_VLOCK "1" #define DB_VERSION "1.0.2" -#define CKDB_VERSION DB_VERSION"-1.233" +#define CKDB_VERSION DB_VERSION"-1.240" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -2633,6 +2633,7 @@ extern bool check_2fa(USERS *users, int32_t value); extern bool tst_2fa(K_ITEM *old_u_item, int32_t value, char *by, char *code, char *inet, tv_t *cd, K_TREE *trf_root); extern K_ITEM *remove_2fa(K_ITEM *old_u_item, int32_t value, char *by, - char *code, char *inet, tv_t *cd, K_TREE *trf_root); + char *code, char *inet, tv_t *cd, K_TREE *trf_root, + bool check); #endif diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index a94abd60..7ad2e25b 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -301,9 +301,24 @@ static char *cmd_2fa(__maybe_unused PGconn *conn, char *cmd, char *id, else { key = false; sfa_status = "ok"; + sfa_error = "2FA Enabled"; } // Report sfa_error to web ok = true; + } else if (strcmp(action, "untest") == 0) { + // Can't untest if it's not ready to test + if ((users->databits & (USER_TOTPAUTH | USER_TEST2FA)) + != (USER_TOTPAUTH | USER_TEST2FA)) + goto dame; + // since it's currently test, the value isn't required + u_new = remove_2fa(u_item, 0, by, code, inet, now, + trf_root, false); + if (u_new) { + ok = true; + sfa_status = EMPTY; + key = false; + sfa_error = "2FA Cancelled"; + } } else if (strcmp(action, "new") == 0) { // Can't new if 2FA isn't already present -> setup if ((users->databits & USER_TOTPAUTH) == 0) @@ -329,18 +344,22 @@ static char *cmd_2fa(__maybe_unused PGconn *conn, char *cmd, char *id, // Can't remove if 2FA isn't already present if (!(users->databits & (USER_TOTPAUTH | USER_TEST2FA))) goto dame; + // remove requires value value = (int32_t)atoi(transfer_data(i_value)); if (!check_2fa(users, value)) { sfa_error = "Invalid code"; // Report sfa_error to web ok = true; } else { + /* already tested 2fa so don't retest, also, + * a retest will fail using the same value */ u_new = remove_2fa(u_item, value, by, code, - inet, now, trf_root); + inet, now, trf_root, false); if (u_new) { ok = true; sfa_status = EMPTY; key = false; + sfa_error = "2FA Removed"; } } } diff --git a/src/ckdb_crypt.c b/src/ckdb_crypt.c index d285e9ac..7ad0109f 100644 --- a/src/ckdb_crypt.c +++ b/src/ckdb_crypt.c @@ -237,14 +237,17 @@ bool tst_2fa(K_ITEM *old_u_item, int32_t value, char *by, char *code, } K_ITEM *remove_2fa(K_ITEM *old_u_item, int32_t value, char *by, char *code, - char *inet, tv_t *cd, K_TREE *trf_root) + char *inet, tv_t *cd, K_TREE *trf_root, bool check) { K_ITEM *u_item = NULL; USERS *old_users, *users; - bool ok, did = false; + bool ok = true, did = false; DATA_USERS(old_users, old_u_item); - ok = check_2fa(old_users, value); + /* N.B. check_2fa will fail if it is called a second time + * with the same value */ + if (check) + ok = check_2fa(old_users, value); if (ok) { K_WLOCK(users_free); u_item = k_unlink_head(users_free);