<?php
require_once(__DIR__ . '/sql.inc.php');
require_once(__DIR__ . '/state.inc.php');
require_once(__DIR__ . '/prof.inc.php');
require_once(__DIR__ . '/mail.inc.php');
require_once(__DIR__ . '/events.inc.php');
require_once(__DIR__ . '/cache.inc.php');
if (!isset($rg_max_ssh_keys))
$rg_max_ssh_keys = 30;
$rg_keys_error = "";
function rg_keys_set_error($str)
{
global $rg_keys_error;
$rg_keys_error = $str;
rg_log($str);
}
function rg_keys_error()
{
global $rg_keys_error;
return $rg_keys_error;
}
/*
* Events functions
*/
$rg_keys_functions = array(
1000 => "rg_keys_event_new",
1001 => "rg_keys_event_del",
1002 => "rg_keys_event_regen",
1003 => "rg_keys_event_notify_user",
// new style
'rg_keys_event_regen' => 'rg_keys_event_regen',
// new new style
'keys_event_new' => 'rg_keys_event_new',
'keys_event_del' => 'rg_keys_event_del',
'keys_event_regen' => 'rg_keys_event_regen',
'keys_event_notify_user' => 'rg_keys_event_notify_user'
);
rg_event_register_functions($rg_keys_functions);
/*
* Event for adding a new key
*/
function rg_keys_event_new($db, $event)
{
$ret = array();
$event['op'] = "new";
// mark keys dirty
$ret[] = array_merge($event, array(
'category' => 'rg_keys_event_regen',
'source' => 'keys_event_new',
'prio' => 10,
'source' => 'keys_event_new')
);
// notify user
$ret[] = array_merge($event,
array('category' => 'keys_event_notify_user', 'prio' => 100));
return $ret;
}
/*
* Event for deleting a key
*/
function rg_keys_event_del($db, $event)
{
$ret = array();
$event['type'] = 1;
$event['op'] = "del";
$event['source'] = 'keys_event_del';
// mark keys dirty
$ret[] = array_merge($event, array(
'category' => 'rg_keys_event_regen',
'prio' => 10,
'source' => 'keys_event_del')
);
// notify user
$ret[] = array_merge($event,
array('category' => 'keys_event_notify_user', 'prio' => 100));
return $ret;
}
/*
* Regenerate keyring.
* We ignore requests that were inserted in queue after we already
* regenerated the keys.
*/
function rg_keys_event_regen($db, $event)
{
rg_log("keys_event_regen");
$last = rg_cache_get("key::last_regen_time");
if ($last === FALSE)
$last = 0;
if ($event['itime'] < $last) {
rg_log_debug('event itime(' . $event['itime'] . ') < last(' . $last . ')'
. '; skip regeneration of keys');
} else {
$r = rg_keys_regen($db);
if ($r === FALSE)
return FALSE;
}
return array();
}
/*
* Notify user that a new key was added to the keyring
*/
function rg_keys_event_notify_user($db, $event)
{
rg_prof_start("keys_event_notify_user");
rg_log("keys_event_notify_user: event=" . rg_array2string($event));
// TODO: del: Maybe add also the statistics.
// TODO: del: Do not forget that here we have a list
// TODO: del: Take care: we already deleted the keys. We cannot inspect
// them anymore! Maybe put info in the event.
$ret = FALSE;
while (1) {
$r = rg_mail_template("mail/user/key/" . $event['op'], $event);
if ($r === FALSE)
break;
$ret = array();
break;
}
rg_prof_end("keys_event_notify_user");
return $ret;
}
/*
* Returns TRUE if the key is too weak by the admin standards
* @ki - output of rg_keys_info()
*/
function rg_keys_weak($db, $ki)
{
$ret = array('ok' => 0, 'weak' => 1);
if (strcmp($ki['type'], 'ssh-rsa') == 0) {
$min = rg_state_get_uint($db, 'ssh_key_min_bits_rsa');
if ($min === FALSE) {
rg_keys_set_error('cannot lookup state');
return $ret;
}
if ($ki['bits'] < $min) {
rg_keys_set_error('RSA key has less than '
. $min . ' bits (' . $ki['bits'] . ')');
$ret['ok'] = 1;
return $ret;
}
} else if (strcmp($ki['type'], 'ssh-dss') == 0) {
$r = rg_state_get_uint($db, 'ssh_key_allow_dsa');
if ($r === FALSE) {
rg_keys_set_error('cannot lookup state');
return $ret;
}
if ($r != 1) {
rg_keys_set_error('DSA keys are not allowed');
$ret['ok'] = 1;
return $ret;
}
} else if (strncmp($ki['type'], 'ecdsa-', 6) == 0) {
$min = rg_state_get_uint($db, 'ssh_key_min_bits_ecdsa');
if ($min === FALSE) {
rg_keys_set_error('cannot lookup state');
return $ret;
}
if ($ki['bits'] < $min) {
rg_keys_set_error('ECDSA key has less than '
. $min . ' bits (' . $ki['bits'] . ')');
$ret['ok'] = 1;
return $ret;
}
}
$ret['ok'] = 1;
$ret['weak'] = 0;
return $ret;
}
/*
* Extracts info about a ssh key
* 'sk' key formats: https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.u2f
*/
function rg_keys_info($key)
{
rg_prof_start("keys_info");
rg_log_enter('keys_info key=' . $key);
$ret = array();
$ret['ok'] = 0;
while(1) {
$key = trim($key);
$key = str_replace("\n", ' ', $key);
if (empty($key)) {
rg_keys_set_error('you did not uploaded the key');
break;
}
if (strpos($key, "PRIVATE KEY") !== FALSE) {
rg_keys_set_error("private instead of public key");
break;
}
// We must have at least key type and the key
$t = explode(' ', $key, 2);
if (!isset($t[1])) {
rg_keys_set_error("malformed ssh key (missing fields)");
break;
}
$ret['type'] = $t[0];
if ((strncmp($ret['type'], 'ssh-', 4) != 0)
&& (strncmp($ret['type'], 'sk-ssh-', 7) != 0)
&& (strncmp($ret['type'], 'ecdsa-', 6) != 0)
&& (strncmp($ret['type'], 'sk-ecdsa-', 9) != 0)
) {
rg_log('key: ' . $key);
rg_keys_set_error('key does not start with [sk-]ssh-'
. ' or [sk-]ecdsa-');
break;
}
// We try to detect the key because spaces may mess up things
$ret['comment'] = '';
$ret['extra_info'] = '';
$error = TRUE;
$off = 0;
while (1) {
rg_log_debug('off=' . $off);
// -1 signals that we used the whole string
if ($off == -1)
break;
$pos = strpos($t[1], ' ', $off);
if ($pos === FALSE) {
$ret['key'] = $t[1];
$off = -1;
} else {
$ret['key'] = substr($t[1], 0, $pos);
$ret['key'] = str_replace(' ', '', $ret['key']);
$off = $pos + 1;
}
rg_log_debug('pos=' . $pos . ' off=' . $off . ' key=' . $ret['key']);
$d = base64_decode($ret['key']);
if ($d === FALSE) {
rg_keys_set_error("malformed ssh key (base64 failed)");
continue;
}
$d_len = strlen($d);
rg_log('d_len=' . $d_len . ' d:' . bin2hex($d));
if ($d_len < 4) {
rg_keys_set_error("key is too short (< 4)");
continue;
}
// First, we have the length of the string '[sk-]ssh-*'
$_t = unpack('N', $d);
$len = $_t[1];
rg_log_ml("len=$len");
if ($d_len < 4 + $len) {
rg_keys_set_error("key is too short");
continue;
}
$type2 = substr($d, 4, $len);
rg_log_debug('type2=' . $type2);
if (strcasecmp($ret['type'], $type2) != 0) {
rg_keys_set_error('key type mismatch: ' . $ret['type'] . ' != ' . $type2);
break;
}
$bits_div = 1;
$bits_sub = 1;
$fixes = array();
$bits_pos = 2000; // 2000 = not present
$extra_info_pos = 2000;
if (strcasecmp($ret['type'], 'ssh-rsa') == 0) {
// OK
$count = 2;
$bits_pos = 1;
} else if (strcasecmp($ret['type'], 'ssh-dss') == 0) {
// Always 1024 - OK
$count = 4;
$bits_pos = 3;
$bits_sub = 0;
} else if (strncasecmp($ret['type'], 'ecdsa-', 6) == 0) {
// Possible: 256, 384, 521 - OK
$count = 2;
$bits_pos = 1;
$bits_div = 2;
$fixes[528] = 521;
} else if (strcasecmp($ret['type'], 'sk-ecdsa-sha2-nistp256@openssh.com') == 0) {
// pos 0: len + curve_name [example: "nistp256" ]
// pos 1: len + ec_point Q
// pos 2: len + application (default "ssh:")
$count = 3;
$ret['bits'] = 256; // always 256
$bits_pos = 2000;
$extra_info_pos = 2;
} else if (strcasecmp($ret['type'], 'ssh-ed25519') == 0) {
// Always 256 - OK
$count = 1;
$bits_pos = 0;
$bits_sub = 0;
} else if (strcasecmp($ret['type'], 'sk-ssh-ed25519@openssh.com') == 0) {
// pos 0: len(32) + public key
// pos 1: len + application (default "ssh:")
// Always 256
$count = 2;
$bits_pos = 0;
$bits_sub = 0;
$extra_info_pos = 1;
} else {
rg_log('Strange key type: ' . $ret['type']);
// Probably this is a new key type, just
// consider it valid
$_t = explode(' ', $key, 3);
$ret['key'] = $_t[1];
if (isset($_t[2]))
$ret['comment'] = trim($_t[2]);
$error = FALSE;
break;
}
rg_log_debug('count=' . $count);
$have_all_chunks = TRUE;
$used = 4 + $len;
for ($i = 0; $i < $count; $i++) {
if ($d_len < $used + 4) {
rg_keys_set_error('key is too short (chunk ' . $i . '):'
. ' ' . $d_len . ' < ' . ($used + 4));
$have_all_chunks = FALSE;
break;
}
$_t = unpack('N', substr($d, $used, 4)); $used += 4;
$xlen = $_t[1];
rg_log_debug('xlen=' . $xlen);
//rg_log_debug('bin: ' . bin2hex(substr($d, $used, $xlen)));
//rg_log_debug('ascii: ' . substr($d, $used, $xlen));
if ($d_len < $used + $xlen) {
rg_keys_set_error('key is too short (chunk ' . $i . '):'
. ' ' . $d_len . ' < ' . ($used + $xlen));
$have_all_chunks = FALSE;
break;
}
if ($i == $bits_pos) {
rg_log_debug('bits_sub=' . $bits_sub . ' bits_div=' . $bits_div);
$ret['bits'] = (($xlen - $bits_sub) / $bits_div) * 8;
if (isset($fixes[$ret['bits']])) {
rg_log_debug('apply fix from ' . $ret['bits'] . ' to ' . $fixes[$ret['bits']]);
$ret['bits'] = $fixes[$ret['bits']];
}
} else if ($i == $extra_info_pos) {
$_x = unpack('a*', substr($d, $used, $xlen));
$ret['extra_info'] = 'application=' . $_x[1];
rg_log_debug('extra_info set to [' . $ret['extra_info'] . ']');
}
$used += $xlen;
}
if ($have_all_chunks === FALSE) {
rg_log_debug('not all chunk present, starting over');
continue;
}
$ret['comment'] = trim(substr($t[1], $off));
$ret['key'] = trim($ret['key']);
$error = FALSE;
break;
}
if ($error)
break;
$digest = md5($d);
$a = array();
for ($i = 0; $i < 16; $i++)
$a[] = substr($digest, $i * 2, 2);
$ret['fingerprint_md5'] = implode(":", $a);
$_x = base64_encode(hash('sha256', $d, TRUE));
$ret['fingerprint_sha256'] = rtrim($_x, "=");
$ret['ok'] = 1;
break;
}
rg_log_exit();
rg_prof_end("keys_info");
return $ret;
}
/*
* Remove keys from database for user 'ui_login'
*/
function rg_keys_remove($db, $ui_login, $list)
{
rg_prof_start("keys_remove");
rg_log_enter("keys_remove: list=" . rg_array2string($list));
$ret = FALSE;
while (1) {
$my_list = array();
foreach ($list as $key_id => $junk)
$my_list[] = sprintf("%u", $key_id);
$params = array("uid" => $ui_login['uid']);
$sql_list = implode(", ", $my_list);
$sql = "DELETE FROM keys"
. " WHERE uid = @@uid@@"
. " AND key_id IN (" . $sql_list . ")";
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_keys_set_error("cannot delete keys"
. " (" . rg_sql_error() . ")");
break;
}
rg_sql_free_result($res);
$event = array(
'category' => 'keys_event_del',
'source' => 'keys_remove',
'prio' => 50,
'ui_login' => $ui_login,
'keys' => implode(',', $my_list));
$r = rg_event_add($db, $event);
if ($r !== TRUE) {
rg_keys_set_error("cannot add event"
. " (" . rg_event_error() . ")");
break;
}
$key = 'user' . '::' . $ui_login['uid'] . '::' . 'keys';
foreach ($my_list as $_key_id)
rg_cache_unset($key . '::' . $_key_id,
RG_SOCKET_NO_WAIT);
rg_event_signal_daemon('', 0);
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end("keys_remove");
return $ret;
}
/*
* Count the number of keys per user
*/
function rg_keys_count($db, $uid)
{
rg_prof_start("keys_count");
$ret = FALSE;
while (1) {
$params = array("uid" => $uid);
$sql = "SELECT COUNT(*) AS count FROM keys"
. " WHERE uid = @@uid@@";
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_keys_set_error("cannot query (" . rg_sql_error() . ")");
break;
}
$row = rg_sql_fetch_array($res);
rg_sql_free_result($res);
$ret = $row['count'];
break;
}
rg_prof_end("keys_count");
return $ret;
}
/*
* Returns the maximum number of keys allowed per user
*/
function rg_keys_max($db)
{
global $rg_max_ssh_keys;
$r = rg_state_get($db, 'max_ssh_keys');
if (($r === FALSE) || empty($r))
return $rg_max_ssh_keys;
return intval($r);
}
/*
* Adds a key
* Returns the key_id of the key.
*/
function rg_keys_add($db, $ui_login, $key, $flags)
{
rg_prof_start("keys_add");
rg_log_enter('keys_add: flags=' . $flags . ' key=' . $key);
$ret = FALSE;
$do_rollback = 0;
while (1) {
$itime = time();
$ki = rg_keys_info($key);
if ($ki['ok'] != 1)
break;
$r = rg_keys_weak($db, $ki);
if ($r['ok'] != 1)
break;
if ($r['weak'] != 0)
break;
// Check if we are over the maximum
// the config after update may not have this defined.
$no_of_keys = rg_keys_count($db, $ui_login['uid']);
if ($no_of_keys === FALSE)
break;
if ($no_of_keys >= rg_keys_max($db)) {
rg_keys_set_error("too many keys"
. " (" . $no_of_keys . "); please delete some");
break;
}
$r = rg_sql_begin($db);
if ($r !== TRUE) {
rg_keys_set_error("cannot start transaction"
. " (" . rg_sql_error() . ")");
break;
}
$do_rollback = 1;
$params = array(
'itime' => $itime,
'uid' => $ui_login['uid'],
'key' => $ki['type'] . ' ' . $ki['key']
. ' ' . $ki['comment'],
'count' => 0,
'first_use' => 0,
'fingerprint_sha256' => $ki['fingerprint_sha256'],
'flags' => $flags);
$sql = "INSERT INTO keys (itime, uid, key"
. ", fingerprint_sha256, flags)"
. " VALUES (@@itime@@, @@uid@@, @@key@@"
. ", @@fingerprint_sha256@@, @@flags@@)"
. " RETURNING key_id";
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_keys_set_error("cannot insert key"
. " (" . rg_sql_error() . ")");
break;
}
$row = rg_sql_fetch_array($res);
$key_id = $row['key_id'];
rg_sql_free_result($res);
$event = array(
'category' => 'keys_event_new',
'source' => 'keys_add',
'prio' => 50,
'ui_login' => $ui_login,
'ki' => $ki,
'key_id' => $key_id);
$r = rg_event_add($db, $event);
if ($r !== TRUE) {
rg_keys_set_error("cannot add event"
. " (" . rg_event_error() . ")");
break;
}
$r = rg_sql_commit($db);
if ($r !== TRUE) {
rg_keys_set_error("cannot commit transaction"
. " (" . rg_sql_error() . ")");
break;
}
$do_rollback = 0;
$_key = 'user' . '::' . $ui_login['uid'] . '::'
. 'keys' . '::' . $key_id;
rg_cache_merge($_key, $params, RG_SOCKET_NO_WAIT);
rg_event_signal_daemon('', 0);
$ret = $key_id;
break;
}
if ($do_rollback == 1)
rg_sql_rollback($db);
rg_log_exit();
rg_prof_end("keys_add");
return $ret;
}
/*
* Update first_use, last_use, last_ip and count
*/
function rg_keys_update_use($db, $uid, $key_id, $cmd)
{
// The build system will not update table 'keys'
if ($key_id == 0)
return TRUE;
rg_prof_start("keys_update_use");
rg_log_enter('keys_update_use: uid=' . $uid . ' key_id=' . $key_id
. ' cmd=' . $cmd);
$ret = FALSE;
while (1) {
$now = time();
$update_first_use = TRUE;
$update_last_use = TRUE;
$key = 'user' . '::' . $uid . '::' . 'keys' . '::' . $key_id;
$c = rg_cache_get($key);
if ($c !== FALSE) {
if (isset($c['first_use']) && ($c['first_use'] > 0))
$update_first_use = FALSE;
// We will not update the field if is too soon
if (isset($c['last_use'])
&& (strcmp(rg_ip(), $c['last_ip']) == 0)
&& (strcmp($cmd, $c['last_cmd']) == 0)
&& ($now - $c['last_use'] < 60))
$update_last_use = FALSE;
}
$params = array(
'now' => $now,
'key_id' => $key_id,
'ip' => rg_ip(),
'last_cmd' => $cmd);
if ($update_first_use) {
$sql = "UPDATE keys SET first_use = @@now@@"
. " WHERE first_use = 0"
. " AND key_id = @@key_id@@";
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_keys_set_error("cannot update key's first use");
break;
}
rg_sql_free_result($res);
rg_cache_set($key . '::' . 'first_use', $now,
RG_SOCKET_NO_WAIT);
}
if ($update_last_use) {
$sql = "UPDATE keys SET last_use = @@now@@"
. ", last_ip = @@ip@@"
. ", last_cmd = @@last_cmd@@"
. ", count = count + 1"
. " WHERE key_id = @@key_id@@";
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_keys_set_error("cannot update key"
. " (" . rg_sql_error() . ")");
break;
}
rg_sql_free_result($res);
$a = array(
'last_use' => $now,
'last_ip' => rg_ip(),
'last_cmd' => $cmd);
rg_cache_merge($key, $a, RG_SOCKET_NO_WAIT);
}
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end("keys_update_use");
return $ret;
}
/*
* Returns flags for keys_output_line function
*/
function rg_keys_global_flags($db)
{
$ret = '';
$fido2_security = rg_state_get_uint($db, 'fido2_security');
if ($fido2_security == 2) // PIN required
$ret .= 'V';
else if ($fido2_security == 0) // no-touch allowed
$ret .= 't';
return $ret;
}
/*
* Outputs a line for authorized_keys file
*/
function rg_keys_output_line($i, $flags)
{
global $rg_scripts;
global $rg_ssh_paras;
$ret = 'command="'
. $rg_scripts . '/scripts/remote.sh'
. ' ' . $i['uid']
. ' ' . $i['key_id']
. ' ' . $i['flags']
. '"'
. ',' . $rg_ssh_paras;
if (strstr($flags, 't'))
$ret .= ',no-touch-required';
if (strstr($flags, 'V'))
$ret .= ',verify-required';
$ret .= ' ' . $i['key'] . "\n";
return $ret;
}
/*
* Regenerates authorized_keys files
*/
function rg_keys_regen($db)
{
global $rg_keys_file;
global $rg_scripts;
rg_prof_start('keys_regen');
rg_log_enter('keys_regen');
$now = time();
$ret = FALSE;
while (1) {
$akp = rg_state_get($db, 'AuthorizedKeysCommand');
if ($akp === FALSE) {
rg_keys_set_error('cannot get state of AuthorizedKeysCommand');
break;
}
$akp = intval($akp);
if ($akp == 1) {
rg_log_debug('No need to regenerate because'
. ' AuthorizedKeysCommand is active');
if (file_exists($rg_keys_file))
@unlink($rg_keys_file);
$ret = TRUE;
break;
}
$global_flags = rg_keys_global_flags($db);
// create .ssh folder if does not exists
$dir = dirname($rg_keys_file);
if (!file_exists($dir)) {
if (!@mkdir($dir, 0700, TRUE)) {
rg_keys_set_error("cannot create dir [$dir] (" . rg_php_err() . ")");
break;
}
chown($dir, "rocketgit");
chgrp($dir, "rocketgit");
}
$tmp = $rg_keys_file . ".tmp";
$f = @fopen($tmp, "w");
if ($f === FALSE) {
rg_keys_set_error("cannot open file $tmp (" . rg_php_err() . ")");
break;
}
if (chmod($tmp, 0600) === FALSE) {
rg_keys_set_error("cannot chmod tmp file (" . rg_php_err() . ")");
fclose($f);
break;
}
chown($tmp, "rocketgit");
chgrp($tmp, "rocketgit");
$list = array();
$sql = 'SELECT key_id, uid, key, flags'
. ' FROM keys ORDER BY count DESC';
$res = rg_sql_query($db, $sql);
if ($res === FALSE) {
rg_keys_set_error('cannot query keys table');
break;
}
while (($row = rg_sql_fetch_array($res))) {
$row['flags'] .= 'N';
$list[] = $row;
}
rg_sql_free_result($res);
$sql = 'SELECT id, who, ssh_key FROM workers';
$res = rg_sql_query($db, $sql);
if ($res === FALSE) {
rg_keys_set_error('cannot query workers table');
break;
}
while (($row = rg_sql_fetch_array($res))) {
$a = array(
'key_id' => $row['id'],
'uid' => $row['who'],
'key' => $row['ssh_key'],
'flags' => 'W'
);
$list[] = $a;
}
rg_sql_free_result($res);
$errors = 0;
foreach ($list as $row) {
// Ignore invalid keys
$ki = rg_keys_info($row['key']);
if ($ki['ok'] != 1)
continue;
// Ignore weak keys
$r = rg_keys_weak($db, $ki);
if ($r['ok'] != 1)
continue;
if ($r['weak'] != 0)
continue;
//rg_log("Writing key [" . $row['key'] . "] for uid " . $row['uid']);
$buf = rg_keys_output_line($row, $global_flags);
if (@fwrite($f, $buf) === FALSE) {
rg_keys_set_error("cannot write; disk space problems? (" . rg_php_err() . ")");
$errors = 1;
break;
}
}
fclose($f);
if ($errors == 1) {
unlink($tmp);
break;
}
if (@rename($tmp, $rg_keys_file) === FALSE) {
rg_keys_set_error("cannot rename $tmp to $rg_keys_file (" . rg_php_err() . ")");
unlink($tmp);
break;
}
rg_cache_set("key::last_regen_time", $now, RG_SOCKET_NO_WAIT);
$ret = TRUE;
break;
}
rg_log_exit();
rg_prof_end('keys_regen');
return $ret;
}
/*
* List keys
*/
function rg_keys_list($db, $ui_login)
{
rg_prof_start("keys_list");
rg_log_enter("keys_list: uid=" . $ui_login['uid']);
$ret = FALSE;
while (1) {
$params = array('uid' => $ui_login['uid']);
$sql = "SELECT * FROM keys WHERE uid = @@uid@@"
. " ORDER BY itime DESC";
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_keys_set_error('cannot select from db');
break;
}
$ret = array();
while (($row = rg_sql_fetch_array($res))) {
$ki = rg_keys_info($row['key']);
if ($ki['ok'] != 1) {
rg_internal_error("Invalid key in db!");
continue;
}
$r = rg_keys_weak($db, $ki);
if ($r['ok'] != 1)
continue;
$ki['weak'] = $r['weak'];
$t = $ki;
$t['key_id'] = $row['key_id'];
if ($row['itime'] == 0)
$t['itime'] = "N/A";
else
$t['itime'] = gmdate("Y-m-d H:i", $row['itime']);
if ($row['first_use'] == 0)
$t['first_use'] = "N/A";
else
$t['first_use'] = gmdate("Y-m-d H:i", $row['first_use']);
if (empty($row['last_ip']))
$t['last_ip'] = "N/A";
else
$t['last_ip'] = $row['last_ip'];
if ($row['last_use'] == 0)
$t['last_use'] = "N/A";
else
$t['last_use'] = gmdate("Y-m-d H:i", $row['last_use']);
if (empty($row['last_cmd']))
$t['last_cmd'] = "N/A";
else
$t['last_cmd'] = $row['last_cmd'];
$t['count'] = $row['count'];
$ret[] = $t;
}
rg_sql_free_result($res);
break;
}
rg_log_exit();
rg_prof_end("keys_list");
return $ret;
}
/*
* Search a key by fingerprint
* Used for OpenSSH (rg_authorize script)
*/
function rg_keys_search_by_fingerprint($db, $fp)
{
rg_prof_start('keys_search_by_fingerprint');
$ret = array('ok' => 0, 'list' => array());
while (1) {
$params = array('fp' => $fp);
$sql = 'SELECT key_id, uid, key, flags FROM keys'
. ' WHERE fingerprint_sha256 = @@fp@@';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_keys_set_error('cannot select from keys table');
break;
}
while (($row = rg_sql_fetch_array($res))) {
$row['flags'] .= 'N';
$ret['list'][] = $row;
}
rg_sql_free_result($res);
$sql = 'SELECT id, who, ssh_key FROM workers'
. ' WHERE fingerprint_sha256 = @@fp@@';
$res = rg_sql_query_params($db, $sql, $params);
if ($res === FALSE) {
rg_keys_set_error('cannot select from workers table');
break;
}
while (($row = rg_sql_fetch_array($res))) {
$row2 = array(
'key_id' => $row['id'],
'uid' => $row['who'],
'key' => $row['ssh_key'],
'flags' => 'W'
);
$ret['list'][] = $row2;
}
rg_sql_free_result($res);
$ret['ok'] = 1;
break;
}
rg_prof_end('keys_search_by_fingerprint');
return $ret;
}