<?php // // Description: This file deals with commands accepted by ssh // require_once($INC . '/sql.inc.php'); require_once($INC . '/state.inc.php'); require_once($INC . '/prof.inc.php'); require_once($INC . '/repo.inc.php'); require_once($INC . '/totp.inc.php'); function rg_ssh_status($db, $uid) { rg_log('ssh_status'); echo 'Here will be the status.' . "\n"; // also details about payments: warn if disk space is low etc. } /* * List repos */ function rg_ssh_repos($db, $uid) { rg_log('ssh_repos'); $params = array('uid' => $uid); $sql = 'SELECT * FROM repos' . ' WHERE uid = @@uid@@' . ' AND deleted = 0' . ' ORDER BY name, itime'; $pad = ' '; $res = rg_sql_query_params($db, $sql, $params); $rows = rg_sql_num_rows($res); if ($rows > 0) { echo 'Repositories (name, creation, disk used):' . "\n"; while (($row = rg_sql_fetch_array($res))) { $_name = mb_substr($row['name'], 0, 40, 'UTF-8'); echo mb_substr($_name . $pad, 0, 32, 'UTF-8') . ' ' . gmdate('Y-m-d', $row['itime']) . ' ' . rg_1024($row['disk_used_mb'] * 1024 * 1024) . "\n"; } } else { echo 'You have no repository.' . "\n"; } rg_sql_free_result($res); } /* * Info about a repo */ function rg_ssh_repo($db, $uid, $paras) { rg_log('ssh_repo: ' . rg_array2string($paras)); if (empty($paras)) { echo 'Please specify the repo name.' . "\n"; exit(0); } $repo_name = trim($paras[0]); $ri = rg_repo_info($db, 0, $uid, $repo_name); if ($ri['exists'] != 1) { echo 'Error: unknown repo.' . "\n"; exit(0); } echo 'Repo: ' . $ri['name'] . "\n"; echo 'Repo type: ' . ($ri['public'] == 1 ? 'public' : 'private') . "\n"; echo 'Creation time: ' . gmdate('Y-m-d', $ri['itime']) . ' UTC' . "\n"; echo 'Disk used: ' . rg_1024($ri['disk_used_mb'] * 1024 * 1024) . "\n"; if ($ri['master'] > 0) { $mri = rg_repo_info($db, $ri['master'], 0, ''); if ($mri !== FALSE) { echo 'Master: ' . $mri['name'] . "\n"; } else { echo 'Master: ' . 'Error getting info' . "\n"; } } echo 'Description:' . "\n"; $_d = explode("\n", $ri['description']); foreach ($_d as $_line) echo ' ' . $_line . "\n"; $ls = rg_repo_lock_status($db, $ri['repo_id']); if ($ls['ok'] == 1) { if ($ls['status'] == 0) { echo 'Repository is not locked.' . "\n"; } else { $_ui = rg_user_info($db, $ls['uid'], '', ''); if ($_ui['exists'] == 1) $_u = $_ui['username']; else $_u = '?'; $reason = ''; $_r = explode("\n", $ls['reason']); foreach ($_r as $_line) $reason .= ' ' . $_line . "\n"; echo 'Repository has been locked by user ' . $_u . ' at ' . gmdate('Y-m-d H:i', $ls['itime']) . ' UTC.' . ' Reason:' . "\n" . $reason . "\n"; } } else { echo 'Error: cannot get info about the lock status!' . "\n"; } } /* * Helper for totp_verify_ip - mostly to not duplicate error messages */ function rg_ssh_totp_verify_ip($db, $uid, $ip) { $ret = FALSE; while (1) { $r = rg_totp_verify_ip($db, $uid, $ip); if (($r['ok'] == 0) || (empty($r['ip_list']))) { echo 'Error: ' . rg_totp_error() . ".\n"; break; } if ($r['enrolled'] == 0) { echo 'Info: You are not enrolled.' . "\n"; break; } $ret = $r['ip_list']; break; } return $ret; } /* * Deal with TOTP stuff */ function rg_ssh_totp($db, $ip, $uid, $paras) { rg_prof_start('ssh_totp'); rg_log_enter('ssh_totp ip=' . $ip . ' uid=' . $uid . ' paras=' . rg_array2string($paras)); $cmd = array_shift($paras); switch ($cmd) { case 'enroll': // this has nothing to do with scratch codes if (empty($paras)) { $secret = rg_totp_base32_generate(16); $r = rg_totp_enroll($db, $uid, 'SSH', $secret, $ip, 'f'); if ($r !== TRUE) { echo 'Error: ' . rg_totp_error() . ".\n"; break; } echo 'Scan the following code with the mobile application:' . "\n"; echo rg_totp_text($secret) . "\n"; echo 'or manually enter the following code: ' . $secret . ".\n"; echo 'To finish the enrollment you will have to confirm'; echo ' it by running the following command:'; echo ' ssh ... totp enroll <6_digits_code_generated_by_device>' . "\n"; break; } $token = array_shift($paras); $v = rg_totp_device_verify($db, $uid, $token); if ($v['token_valid'] != 1) { echo 'Error: ' . rg_totp_error() . ".\n"; break; } echo 'Success!' . "\n"; break; case 'val': $token = array_shift($paras); $days = 0; $hours = 0; $minutes = 0; if (empty($paras)) { $hours = 1; } else { $s_expire = array_shift($paras); $val = intval($s_expire); if (stristr($s_expire, 'w')) $days = 7 * $val; else if (stristr($s_expire, 'd')) $days = $val; else if (stristr($s_expire, 'h')) $hours = $val; else $minutes = $val; } //rg_log("token=$token days=$days hours=$hours minutes=$minutes"); $v = rg_totp_verify_any($db, $uid, $token); if ($v['token_valid'] != 1) { echo 'Error: ' . rg_totp_error() . ".\n"; break; } $expire_ts = gmmktime(gmdate('H') + $hours, gmdate('i') + $minutes, gmdate('s'), gmdate('m'), gmdate('d') + $days, gmdate('Y')); $r = rg_totp_add_ip($db, $uid, $v['id'], $ip, $expire_ts); if ($r === FALSE) { echo 'Error: ' . rg_totp_error() . ".\n"; break; } echo 'Success! IP ' . $ip . ' added and is valid till ' . gmdate('Y-m-d H:i:s', $expire_ts) . ' (UTC).' . "\n"; break; case 'list-val': $r = rg_ssh_totp_verify_ip($db, $uid, $ip); if ($r === FALSE) break; echo 'Insert date (UTC) Expire date (UTC) IP' . "\n"; foreach ($r as $t) { echo gmdate('Y-m-d H:i:s', $t['itime']) . ' ' . gmdate('Y-m-d H:i:s', $t['expire']) . ' ' . $t['ip'] . "\n"; } echo 'Done!' . "\n"; break; case 'unenroll': $token = array_shift($paras); $v = rg_totp_verify_any($db, $uid, $token); if ($v['token_valid'] != 1) { echo 'Error: ' . rg_totp_error() . ".\n"; break; } $r = rg_totp_unenroll($db, $uid); if ($r !== TRUE) { echo'Error: ' . rg_totp_error() . ".\n"; break; } echo 'You are now unenrolled.' . "\n"; break; case 'remove-device': $token = array_shift($paras); $v = rg_totp_device_verify($db, $uid, $token); if ($v['token_valid'] != 1) { echo 'Error: ' . rg_totp_error() . ".\n"; break; } $a = array($v['id'] => ''); $r = rg_totp_remove($db, $uid, $a); if ($r !== TRUE) { echo 'Error: ' . rg_totp_error() . ".\n"; break; } echo 'Success!' . "\n"; break; case 'inval': if (empty($paras)) { echo 'Error: Please specify the IP address or \'all\'.' . "\n"; break; } $del_ip = array_shift($paras); if (rg_ssh_totp_verify_ip($db, $uid, $ip) === FALSE) break; $r = rg_totp_del_ip($db, $uid, $del_ip); if ($r['found'] != 1) { echo 'Error: ' . rg_totp_error() . ".\n"; break; } echo 'Success!' . "\n"; break; default: echo 'Posible TOTP commands:' . "\n"; echo ' enroll <token> - adds a new device in the system' . "\n"; echo ' val [X(w|d|h|m|s)] <token> - adds your IP to the allow list for X time' . "\n"; echo ' the default is 1 hour; X is a number; defauls is \'minutes\';' . "\n"; echo ' w=weeks, d=days, h=hours, m=minutes, and s for seconds' . "\n"; echo ' list-val - lists the already validated IPs' . "\n"; echo ' inval ip|all - invalidates IP address(es)' . "\n"; echo ' remove-device <token> - removes a device from TOTP system' . "\n"; echo ' unenroll <token> - removes all devices and scratch codes from TOTP system' . "\n"; echo "\n"; echo 'Notes:' . "\n"; echo ' - <token> means a code generated by mobile device or a scratch code' . "\n"; break; } rg_log_exit(); rg_prof_end('ssh_totp'); } /* * Returns TRUE if we need to stop execution */ function rg_ssh_dispatch($db, $ip, $uid, $orig_cmd) { rg_log('ssh_dispatch orig_cmd=[' . $orig_cmd . ']'); $paras = explode(' ', $orig_cmd); $cmd = array_shift($paras); // some commands are not executed here switch ($cmd) { case 'git-upload-pack'; return; case 'git-receive-pack': return; } // First, test if the IP is validated switch ($cmd) { case '': break; case 'totp': break; // totp will verify the ip only for some commands default: $r = rg_totp_verify_ip($db, $uid, $ip); if (($r['ok'] == 0) || (($r['enrolled'] == 1) && (empty($r['ip_list'])))) { echo 'Error: ' . rg_totp_error() . ".\n"; return TRUE; // = we must exit' } break; } // Now, we can safely execute the command switch ($cmd) { case 'status': rg_ssh_status($db, $uid); return TRUE; case 'repos': rg_ssh_repos($db, $uid); return TRUE; case 'repo': rg_ssh_repo($db, $uid, $paras); return TRUE; case 'totp': rg_ssh_totp($db, $ip, $uid, $paras); return TRUE; case '': echo "Available commmands:\n" . " status - show some status about the user\n" . " repos - list repos and information about them\n" . " repo - list info about a repo\n" . " totp - two-factor authentication commands\n"; return TRUE; } return FALSE; // = continue execution } ?>