comparison marcus.php @ 0:3ac7bd7495fd draft

Initial commit
author Ivo Smits <Ivo@UCIS.nl>
date Sat, 08 Nov 2014 22:22:42 +0100
parents
children caa68b502313
comparison
equal deleted inserted replaced
-1:000000000000 0:3ac7bd7495fd
1 <?php
2 require_once './marccore.php';
3
4 error_reporting(E_ALL);
5
6 if (!isset($argv)) $argv = $_SERVER['argv'];
7 $argi = 1;
8
9 class FilteredResourceIterator implements Iterator {
10 private $source, $filtertype, $filtervalue;
11 public function __construct($source, $filtertype, $filtervalue) {
12 $this->source = $source;
13 $this->filtertype = $filtertype;
14 $this->filtervalue = $filtervalue;
15 }
16 public function current() { $r = $this->source->current(); return $r; }
17 public function key() { return $this->source->key(); }
18 public function valid() { return $this->source->valid(); }
19 public function next() { $this->source->next(); $this->findnext(); }
20 public function rewind() { $this->source->rewind(); $this->findnext(); }
21 private function findnext() {
22 while ($this->source->valid()) {
23 $c = $this->source->current();
24 if ($this->filter($c)) break;
25 $this->source->next();
26 }
27 }
28 private function filter($c) {
29 switch ($this->filtertype) {
30 case 'OWNER': return isset($c['value']['owner']) && is_scalar($c['value']['owner']) && strcasecmp($c['value']['owner'], $this->filtervalue) == 0;
31 case 'KEY': return $c['key'] == hex2bin($this->filtervalue);
32 case 'DOMEXT': return ord($c['label'][0]) == 4 && substr_compare($c['label'], $this->filtervalue, -strlen($this->filtervalue), strlen($this->filtervalue), TRUE) == 0;
33 default: return FALSE;
34 }
35 }
36 }
37
38 $database = new MARCDatabaseFlatFile();
39 $key = NULL;
40 $resource = NULL;
41 $reschanged = FALSE;
42
43 while ($argi < count($argv)) {
44 switch (strtoupper($argv[$argi++])) {
45 case 'OPEN':
46 if ($reschanged) echo "Warning: selected resource has not been updated.\n";
47 if ($database->IsChanged()) echo "Warning: database has unsaved changes.\n";
48 $database->Open($argv[$argi++]);
49 $reschanged = FALSE;
50 break;
51 case 'OPENSQLITE':
52 $database = new MARCDatabaseSQLite($argv[$argi++]);
53 break;
54 case 'OPENDBA':
55 $database = new MARCDatabaseDBA($argv[$argi++]);
56 break;
57 case 'SAVE':
58 $database->Save();
59 break;
60 case 'SAVEAS':
61 $database->SaveAs($argv[$argi]);
62 break;
63 case 'SYNC':
64 $database->SyncHTTP($argv[$argi++]);
65 break;
66 case 'KEY':
67 switch (strtoupper($argv[$argi++])) {
68 case 'CREATE':
69 $key = array('store' => TRUE);
70 $key['pk'] = nacl_crypto_sign_ed25519_keypair($key['sk'], randombytes(32));
71 $dbchanged = TRUE;
72 echo 'Created public key '.bin2hex($key['pk'])."\n";
73 break;
74 case 'FORGET':
75 $key['store'] = FALSE;
76 $dbchanged = TRUE;
77 break;
78 case 'STORE':
79 $key['store'] = TRUE;
80 $dbchanged = TRUE;
81 break;
82 case 'USE':
83 $key = array('store' => FALSE, 'pk' => $resource['key']);
84 if (isset($resource['value']['seckey'])) $key['sk'] = $resource['value']['seckey'];
85 if (isset($resource['value']['seckeyenc'])) $key['locked'] = $resource['value']['seckeyenc'];
86 break;
87 case 'IMPORT':
88 $key = array('store' => FALSE);
89 $key['pk'] = nacl_crypto_sign_ed25519_keypair($key['sk'], hex2bin($argv[$argi++]));
90 $dbchanged = TRUE;
91 break;
92 case 'UNLOCK':
93 if (!isset($key['locked'])) throw new Exception('The key is not locked');
94 $ret = hash('sha512', $key['pk'].$argv[$argi++], TRUE);
95 $key['sk'] = '';
96 for ($i = 0; $i < 32; $i++) $key['sk'] .= chr(ord($key['locked'][$i]) ^ ord($ret[$i]));
97 $ret = nacl_crypto_sign_ed25519_keypair($key['sk'], $key['sk']);
98 if ($ret != $key['pk']) throw new Exception('Key password is not valid');
99 echo 'Unlocked public key '.bin2hex($key['pk'])."\n";
100 break;
101 default:
102 throw new Exception('Unknown key operation '.$argv[$argi-1]);
103 }
104 break;
105 case 'LIST':
106 foreach ($database->GetResources() as $ret) echo labeltoname($ret['label'])."\n";
107 break;
108 case 'FIND':
109 foreach (filterresources($database->GetResources(), $argv, $argi) as $ret) echo labeltoname($ret['label'])."\n";
110 break;
111 case 'DELETE':
112 $database->DeleteResource($resource['label']);
113 break;
114 case 'DUMP':
115 dumpresource($resource);
116 break;
117 case 'UPDATE':
118 if (!isset($resource['key'])) $resource['key'] = $key['pk'];
119 unset($resource['serial']);
120 $res = $database->UpdateResource($resource, $key['sk']);
121 if (!$res) throw new Exception('Could not update resource');
122 $resource = $res->ToArray();
123 $reschanged = FALSE;
124 break;
125 case 'SET':
126 switch (strtoupper($argv[$argi++])) {
127 case 'OWNER':
128 if (!isset($resource['value']) || !is_array($resource['value'])) $resource['value'] = array();
129 $resource['value']['owner'] = $argv[$argi++];
130 $reschanged = TRUE;
131 break;
132 case 'DESC':
133 case 'DESCR':
134 case 'DESCRIPTION':
135 if (!isset($resource['value']) || !is_array($resource['value'])) $resource['value'] = array();
136 $resource['value']['descr'] = $argv[$argi++];
137 $reschanged = TRUE;
138 break;
139 case 'PWAUTH':
140 if (!isset($key['sk'])) throw new Exception('The key is not available');
141 $ret = hash('sha512', $key['pk'].$argv[$argi++], TRUE);
142 $key['locked'] = '';
143 for ($i = 0; $i < 32; $i++) $key['locked'] .= chr(ord($key['sk'][$i]) ^ ord($ret[$i]));
144 if (!isset($resource['value']) || !is_array($resource['value'])) $resource['value'] = array();
145 $resource['value']['seckeyenc'] = $key['locked'];
146 $reschanged = TRUE;
147 break;
148 case 'TRANSFER':
149 $ret = $argv[$argi++];
150 $resource['value']['transfer'] = (strtolower($ret) == 'any') ? '' : hex2bin($ret);
151 $reschanged = TRUE;
152 break;
153 default:
154 throw new Exception('Unknown set operation '.$argv[$argi-1]);
155 }
156 break;
157 case 'ADD':
158 switch (strtoupper($argv[$argi++])) {
159 case 'NS':
160 if (!isset($resource['value']) || !is_array($resource['value'])) $resource['value'] = array();
161 if (!isset($resource['value']['ns']) || !is_array($resource['value']['ns'])) $resource['value']['ns'] = array();
162 $nsname = $argv[$argi++];
163 $nsglue = (strlen($nsname) && $nsname[strlen($nsname)-1] != '.') ? $argv[$argi++] : NULL;
164 if (!isset($resource['value']['ns'][$nsname]) || !is_array($resource['value']['ns'][$nsname])) $resource['value']['ns'][$nsname] = array();
165 if ($nsglue !== NULL) $resource['value']['ns'][$nsname][] = $nsglue;
166 $reschanged = TRUE;
167 break;
168 default:
169 throw new Exception('Unknown add operation '.$argv[$argi-1]);
170 }
171 break;
172 case 'RESET':
173 switch (strtoupper($argv[$argi++])) {
174 case 'OWNER':
175 if (!is_array($resource['value'])) $resource['value'] = array();
176 unset($resource['value']['owner']);
177 $reschanged = TRUE;
178 break;
179 case 'DESC':
180 case 'DESCR':
181 case 'DESCRIPTION':
182 if (!is_array($resource['value'])) $resource['value'] = array();
183 unset($resource['value']['descr']);
184 $reschanged = TRUE;
185 break;
186 case 'PWAUTH':
187 if (!is_array($resource['value'])) $resource['value'] = array();
188 unset($resource['value']['seckeyenc']);
189 $reschanged = TRUE;
190 break;
191 case 'NS':
192 if (!is_array($resource['value'])) $resource['value'] = array();
193 unset($resource['value']['ns']);
194 $reschanged = TRUE;
195 break;
196 case 'VALUE':
197 $resource['value'] = array();
198 $reschanged = TRUE;
199 break;
200 case 'TRANSFER':
201 unset($resource['transfer']);
202 $reschanged = TRUE;
203 break;
204 case 'EXPIRATION':
205 unset($resource['expiration']);
206 $reschanged = TRUE;
207 break;
208 default:
209 throw new Exception('Unknown reset operation '.$argv[$argi-1]);
210 }
211 break;
212 case 'CREATE':
213 if ($reschanged) echo "Warning: selected resource has not been updated.\n";
214 $reschanged = TRUE;
215 $resource = array('label' => argtolabel($argv, $argi));
216 break;
217 case 'SELECT':
218 if ($reschanged) echo "Warning: selected resource has not been updated.\n";
219 $reschanged = FALSE;
220 $label = argtolabel($argv, $argi);
221 $resource = $database->GetResource($label);
222 if (!$resource) echo "Warning: resource ".labeltoname($label)." does not exist.\n";
223 else $resource = $resource->ToArray();
224 break;
225 case 'HELP':
226 print_help();
227 break;
228 default:
229 throw new Exception('Unknown operation '.$argv[$argi-1]);
230 }
231 }
232 if ($reschanged) echo "Warning: selected resource has not been updated.\n";
233 if ($database->IsChanged()) echo "Warning: database has unsaved changes.\n";
234 $database->Close();
235
236 function filterresources($iterator, $argv, &$argi) {
237 $filtertype = strtoupper($argv[$argi++]);
238 switch ($filtertype) {
239 case 'OWNER':
240 case 'KEY':
241 case 'DOMEXT':
242 $filtervalue = $argv[$argi++];
243 break;
244 default:
245 throw new Exception('Unknown filter type '.$t);
246 }
247 return new FilteredResourceIterator($iterator, $filtertype, $filtervalue);
248 }
249 function argtolabel($argv, &$argi) {
250 $t = $argv[$argi++];
251 switch (strtoupper($t)) {
252 case 'LABEL': return hex2bin($argv[$argi++]);
253 case 'CURRENTKEY': return chr(0).$GLOBALS['key']['pk'];
254 case 'RESOURCEKEY': return chr(0).$GLOBALS['resource']['key'];
255 case 'KEY': return chr(0).hex2bin($argv[$argi++]);
256 case 'IP':
257 case 'IP4':
258 case 'IPV4':
259 case 'IP6':
260 case 'IPV6': return ipnettolabel($argv[$argi++]);
261 case 'AS': return chr(3).marc_decode_int32be($argv[$argi++]);
262 case 'DOM':
263 case 'DOMAIN': return chr(4).strtolower(trim($argv[$argi++], '.'));
264 default:
265 if (preg_match('/^AS[0-9]{1-9}$/', $t)) return chr(3).marc_decode_int32be(substr($argv[$argi++], 2));
266 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 ipv4tolabel($t);
267 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 ipv6tolabel($t);
268 if (preg_match('/^[a-f0-9]{64}$/i', $t)) return chr(0).hex2bin($t);
269 if (preg_match('/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$/i', $t)) return chr(4).strtolower(trim($t, '.'));
270 throw new Exception('Could not detect label type for '.$t);
271 }
272 }
273 function ipnettolabel($s) {
274 $ip = inet_pton(strtok($s, '/'));
275 $pl = intval(strtok('/'));
276 if ($pl == 0) throw new Exception('Invalid IP network specified');
277 if (strlen($ip) == 4) return chr(1).$ip.chr($pl);
278 if (strlen($ip) == 16) return chr(2).$ip.chr($pl);
279 }
280 function labeltoname($l) {
281 switch (ord($l)) {
282 case 0: return 'KEY '.bin2hex(substr($l, 1));
283 case 1: if (strlen($l) == 6) return 'IPv4 '.inet_ntop(substr($l, 1, 4)).'/'.ord($l[5]); else return 'LABEL '.bin2hex($l);
284 case 2: if (strlen($l) == 18) return 'IPv6 '.inet_ntop(substr($l, 1, 16)).'/'.ord($l[17]); else return 'LABEL '.bin2hex($l);
285 case 3: if (strlen($l) == 5) return 'AS '.marc_decode_int32be(substr($l, 1, 4)); else return 'LABEL '.bin2hex($l);
286 case 4: if (strlen($l) > 1) return 'DOM '.substr($l, 1); else return 'LABEL '.bin2hex($l);
287 default: return 'LABEL '.bin2hex($l);
288 }
289 }
290 function dumpresource($r, $p = '') {
291 if (is_null($r)) {
292 echo "NULL\n";
293 } else if (is_string($r)) {
294 $bin = FALSE;
295 for ($i = 0; $i < strlen($r); $i++) $bin |= ord($r[$i]) < 32 || ord($r[$i]) > 126;
296 if ($bin) echo '0x'.bin2hex($r)."\n";
297 else echo '"'.$r.'"'."\n";
298 } else if (is_scalar($r)) {
299 echo $r."\n";
300 } else if (is_array($r)) {
301 echo "array(\n";
302 foreach ($r as $key => $value) {
303 echo $p.' ['.$key.'] => ';
304 dumpresource($value, $p.' ');
305 }
306 echo $p." )\n";
307 } else {
308 print_r($r);
309 }
310 }
311 function randombytes($n) {
312 $b = '';
313 $file = fopen('/dev/urandom', 'r');
314 for ($i = 0; $i < $n; $i++) $b .= fgetc($file);
315 fclose($file);
316 return $b;
317 }
318
319 function print_help() {
320 echo 'Usage: marcus.php [operation] [arguments] [operation] [arguments]...
321 open [filename.mdb] - opens the specified database file
322 save - saves the current database file
323 saveas [filename.mdb] - saves the current data to a new databse file
324 sync [url] - synchronize database with remote server
325 key create - create a new key pair
326 key forget - do not store the current key pair in the local database
327 key store - store the current key pair in the local database
328 key use - use the key pair from the currently selected resource
329 key import [secretkey] - import the key pair defined by the given secret key
330 key unlock [password] - unlock a password protected key pair
331 list - list registered resources
332 find [type] [value] - list registered sources matching filter (type=OWNER|KEY|DOMEXT)
333 create [identifier] - create given resource
334 create currentkey - create resource for current key pair
335 create ip|ip4|ipv4 [ipv4network] - create resource for IPv4 network
336 create ip|ip6|ipv6 [ipv6network] - create resource for IPv6 network
337 create dom|domain [ipv6network] - create resource for domain name
338 select [identifier] - select resource given by identifier
339 select currentkey - select key resource for current key pair
340 select resourcekey - select key resource for the key that signed the currently selected resource
341 select label [identifier] - select resource by hexadecimal label
342 select key [publickey] - select key resource (hexadecimal)
343 select ip|ip4|ipv4 [ipv4network] - select resource for IPv4 network
344 select ip|ip6|ipv6 [ipv6network] - select resource for IPv6 network
345 select dom|domain [ipv6network] - select resource for domain name
346 delete - delete currently selected resource
347 dump - display currently selected resource
348 update - update selected resource in the local database
349 set owner [name] - set the owner name for the selected resource
350 set descr|desc|description [text] - set the description for the selected resource
351 set pwauth [password] - store the current key pair in the selected resource for password authentication
352 set transfer any - allow anyone to take over the resource
353 set transfer [key] - transfer the resource to given key
354 TODO: set expiration [value]
355 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)
356 add ns [name]. - add an external nameserver
357 reset owner - clear the owner
358 reset descr|desc|description - clear the description
359 reset pwauth - remove the key pair from the recourse, disabling password authentication
360 reset ns - clear the nameserver records
361 reset transfer - disable resource transfers
362 reset expiration - disable explicit expiration
363 reset value - clear the owner, description, password authentication and nameserver records
364
365 Examples:
366 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
367 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
368 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
369 OPEN marc.mdb SYNC http://marc.ucis.ano/ SAVE
370 ';
371 }