chiark / gitweb /
test-example: all-privkeys: Define, and indirect through, a variable
[secnet.git] / pubkeys.fl.pl
1 #!/usr/bin/perl -w
2 # -*- C -*-
3 #
4 # secnet - pubkeys.fl.pl
5 #
6 # This file is part of secnet.
7 # See README for full list of copyright holders.
8 #
9 # secnet is free software; you can redistribute it and/or modify it
10 # under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3 of the License, or
12 # (at your option) any later version.
13
14 # secnet is distributed in the hope that it will be useful, but
15 # WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 # General Public License for more details.
18
19 # You should have received a copy of the GNU General Public License
20 # version 3 along with secnet; if not, see
21 # https://www.gnu.org/licenses/gpl.html.
22
23 # We process __DATA__ of this file first through the perl code,
24 # and then through flex.  We do it like this because directives
25 # with positional arguments are otherwise rather tedious to specify
26 # in flex.  Of course we could have used bison too but this seems
27 # better overall.
28
29 use strict;
30
31 our $do = '';
32 our $co = '';
33 our $kw;
34 our $kwid;
35 our @next_kw;
36 our $in_s;
37 our $data_off;
38
39 our %subst = (GRPIDSZ => 4, SERIALSZ => 4);
40
41 our $last_lno = -1;
42 sub lineno (;$$) {
43     my ($always, $delta) = @_;
44     my $o = '';
45     $delta //= 0;
46     if ($always || $. != $last_lno+1) {
47         $o .= sprintf "#line %d \"%s\"\n", $delta+$data_off+$., $0;
48     }
49     $last_lno = $.;
50     $o;
51 }
52
53 while (<DATA>) {
54         last if m/^\%\%\s*$/;
55         if (m/^!SUBSTCHECKS\s*$/) {
56                 foreach (keys %subst) {
57                         $do .= <<END
58 #if $_ != $subst{$_}
59 # error $_ value disagrees between pubkeys.fl.pl and C headers
60 #endif
61 END
62                 }
63                 next;
64         }
65         $do .= lineno();
66         $do .= $_;
67 }
68
69 sub inst ($) {
70         $do .= "%x $_[0]\n";
71         "<$_[0]>";
72 }
73
74 while (<DATA>) {
75     s#\{!2(\w+)\}# '{'.(2 * ($subst{$1}//die "$1 ?")).'}' #ge;
76     if (m/^!(KEYWORD|KWALIAS) ([-0-9a-z]+)(\s*\{.*\})?$/) {
77         my $kwt=$2;
78         if ($1 eq 'KEYWORD') {
79             die if $kw;
80             $kw = $kwt;
81         } else {
82             die if @next_kw;
83             die unless $kw;
84         }
85         my $xact = $3 // '';
86         $kwid = $kw; $kwid =~ y/-/_/;
87         $in_s = "HK_${kwid}";
88         $co .= "{L}$kwt { BEGIN($in_s); $xact }\n";
89         next;
90     }
91     if (m/^!ARG (\w+) (\S.*\S) \{\s*$/) {
92         die unless $kw;
93         die if @next_kw;
94         $co .= inst("$in_s")."{S} { BEGIN(D_${kwid}_$1); }\n";
95         $co .= inst("D_${kwid}_$1")."$2 {\n";
96         $in_s = "HA_${kwid}_$1";
97         $co .= "\tBEGIN($in_s);\n";
98         @next_kw = ($kw);
99         $co .= lineno(1,1);
100         next;
101     }
102     if (m/^!\}\s*$/) {
103         die unless @next_kw;
104         $co .= lineno(1,0);
105         $co .= "}\n";
106         $kw = shift @next_kw;
107         next;
108     }
109     if (m/^!FINAL \{\s*$/) {
110         die unless $kw;
111         die if @next_kw;
112         $co .= inst("FIN_$kwid")."\\n { BEGIN(0); c->loc.line++; }\n";
113         $co .= inst("$in_s")."{L}/\\n {\n";
114         $co .= "\tBEGIN(FIN_$kwid);\n";
115         $co .= lineno(1,1);
116         @next_kw = (undef);
117         next;
118     }
119     if (m/^!/) {
120         die;
121     }
122     $co .= $_;
123     if (m/^\%\%\s*$/) {
124         $co .= lineno(1,1);
125     }
126 }
127
128 print $do, "%%\n", $co or die $!;
129
130 BEGIN { $data_off = __LINE__ + 1; }
131 __DATA__
132
133 L       [ \t]*
134 S       [ \t]+
135 BASE91S []-~!#-&(-[]+
136 %x SKIPNL
137 %x SYNTAXERR
138
139 %option yylineno
140 %option noyywrap
141 %option batch
142 %option 8bit
143 %option nodefault
144 %option never-interactive
145
146 %option prefix="pkyy"
147
148 %option warn
149
150 %{
151
152 #include "secnet.h"
153 #include "pubkeys.h"
154 #include "util.h"
155 #include "unaligned.h"
156 #include "base91s/base91.h"
157
158 !SUBSTCHECKS
159
160 struct pubkeyset_context {
161     /* filled in during setup: */
162     struct cloc loc; /* line is runtime */
163     struct log_if *log;
164     struct hash_if *defhash;
165     struct buffer_if *data_buf;
166     struct peer_keyset *building;
167     /* runtime: */
168     bool_t had_serial;
169     bool_t fallback_skip;
170     const struct sigscheme_info *scheme;
171     uint8_t grpid[GRPIDSZ];
172     serialt serial;
173 };
174
175 static struct pubkeyset_context c[1];
176
177 #define LI (c->log)
178 #define HEX2BIN(v,l) ({                                                 \
179         int32_t outlen;                                                 \
180         bool_t ok=hex_decode((v), ((l)), &outlen, yytext, False);       \
181         assert(ok);                                                     \
182         assert(outlen==((l)));                                          \
183     })
184 #define HEX2BIN_ARRAY(v) HEX2BIN((v),sizeof((v)))
185 #define DOSKIPQ ({                                      \
186         BEGIN(SKIPNL);                                  \
187         break;                                          \
188     })
189 #define DOSKIP(m) ({                                    \
190         slilog(LI,M_INFO,"%s:%d: " m, c->loc.file, c->loc.line);        \
191         DOSKIPQ;                                        \
192     })
193 #define FAIL(m) do{                                     \
194         slilog(LI,M_ERR,"%s:%d: " m, c->loc.file, c->loc.line); \
195         return -1;                                      \
196     }while(0)
197
198 %}
199
200 %%
201
202 !KEYWORD pkg  { c->fallback_skip=0; }
203 !KWALIAS pkgf { c->fallback_skip=!!c->building->nkeys; }
204 !ARG id [0-9a-f]{!2GRPIDSZ} {
205     HEX2BIN_ARRAY(c->grpid);
206 !}
207 !FINAL {
208 !}
209 !KEYWORD pub
210 !ARG algo [-0-9a-z]+ {
211     if (c->fallback_skip) DOSKIP("fallback not needed");
212     c->scheme = sigscheme_lookup(yytext);
213     if (!c->scheme) DOSKIP("unknown pk algorithm");
214 !}
215 !ARG data {BASE91S} {
216     /* baseE91 and thus base91s can sometimes store 14 bits per
217      * character pair, so the max decode ratio is 14/16. */
218     size_t maxl = base91s_decode_maxlen(yyleng);
219     buffer_init(c->data_buf,0);
220     if (buf_remaining_space(c->data_buf) < maxl) DOSKIP("pk data too long");
221     struct base91s b91;
222     base91s_init(&b91);
223     size_t l = base91s_decode(&b91, yytext, yyleng, c->data_buf->start);
224     l += base91s_decode_end(&b91, c->data_buf->start + l);
225     assert(l <= maxl);
226     buf_append(c->data_buf,l);
227 !}
228 !FINAL {
229     if (c->building->nkeys >= MAX_SIG_KEYS) DOSKIP("too many public keys");
230     struct sigpubkey_if *pubkey;
231     closure_t *cl;
232     bool_t ok=c->scheme->loadpub(c->scheme,c->data_buf,
233                                  &pubkey,&cl,c->log,c->loc);
234     if (!ok) break;
235     if (pubkey->sethash) {
236         if (!c->defhash) {
237             pubkey->dispose(pubkey->st);
238             DOSKIP("public key requires default hash to load");
239         }
240         pubkey->sethash(pubkey->st,c->defhash);
241     }
242     struct peer_pubkey *fill=&c->building->keys[c->building->nkeys];
243     memcpy(fill->id.b,c->grpid,GRPIDSZ);
244     assert(ALGIDSZ==1); /* otherwise need htons or htonl or something */
245     fill->id.b[GRPIDSZ]=c->scheme->algid;
246     fill->pubkey=pubkey;
247     c->building->nkeys++;
248 !}
249
250 !KEYWORD serial
251 !ARG id [0-9a-f]{!2SERIALSZ} {
252     if (c->had_serial) FAIL("`serial' repeated");
253     c->had_serial = 1;
254     uint8_t sb[SERIALSZ];
255     HEX2BIN_ARRAY(sb);
256     c->serial=get_uint32(sb);
257 !}
258 !FINAL {
259 !}
260
261 {L}[-0-9a-z]+ {
262     DOSKIP("unknown directive");
263 }
264
265 {L}\# {
266     BEGIN(SKIPNL);
267 }
268 {L}\n {
269     c->loc.line++;
270 }
271
272 <SKIPNL>.*\n {
273     c->loc.line++;
274     BEGIN(0);
275 }
276
277 <INITIAL><<EOF>>        { return 0; }
278
279 <*>. {
280     yymore();
281     BEGIN(SYNTAXERR);
282 }
283 <SYNTAXERR>.* {
284     slilog(LI,M_DEBUG,"pubkeys syntax error at `%s'", yytext);
285     FAIL("syntax error");
286 }
287 <*>\n { FAIL("syntax error - unexpected newline"); }
288 <<EOF>> { FAIL("syntax error - unexpected eof"); }
289
290 %%
291
292 extern struct peer_keyset *
293 keyset_load(const char *path, struct buffer_if *data_buf,
294             struct log_if *log, int logcl_enoent,
295             struct hash_if *defhash) {
296     assert(!c->building);
297     c->log=log;
298     c->defhash=defhash;
299     c->loc.file=path;
300     pkyyin = fopen(path, "r");
301     if (!pkyyin) {
302         slilog(LI,
303                errno==ENOENT ? logcl_enoent : M_ERR,
304                "%scould not open keyset file %s: %s",
305                logcl_enoent==M_DEBUG && errno==ENOENT ? "expectedly " : "",
306                path,strerror(errno));
307         goto err;
308     }
309
310     pkyyrestart(pkyyin);
311     BEGIN(0);
312     c->data_buf=data_buf;
313     NEW(c->building);
314     c->building->nkeys=0;
315     c->building->refcount=1;
316     c->fallback_skip=0;
317     c->had_serial=0;
318     c->loc.line=1;
319     FILLZERO(c->grpid);
320     FILLZERO(c->serial);
321     int r=pkyylex();
322     if (r) goto err_bad;
323
324     if (!c->building->nkeys) {
325         slilog(LI,M_ERR,"no useable keys in %s",path);
326         goto err_bad;
327     }
328     fclose(pkyyin);
329     struct peer_keyset *built=c->building;
330     c->building=0;
331     return built;
332
333  err_bad:
334     errno=EBADMSG;
335  err:
336     if (c->building) { free(c->building); c->building=0; }
337     if (pkyyin) { fclose(pkyyin); pkyyin=0; }
338     return 0;
339 }
340