Mercurial > hg > marc_php
view marcus.php @ 4:c642254dc9ee draft default tip
Fixed transfer chain generation and construction of empty updates, some small improvements in tools
author | Ivo Smits <Ivo@UCIS.nl> |
---|---|
date | Sat, 22 Nov 2014 18:18:52 +0100 |
parents | 5c8c4fa95803 |
children |
line wrap: on
line source
<?php require_once './marccore.php'; error_reporting(E_ALL); if (!isset($argv)) $argv = $_SERVER['argv']; $argi = 1; class FilteredResourceIterator implements Iterator { private $source, $filtertype, $filtervalue; public function __construct($source, $filtertype, $filtervalue) { $this->source = $source; $this->filtertype = $filtertype; $this->filtervalue = $filtervalue; } public function current() { $r = $this->source->current(); return $r; } public function key() { return $this->source->key(); } public function valid() { return $this->source->valid(); } public function next() { $this->source->next(); $this->findnext(); } public function rewind() { $this->source->rewind(); $this->findnext(); } private function findnext() { while ($this->source->valid()) { $c = $this->source->current(); if ($this->filter($c)) break; $this->source->next(); } } private function filter($c) { switch ($this->filtertype) { case 'OWNER': return isset($c['value']['owner']) && is_scalar($c['value']['owner']) && strcasecmp($c['value']['owner'], $this->filtervalue) == 0; case 'KEY': return $c['key'] == hex2bin($this->filtervalue); case 'DOMEXT': return ord($c['label'][0]) == 4 && substr_compare($c['label'], $this->filtervalue, -strlen($this->filtervalue), strlen($this->filtervalue), TRUE) == 0; default: return FALSE; } } } $database = new MARCDatabaseFlatFile(); $key = NULL; $resource = NULL; $reschanged = FALSE; while ($argi < count($argv)) { switch (strtoupper($argv[$argi++])) { case 'OPEN': if ($reschanged) echo "Warning: selected resource has not been updated.\n"; if ($database->IsChanged()) echo "Warning: database has unsaved changes.\n"; $database->Open($argv[$argi++]); $reschanged = FALSE; break; case 'OPENSQLITE': $database = new MARCDatabaseSQLite($argv[$argi++]); break; case 'OPENDBA': $database = new MARCDatabaseDBA($argv[$argi++]); break; case 'SAVE': $database->Save(); break; case 'SAVEAS': $database->SaveAs($argv[$argi]); break; case 'SYNC': $database->SyncHTTP($argv[$argi++]); break; case 'KEY': switch (strtoupper($argv[$argi++])) { case 'CREATE': $key = array('store' => TRUE); $key['pk'] = nacl_crypto_sign_ed25519_keypair($key['sk'], randombytes(32)); echo 'Created public key '.bin2hex($key['pk'])."\n"; break; case 'FORGET': $key['store'] = FALSE; break; case 'STORE': $key['store'] = TRUE; break; case 'USE': $key = array('store' => FALSE, 'pk' => $resource['key']); if (isset($resource['value']['seckey'])) $key['locked'] = $resource['value']['seckey']; break; case 'IMPORT': $key = array('store' => FALSE); $key['pk'] = nacl_crypto_sign_ed25519_keypair($key['sk'], hex2bin($argv[$argi++])); break; case 'UNLOCK': if (!isset($key['locked'])) throw new Exception('The key is not locked'); if (!is_array($key['locked']) || !isset($key['locked']['key'])) throw new Exception('The locked key is invalid'); $rounds = isset($key['locked']['rounds']) ? intval($key['locked']['rounds']) : 0; $ret = str_repeat(chr(0), 64); for ($i = 0; $i < $rounds; $i++) $ret = hash('sha512', $ret.$argv[$argi].$key['pk'], TRUE); $argi++; $ret = substr($key['locked'] ^ $ret, 0, 32); $ret = nacl_crypto_sign_ed25519_keypair($key['sk'], $ret); if ($ret != $key['pk']) throw new Exception('Key password is not valid'); echo 'Unlocked public key '.bin2hex($key['pk'])."\n"; break; default: throw new Exception('Unknown key operation '.$argv[$argi-1]); } break; case 'LIST': foreach ($database->GetResources() as $ret) echo labeltoname($ret['label'])."\n"; break; case 'FIND': foreach (filterresources($database->GetResources(), $argv, $argi) as $ret) echo labeltoname($ret['label'])."\n"; break; case 'DELETE': $database->DeleteResource($resource['label']); break; case 'DUMP': dumpresource($resource); break; case 'UPDATE': if (!isset($resource['key'])) $resource['key'] = $key['pk']; unset($resource['serial']); $res = $database->UpdateResource($resource, $key['sk']); if (!$res) throw new Exception('Could not update resource'); $resource = $res->ToArray(); $reschanged = FALSE; break; case 'SET': switch (strtoupper($argv[$argi++])) { case 'OWNER': if (!isset($resource['value']) || !is_array($resource['value'])) $resource['value'] = array(); $resource['value']['owner'] = $argv[$argi++]; $reschanged = TRUE; break; case 'DESC': case 'DESCR': case 'DESCRIPTION': if (!isset($resource['value']) || !is_array($resource['value'])) $resource['value'] = array(); $resource['value']['descr'] = $argv[$argi++]; $reschanged = TRUE; break; case 'PWAUTH': if (!isset($key['sk'])) throw new Exception('The key is not available'); $rounds = 5000; $ret = str_repeat(chr(0), 64); for ($i = 0; $i < $rounds; $i++) $ret = hash('sha512', $ret.$argv[$argi].$key['pk'], TRUE); $argi++; $key['locked'] = array('rounds' => $rounds, 'key' => substr($key['sk'] ^ $hash, 0, 32)); if (!isset($resource['value']) || !is_array($resource['value'])) $resource['value'] = array(); $resource['value']['seckeyenc'] = $key['locked']; $reschanged = TRUE; break; case 'TRANSFER': $ret = $argv[$argi++]; $resource['value']['transfer'] = (strtolower($ret) == 'any') ? '' : hex2bin($ret); $reschanged = TRUE; break; default: throw new Exception('Unknown set operation '.$argv[$argi-1]); } break; case 'ADD': switch (strtoupper($argv[$argi++])) { case 'NS': if (!isset($resource['value']) || !is_array($resource['value'])) $resource['value'] = array(); if (!isset($resource['value']['ns']) || !is_array($resource['value']['ns'])) $resource['value']['ns'] = array(); $nsname = $argv[$argi++]; $nsglue = (strlen($nsname) && $nsname[strlen($nsname)-1] != '.') ? $argv[$argi++] : NULL; if (!isset($resource['value']['ns'][$nsname]) || !is_array($resource['value']['ns'][$nsname])) $resource['value']['ns'][$nsname] = array(); if ($nsglue !== NULL) $resource['value']['ns'][$nsname][] = $nsglue; $reschanged = TRUE; break; default: throw new Exception('Unknown add operation '.$argv[$argi-1]); } break; case 'RESET': switch (strtoupper($argv[$argi++])) { case 'OWNER': if (!is_array($resource['value'])) $resource['value'] = array(); unset($resource['value']['owner']); $reschanged = TRUE; break; case 'DESC': case 'DESCR': case 'DESCRIPTION': if (!is_array($resource['value'])) $resource['value'] = array(); unset($resource['value']['descr']); $reschanged = TRUE; break; case 'PWAUTH': if (!is_array($resource['value'])) $resource['value'] = array(); unset($resource['value']['seckeyenc']); $reschanged = TRUE; break; case 'NS': if (!is_array($resource['value'])) $resource['value'] = array(); unset($resource['value']['ns']); $reschanged = TRUE; break; case 'VALUE': $resource['value'] = array(); $reschanged = TRUE; break; case 'TRANSFER': unset($resource['transfer']); $reschanged = TRUE; break; case 'EXPIRATION': unset($resource['expiration']); $reschanged = TRUE; break; default: throw new Exception('Unknown reset operation '.$argv[$argi-1]); } break; case 'CREATE': if ($reschanged) echo "Warning: selected resource has not been updated.\n"; $reschanged = TRUE; $resource = array('label' => argtolabel($argv, $argi)); break; case 'SELECT': if ($reschanged) echo "Warning: selected resource has not been updated.\n"; $reschanged = FALSE; if (strcasecmp($argv[$argi], 'TRANSFERCHAIN') == 0) { $resource = MARCUpdate::Decode($resource['transferchain']); if (!$resource) echo "Warning: failed to decode chained transfer data.\n"; $argi++; } else { $label = argtolabel($argv, $argi); $resource = $database->GetResource($label); if (!$resource) echo "Warning: resource ".labeltoname($label)." does not exist.\n"; } if ($resource) $resource = $resource->ToArray(); break; case 'HELP': print_help(); break; default: throw new Exception('Unknown operation '.$argv[$argi-1]); } } if ($reschanged) echo "Warning: selected resource has not been updated.\n"; if ($database->IsChanged()) echo "Warning: database has unsaved changes.\n"; $database->Close(); function filterresources($iterator, $argv, &$argi) { $filtertype = strtoupper($argv[$argi++]); switch ($filtertype) { case 'OWNER': case 'KEY': case 'DOMEXT': $filtervalue = $argv[$argi++]; break; default: throw new Exception('Unknown filter type '.$t); } return new FilteredResourceIterator($iterator, $filtertype, $filtervalue); } function argtolabel($argv, &$argi) { $t = $argv[$argi++]; switch (strtoupper($t)) { case 'LABEL': return hex2bin($argv[$argi++]); case 'CURRENTKEY': return chr(0).$GLOBALS['key']['pk']; case 'RESOURCEKEY': return chr(0).$GLOBALS['resource']['key']; case 'KEY': return chr(0).hex2bin($argv[$argi++]); case 'IP': case 'IP4': case 'IPV4': case 'IP6': case 'IPV6': return ipnettolabel($argv[$argi++]); case 'AS': return chr(3).marc_decode_int32be($argv[$argi++]); case 'DOM': case 'DOMAIN': return chr(4).strtolower(trim($argv[$argi++], '.')); default: if (preg_match('/^AS[0-9]{1-9}$/', $t)) return chr(3).marc_decode_int32be(substr($t, 2)); if (preg_match('_^[0-9]{1-3}\.[0-9]{1-3}\.[0-9]{1-3}\.[0-9]{1-3}/[0-9]{1-2}$_', $t)) return ipnettolabel($t); if (preg_match('_^(((?=.*(::))(?!.*\3.+\3))\3?|([\dA-F]{1,4}(\3|:\b|$)|\2))(?4){5}((?4){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})\z/[0-9]{1-3}_i', $t)) return ipnettolabel($t); if (preg_match('/^[a-f0-9]{64}$/i', $t)) return chr(0).hex2bin($t); if (preg_match('/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z0-9]{2,6}$/i', $t)) return chr(4).strtolower(trim($t, '.')); throw new Exception('Could not detect label type for '.$t); } } function ipnettolabel($s) { $ip = inet_pton(strtok($s, '/')); $pl = intval(strtok('/')); if ($pl == 0) throw new Exception('Invalid IP network specified'); if (strlen($ip) == 4) return chr(1).$ip.chr($pl); if (strlen($ip) == 16) return chr(2).$ip.chr($pl); } function labeltoname($l) { switch (ord($l)) { case 0: return 'KEY '.bin2hex(substr($l, 1)); case 1: if (strlen($l) == 6) return 'IPv4 '.inet_ntop(substr($l, 1, 4)).'/'.ord($l[5]); else return 'LABEL '.bin2hex($l); case 2: if (strlen($l) == 18) return 'IPv6 '.inet_ntop(substr($l, 1, 16)).'/'.ord($l[17]); else return 'LABEL '.bin2hex($l); case 3: if (strlen($l) == 5) return 'AS '.marc_decode_int32be(substr($l, 1, 4)); else return 'LABEL '.bin2hex($l); case 4: if (strlen($l) > 1) return 'DOM '.substr($l, 1); else return 'LABEL '.bin2hex($l); default: return 'LABEL '.bin2hex($l); } } function dumpresource($r, $p = '') { if (is_null($r)) { echo "NULL\n"; } else if (is_string($r)) { $bin = FALSE; for ($i = 0; $i < strlen($r); $i++) $bin |= ord($r[$i]) < 32 || ord($r[$i]) > 126; if ($bin) echo '0x'.bin2hex($r)."\n"; else echo '"'.$r.'"'."\n"; } else if (is_scalar($r)) { echo $r."\n"; } else if (is_array($r)) { echo "array(\n"; foreach ($r as $key => $value) { echo $p.' ['.$key.'] => '; dumpresource($value, $p.' '); } echo $p." )\n"; } else { print_r($r); } } function randombytes($n) { $b = ''; $file = fopen('/dev/urandom', 'r'); for ($i = 0; $i < $n; $i++) $b .= fgetc($file); fclose($file); return $b; } function print_help() { echo 'Usage: marcus.php [operation] [arguments] [operation] [arguments]... open [filename.mdb] - opens the specified database file save - saves the current database file saveas [filename.mdb] - saves the current data to a new databse file sync [url] - synchronize database with remote server key create - create a new key pair key forget - do not store the current key pair in the local database key store - store the current key pair in the local database key use - use the key pair from the currently selected resource key import [secretkey] - import the key pair defined by the given secret key key unlock [password] - unlock a password protected key pair list - list registered resources find [type] [value] - list registered sources matching filter (type=OWNER|KEY|DOMEXT) create [identifier] - create given resource create currentkey - create resource for current key pair create ip|ip4|ipv4 [ipv4network] - create resource for IPv4 network create ip|ip6|ipv6 [ipv6network] - create resource for IPv6 network create dom|domain [ipv6network] - create resource for domain name select [identifier] - select resource given by identifier select currentkey - select key resource for current key pair select resourcekey - select key resource for the key that signed the currently selected resource select transferchain - extract the chained transfer data from the currently selected resource select label [identifier] - select resource by hexadecimal label select key [publickey] - select key resource (hexadecimal) select ip|ip4|ipv4 [ipv4network] - select resource for IPv4 network select ip|ip6|ipv6 [ipv6network] - select resource for IPv6 network select dom|domain [ipv6network] - select resource for domain name delete - delete currently selected resource dump - display currently selected resource update - update selected resource in the local database set owner [name] - set the owner name for the selected resource set descr|desc|description [text] - set the description for the selected resource set pwauth [password] - store the current key pair in the selected resource for password authentication set transfer any - allow anyone to take over the resource set transfer [key] - transfer the resource to given key TODO: set expiration [value] add ns [name] [glue] - add in-zone nameserver with glue record (name is the part of the nameserver name before the domain name, glue is an IPv4 or IPv6 address) add ns [name]. - add an external nameserver reset owner - clear the owner reset descr|desc|description - clear the description reset pwauth - remove the key pair from the recourse, disabling password authentication reset ns - clear the nameserver records reset transfer - disable resource transfers reset expiration - disable explicit expiration reset value - clear the owner, description, password authentication and nameserver records Examples: OPEN marc.mdb KEY create CREATE currentkey SET owner "Your name" SET pwauth yourpassword UPDATE KEY forget CREATE yourdomain.ano SET owner "Your name" UPDATE SAVE OPEN marc.mdb SELECT yourdomain.ano SELECT resourcekey KEY use KEY unlock yourpassword SELECT yourdomain.ano ADD ns ns1 1.2.3.4 UPDATE SAVE OPEN marc.mdb SELECT yourdomain.ano SELECT resourcekey KEY use KEY unlock yourpassword CREATE 1.2.3.0/24 SET owner "Your name" ADD ns ns1.yourdomain.ano. UPDATE SAVE OPEN marc.mdb SYNC http://marc.ucis.ano/ SAVE '; }