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