chiark / gitweb /
pubkeys: Provide ability to add extra action to KEYWORD
[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 ([-0-9a-z]+)(\s*\{.*\})?$/) {
77         my $kwt=$2;
78         die if $kw;
79         $kw = $1;
80         my $xact = $3 // '';
81         $kwid = $kw; $kwid =~ y/-/_/;
82         $in_s = "HK_${kwid}";
83         $co .= "{L}$kwt { BEGIN($in_s); $xact }\n";
84         next;
85     }
86     if (m/^!ARG (\w+) (\S.*\S) \{\s*$/) {
87         die unless $kw;
88         die if @next_kw;
89         $co .= inst("$in_s")."{S} { BEGIN(D_${kwid}_$1); }\n";
90         $co .= inst("D_${kwid}_$1")."$2 {\n";
91         $in_s = "HA_${kwid}_$1";
92         $co .= "\tBEGIN($in_s);\n";
93         @next_kw = ($kw);
94         $co .= lineno(1,1);
95         next;
96     }
97     if (m/^!\}\s*$/) {
98         die unless @next_kw;
99         $co .= lineno(1,0);
100         $co .= "}\n";
101         $kw = shift @next_kw;
102         next;
103     }
104     if (m/^!FINAL \{\s*$/) {
105         die unless $kw;
106         die if @next_kw;
107         $co .= inst("FIN_$kwid")."\\n { BEGIN(0); c->lno++; }\n";
108         $co .= inst("$in_s")."{L}/\\n {\n";
109         $co .= "\tBEGIN(FIN_$kwid);\n";
110         $co .= lineno(1,1);
111         @next_kw = (undef);
112         next;
113     }
114     if (m/^!/) {
115         die;
116     }
117     $co .= $_;
118     if (m/^\%\%\s*$/) {
119         $co .= lineno(1,1);
120     }
121 }
122
123 print $do, "%%\n", $co or die $!;
124
125 BEGIN { $data_off = __LINE__ + 1; }
126 __DATA__
127
128 L       [ \t]*
129 S       [ \t]+
130 BASE91S []-~!#-&(-[]+
131 %x SKIPNL
132
133 %option yylineno
134 %option noyywrap
135 %option batch
136 %option 8bit
137 %option nodefault
138 %option never-interactive
139
140 %option prefix="pkyy"
141
142 %option warn
143
144 %{
145
146 #include "secnet.h"
147 #include "pubkeys.h"
148 #include "util.h"
149 #include "unaligned.h"
150 #include "base91s/base91.h"
151
152 !SUBSTCHECKS
153
154 struct pubkeyset_context {
155     /* filled in during setup: */
156     struct log_if *log;
157     struct buffer_if *data_buf;
158     struct peer_keyset *building;
159     /* runtime: */
160     bool_t had_serial;
161     int lno;
162     const struct sigscheme_info *scheme;
163     uint8_t grpid[GRPIDSZ];
164     serialt serial;
165 };
166
167 static struct pubkeyset_context c[1];
168
169 #define LI (c->log)
170 #define HEX2BIN(v,l) ({                                                 \
171         int32_t outlen;                                                 \
172         bool_t ok=hex_decode((v), ((l)), &outlen, yytext, False);       \
173         assert(ok);                                                     \
174         assert(outlen==((l)));                                          \
175     })
176 #define HEX2BIN_ARRAY(v) HEX2BIN((v),sizeof((v)))
177 #define DOSKIPQ ({                                      \
178         BEGIN(SKIPNL);                                  \
179         break;                                          \
180     })
181 #define DOSKIP(m) ({                                    \
182         slilog(LI,M_INFO,"l.%d: " m, c->lno);   \
183         DOSKIPQ;                                        \
184     })
185 #define FAIL(m) do{                                     \
186         slilog(LI,M_ERR,"l.%d: " m, c->lno);    \
187         return -1;                                      \
188     }while(0)
189
190 %}
191
192 %%
193
194 !KEYWORD pkg
195 !ARG id [0-9a-f]{!2GRPIDSZ} {
196     HEX2BIN_ARRAY(c->grpid);
197 !}
198 !FINAL {
199 !}
200 !KEYWORD pub
201 !ARG algo [-0-9a-z]+ {
202     c->scheme = sigscheme_lookup(yytext);
203     if (!c->scheme) DOSKIP("unknown pk algorithm");
204 !}
205 !ARG data {BASE91S} {
206     /* baseE91 and thus base91s can sometimes store 14 bits per
207      * character pair, so the max decode ratio is 14/16. */
208     size_t maxl = base91s_decode_maxlen(yyleng);
209     buffer_init(c->data_buf,0);
210     if (buf_remaining_space(c->data_buf) < maxl) DOSKIP("pk data too long");
211     struct base91s b91;
212     base91s_init(&b91);
213     size_t l = base91s_decode(&b91, yytext, yyleng, c->data_buf->start);
214     l += base91s_decode_end(&b91, c->data_buf->start + l);
215     assert(l <= maxl);
216     buf_append(c->data_buf,l);
217 !}
218 !FINAL {
219     if (c->building->nkeys >= MAX_SIG_KEYS) DOSKIP("too many public keys");
220     struct sigpubkey_if *pubkey;
221     bool_t ok=c->scheme->loadpub(c->scheme,c->data_buf,
222                                  &pubkey,c->log);
223     if (!ok) break;
224     memcpy(c->building->keys[c->building->nkeys].id.b,
225            c->grpid,
226            GRPIDSZ);
227     assert(ALGIDSZ==1); /* otherwise need htons or htonl or something */
228     c->building->keys[c->building->nkeys].id.b[GRPIDSZ]=
229       c->scheme->algid;
230     c->building->keys[c->building->nkeys++].pubkey=pubkey;
231 !}
232
233 !KEYWORD serial
234 !ARG id [0-9a-f]{!2SERIALSZ} {
235     if (c->had_serial) FAIL("`serial' repeated");
236     c->had_serial = 1;
237     uint8_t sb[SERIALSZ];
238     HEX2BIN_ARRAY(sb);
239     c->serial=get_uint32(sb);
240 !}
241 !FINAL {
242 !}
243
244 {L}[-0-9a-z]+ {
245     DOSKIP("unknown directive");
246 }
247
248 {L}\# {
249     BEGIN(SKIPNL);
250 }
251 {L}\n {
252     c->lno++;
253 }
254
255 <SKIPNL>.*\n {
256     c->lno++;
257     BEGIN(0);
258 }
259
260 <INITIAL><<EOF>>        { return 0; }
261
262 <*>. { FAIL("syntax error"); }
263 <*>\n { FAIL("syntax error - unexpected newline"); }
264 <<EOF>> { FAIL("syntax error - unexpected eof"); }
265
266 %%
267
268 extern struct peer_keyset *
269 keyset_load(const char *path, struct buffer_if *data_buf,
270             struct log_if *log, int logcl_enoent) {
271     assert(!c->building);
272     c->log=log;
273     pkyyin = fopen(path, "r");
274     if (!pkyyin) {
275         slilog(LI,
276                errno==ENOENT ? logcl_enoent : M_ERR,
277                "could not open keyset file %s: %s",
278                path,strerror(errno));
279         goto err;
280     }
281
282     pkyyrestart(pkyyin);
283     BEGIN(0);
284     c->data_buf=data_buf;
285     NEW(c->building);
286     c->building->nkeys=0;
287     c->building->refcount=1;
288     c->had_serial=0;
289     c->lno=1;
290     FILLZERO(c->grpid);
291     FILLZERO(c->serial);
292     int r=pkyylex();
293     if (r) goto err_bad;
294
295     if (!c->had_serial) {
296         slilog(LI,M_ERR,"missing serial number in %s",path);
297         goto err_bad;
298     }
299     if (!c->building->nkeys) {
300         slilog(LI,M_ERR,"no useable keys in %s",path);
301         goto err_bad;
302     }
303     fclose(pkyyin);
304     struct peer_keyset *built=c->building;
305     c->building=0;
306     return built;
307
308  err_bad:
309     errno=EBADMSG;
310  err:
311     if (c->building) { free(c->building); c->building=0; }
312     if (pkyyin) { fclose(pkyyin); pkyyin=0; }
313     return 0;
314 }
315