1*0957b409SSimon J. Gerraty /*
2*0957b409SSimon J. Gerraty * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
3*0957b409SSimon J. Gerraty *
4*0957b409SSimon J. Gerraty * Permission is hereby granted, free of charge, to any person obtaining
5*0957b409SSimon J. Gerraty * a copy of this software and associated documentation files (the
6*0957b409SSimon J. Gerraty * "Software"), to deal in the Software without restriction, including
7*0957b409SSimon J. Gerraty * without limitation the rights to use, copy, modify, merge, publish,
8*0957b409SSimon J. Gerraty * distribute, sublicense, and/or sell copies of the Software, and to
9*0957b409SSimon J. Gerraty * permit persons to whom the Software is furnished to do so, subject to
10*0957b409SSimon J. Gerraty * the following conditions:
11*0957b409SSimon J. Gerraty *
12*0957b409SSimon J. Gerraty * The above copyright notice and this permission notice shall be
13*0957b409SSimon J. Gerraty * included in all copies or substantial portions of the Software.
14*0957b409SSimon J. Gerraty *
15*0957b409SSimon J. Gerraty * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16*0957b409SSimon J. Gerraty * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17*0957b409SSimon J. Gerraty * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18*0957b409SSimon J. Gerraty * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19*0957b409SSimon J. Gerraty * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20*0957b409SSimon J. Gerraty * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21*0957b409SSimon J. Gerraty * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22*0957b409SSimon J. Gerraty * SOFTWARE.
23*0957b409SSimon J. Gerraty */
24*0957b409SSimon J. Gerraty
25*0957b409SSimon J. Gerraty #include <stdio.h>
26*0957b409SSimon J. Gerraty #include <stdlib.h>
27*0957b409SSimon J. Gerraty #include <string.h>
28*0957b409SSimon J. Gerraty #include <stdint.h>
29*0957b409SSimon J. Gerraty #include <errno.h>
30*0957b409SSimon J. Gerraty
31*0957b409SSimon J. Gerraty #include "brssl.h"
32*0957b409SSimon J. Gerraty #include "bearssl.h"
33*0957b409SSimon J. Gerraty
34*0957b409SSimon J. Gerraty static unsigned
rsa_bit_length(const br_rsa_public_key * pk)35*0957b409SSimon J. Gerraty rsa_bit_length(const br_rsa_public_key *pk)
36*0957b409SSimon J. Gerraty {
37*0957b409SSimon J. Gerraty size_t u;
38*0957b409SSimon J. Gerraty unsigned x, bl;
39*0957b409SSimon J. Gerraty
40*0957b409SSimon J. Gerraty for (u = 0; u < pk->nlen; u ++) {
41*0957b409SSimon J. Gerraty if (pk->n[u] != 0) {
42*0957b409SSimon J. Gerraty break;
43*0957b409SSimon J. Gerraty }
44*0957b409SSimon J. Gerraty }
45*0957b409SSimon J. Gerraty if (u == pk->nlen) {
46*0957b409SSimon J. Gerraty return 0;
47*0957b409SSimon J. Gerraty }
48*0957b409SSimon J. Gerraty bl = (unsigned)(pk->nlen - u - 1) << 3;
49*0957b409SSimon J. Gerraty x = pk->n[u];
50*0957b409SSimon J. Gerraty while (x != 0) {
51*0957b409SSimon J. Gerraty bl ++;
52*0957b409SSimon J. Gerraty x >>= 1;
53*0957b409SSimon J. Gerraty }
54*0957b409SSimon J. Gerraty return bl;
55*0957b409SSimon J. Gerraty }
56*0957b409SSimon J. Gerraty
57*0957b409SSimon J. Gerraty static void
print_rsa(const br_rsa_public_key * pk,int print_text,int print_C)58*0957b409SSimon J. Gerraty print_rsa(const br_rsa_public_key *pk, int print_text, int print_C)
59*0957b409SSimon J. Gerraty {
60*0957b409SSimon J. Gerraty if (print_text) {
61*0957b409SSimon J. Gerraty size_t u;
62*0957b409SSimon J. Gerraty
63*0957b409SSimon J. Gerraty printf("n = ");
64*0957b409SSimon J. Gerraty for (u = 0; u < pk->nlen; u ++) {
65*0957b409SSimon J. Gerraty printf("%02X", pk->n[u]);
66*0957b409SSimon J. Gerraty }
67*0957b409SSimon J. Gerraty printf("\n");
68*0957b409SSimon J. Gerraty printf("e = ");
69*0957b409SSimon J. Gerraty for (u = 0; u < pk->elen; u ++) {
70*0957b409SSimon J. Gerraty printf("%02X", pk->e[u]);
71*0957b409SSimon J. Gerraty }
72*0957b409SSimon J. Gerraty printf("\n");
73*0957b409SSimon J. Gerraty }
74*0957b409SSimon J. Gerraty if (print_C) {
75*0957b409SSimon J. Gerraty size_t u;
76*0957b409SSimon J. Gerraty
77*0957b409SSimon J. Gerraty printf("\nstatic const unsigned char RSA_N[] = {");
78*0957b409SSimon J. Gerraty for (u = 0; u < pk->nlen; u ++) {
79*0957b409SSimon J. Gerraty if (u != 0) {
80*0957b409SSimon J. Gerraty printf(",");
81*0957b409SSimon J. Gerraty }
82*0957b409SSimon J. Gerraty if (u % 12 == 0) {
83*0957b409SSimon J. Gerraty printf("\n\t");
84*0957b409SSimon J. Gerraty } else {
85*0957b409SSimon J. Gerraty printf(" ");
86*0957b409SSimon J. Gerraty }
87*0957b409SSimon J. Gerraty printf("0x%02X", pk->n[u]);
88*0957b409SSimon J. Gerraty }
89*0957b409SSimon J. Gerraty printf("\n};\n");
90*0957b409SSimon J. Gerraty printf("\nstatic const unsigned char RSA_E[] = {");
91*0957b409SSimon J. Gerraty for (u = 0; u < pk->elen; u ++) {
92*0957b409SSimon J. Gerraty if (u != 0) {
93*0957b409SSimon J. Gerraty printf(",");
94*0957b409SSimon J. Gerraty }
95*0957b409SSimon J. Gerraty if (u % 12 == 0) {
96*0957b409SSimon J. Gerraty printf("\n\t");
97*0957b409SSimon J. Gerraty } else {
98*0957b409SSimon J. Gerraty printf(" ");
99*0957b409SSimon J. Gerraty }
100*0957b409SSimon J. Gerraty printf("0x%02X", pk->e[u]);
101*0957b409SSimon J. Gerraty }
102*0957b409SSimon J. Gerraty printf("\n};\n");
103*0957b409SSimon J. Gerraty printf("\nstatic const br_rsa_public_key RSA = {\n");
104*0957b409SSimon J. Gerraty printf("\t(unsigned char *)RSA_N, sizeof RSA_N,\n");
105*0957b409SSimon J. Gerraty printf("\t(unsigned char *)RSA_E, sizeof RSA_E\n");
106*0957b409SSimon J. Gerraty printf("};\n");
107*0957b409SSimon J. Gerraty }
108*0957b409SSimon J. Gerraty }
109*0957b409SSimon J. Gerraty
110*0957b409SSimon J. Gerraty static void
print_ec(const br_ec_public_key * pk,int print_text,int print_C)111*0957b409SSimon J. Gerraty print_ec(const br_ec_public_key *pk, int print_text, int print_C)
112*0957b409SSimon J. Gerraty {
113*0957b409SSimon J. Gerraty if (print_text) {
114*0957b409SSimon J. Gerraty size_t u;
115*0957b409SSimon J. Gerraty
116*0957b409SSimon J. Gerraty printf("Q = ");
117*0957b409SSimon J. Gerraty for (u = 0; u < pk->qlen; u ++) {
118*0957b409SSimon J. Gerraty printf("%02X", pk->q[u]);
119*0957b409SSimon J. Gerraty }
120*0957b409SSimon J. Gerraty printf("\n");
121*0957b409SSimon J. Gerraty }
122*0957b409SSimon J. Gerraty if (print_C) {
123*0957b409SSimon J. Gerraty size_t u;
124*0957b409SSimon J. Gerraty
125*0957b409SSimon J. Gerraty printf("\nstatic const unsigned char EC_Q[] = {");
126*0957b409SSimon J. Gerraty for (u = 0; u < pk->qlen; u ++) {
127*0957b409SSimon J. Gerraty if (u != 0) {
128*0957b409SSimon J. Gerraty printf(",");
129*0957b409SSimon J. Gerraty }
130*0957b409SSimon J. Gerraty if (u % 12 == 0) {
131*0957b409SSimon J. Gerraty printf("\n\t");
132*0957b409SSimon J. Gerraty } else {
133*0957b409SSimon J. Gerraty printf(" ");
134*0957b409SSimon J. Gerraty }
135*0957b409SSimon J. Gerraty printf("0x%02X", pk->q[u]);
136*0957b409SSimon J. Gerraty }
137*0957b409SSimon J. Gerraty printf("\n};\n");
138*0957b409SSimon J. Gerraty printf("\nstatic const br_ec_public_key EC = {\n");
139*0957b409SSimon J. Gerraty printf("\t%d,\n", pk->curve);
140*0957b409SSimon J. Gerraty printf("\t(unsigned char *)EC_Q, sizeof EC_Q\n");
141*0957b409SSimon J. Gerraty printf("};\n");
142*0957b409SSimon J. Gerraty }
143*0957b409SSimon J. Gerraty }
144*0957b409SSimon J. Gerraty
145*0957b409SSimon J. Gerraty static void
usage_verify(void)146*0957b409SSimon J. Gerraty usage_verify(void)
147*0957b409SSimon J. Gerraty {
148*0957b409SSimon J. Gerraty fprintf(stderr,
149*0957b409SSimon J. Gerraty "usage: brssl verify [ options ] file...\n");
150*0957b409SSimon J. Gerraty fprintf(stderr,
151*0957b409SSimon J. Gerraty "options:\n");
152*0957b409SSimon J. Gerraty fprintf(stderr,
153*0957b409SSimon J. Gerraty " -q suppress verbose messages\n");
154*0957b409SSimon J. Gerraty fprintf(stderr,
155*0957b409SSimon J. Gerraty " -sni name check presence of a specific server name\n");
156*0957b409SSimon J. Gerraty fprintf(stderr,
157*0957b409SSimon J. Gerraty " -CA file add certificates in 'file' to trust anchors\n");
158*0957b409SSimon J. Gerraty fprintf(stderr,
159*0957b409SSimon J. Gerraty " -text print public key details (human-readable)\n");
160*0957b409SSimon J. Gerraty fprintf(stderr,
161*0957b409SSimon J. Gerraty " -C print public key details (C code)\n");
162*0957b409SSimon J. Gerraty }
163*0957b409SSimon J. Gerraty
164*0957b409SSimon J. Gerraty typedef VECTOR(br_x509_certificate) cert_list;
165*0957b409SSimon J. Gerraty
166*0957b409SSimon J. Gerraty static void
free_cert_contents(br_x509_certificate * xc)167*0957b409SSimon J. Gerraty free_cert_contents(br_x509_certificate *xc)
168*0957b409SSimon J. Gerraty {
169*0957b409SSimon J. Gerraty xfree(xc->data);
170*0957b409SSimon J. Gerraty }
171*0957b409SSimon J. Gerraty
172*0957b409SSimon J. Gerraty /* see brssl.h */
173*0957b409SSimon J. Gerraty int
do_verify(int argc,char * argv[])174*0957b409SSimon J. Gerraty do_verify(int argc, char *argv[])
175*0957b409SSimon J. Gerraty {
176*0957b409SSimon J. Gerraty int retcode;
177*0957b409SSimon J. Gerraty int verbose;
178*0957b409SSimon J. Gerraty int i;
179*0957b409SSimon J. Gerraty const char *sni;
180*0957b409SSimon J. Gerraty anchor_list anchors = VEC_INIT;
181*0957b409SSimon J. Gerraty cert_list chain = VEC_INIT;
182*0957b409SSimon J. Gerraty size_t u;
183*0957b409SSimon J. Gerraty br_x509_minimal_context mc;
184*0957b409SSimon J. Gerraty int err;
185*0957b409SSimon J. Gerraty int print_text, print_C;
186*0957b409SSimon J. Gerraty br_x509_pkey *pk;
187*0957b409SSimon J. Gerraty const br_x509_pkey *tpk;
188*0957b409SSimon J. Gerraty unsigned usages;
189*0957b409SSimon J. Gerraty
190*0957b409SSimon J. Gerraty retcode = 0;
191*0957b409SSimon J. Gerraty verbose = 1;
192*0957b409SSimon J. Gerraty sni = NULL;
193*0957b409SSimon J. Gerraty print_text = 0;
194*0957b409SSimon J. Gerraty print_C = 0;
195*0957b409SSimon J. Gerraty pk = NULL;
196*0957b409SSimon J. Gerraty for (i = 0; i < argc; i ++) {
197*0957b409SSimon J. Gerraty const char *arg;
198*0957b409SSimon J. Gerraty
199*0957b409SSimon J. Gerraty arg = argv[i];
200*0957b409SSimon J. Gerraty if (arg[0] != '-') {
201*0957b409SSimon J. Gerraty br_x509_certificate *xcs;
202*0957b409SSimon J. Gerraty size_t num;
203*0957b409SSimon J. Gerraty
204*0957b409SSimon J. Gerraty xcs = read_certificates(arg, &num);
205*0957b409SSimon J. Gerraty if (xcs == NULL) {
206*0957b409SSimon J. Gerraty usage_verify();
207*0957b409SSimon J. Gerraty goto verify_exit_error;
208*0957b409SSimon J. Gerraty }
209*0957b409SSimon J. Gerraty VEC_ADDMANY(chain, xcs, num);
210*0957b409SSimon J. Gerraty xfree(xcs);
211*0957b409SSimon J. Gerraty continue;
212*0957b409SSimon J. Gerraty }
213*0957b409SSimon J. Gerraty if (eqstr(arg, "-v") || eqstr(arg, "-verbose")) {
214*0957b409SSimon J. Gerraty verbose = 1;
215*0957b409SSimon J. Gerraty } else if (eqstr(arg, "-q") || eqstr(arg, "-quiet")) {
216*0957b409SSimon J. Gerraty verbose = 0;
217*0957b409SSimon J. Gerraty } else if (eqstr(arg, "-sni")) {
218*0957b409SSimon J. Gerraty if (++ i >= argc) {
219*0957b409SSimon J. Gerraty fprintf(stderr,
220*0957b409SSimon J. Gerraty "ERROR: no argument for '-sni'\n");
221*0957b409SSimon J. Gerraty usage_verify();
222*0957b409SSimon J. Gerraty goto verify_exit_error;
223*0957b409SSimon J. Gerraty }
224*0957b409SSimon J. Gerraty if (sni != NULL) {
225*0957b409SSimon J. Gerraty fprintf(stderr, "ERROR: duplicate SNI\n");
226*0957b409SSimon J. Gerraty usage_verify();
227*0957b409SSimon J. Gerraty goto verify_exit_error;
228*0957b409SSimon J. Gerraty }
229*0957b409SSimon J. Gerraty sni = argv[i];
230*0957b409SSimon J. Gerraty continue;
231*0957b409SSimon J. Gerraty } else if (eqstr(arg, "-CA")) {
232*0957b409SSimon J. Gerraty if (++ i >= argc) {
233*0957b409SSimon J. Gerraty fprintf(stderr,
234*0957b409SSimon J. Gerraty "ERROR: no argument for '-CA'\n");
235*0957b409SSimon J. Gerraty usage_verify();
236*0957b409SSimon J. Gerraty goto verify_exit_error;
237*0957b409SSimon J. Gerraty }
238*0957b409SSimon J. Gerraty arg = argv[i];
239*0957b409SSimon J. Gerraty if (read_trust_anchors(&anchors, arg) == 0) {
240*0957b409SSimon J. Gerraty usage_verify();
241*0957b409SSimon J. Gerraty goto verify_exit_error;
242*0957b409SSimon J. Gerraty }
243*0957b409SSimon J. Gerraty continue;
244*0957b409SSimon J. Gerraty } else if (eqstr(arg, "-text")) {
245*0957b409SSimon J. Gerraty print_text = 1;
246*0957b409SSimon J. Gerraty } else if (eqstr(arg, "-C")) {
247*0957b409SSimon J. Gerraty print_C = 1;
248*0957b409SSimon J. Gerraty } else {
249*0957b409SSimon J. Gerraty fprintf(stderr, "ERROR: unknown option: '%s'\n", arg);
250*0957b409SSimon J. Gerraty usage_verify();
251*0957b409SSimon J. Gerraty goto verify_exit_error;
252*0957b409SSimon J. Gerraty }
253*0957b409SSimon J. Gerraty }
254*0957b409SSimon J. Gerraty if (VEC_LEN(chain) == 0) {
255*0957b409SSimon J. Gerraty fprintf(stderr, "ERROR: no certificate chain provided\n");
256*0957b409SSimon J. Gerraty usage_verify();
257*0957b409SSimon J. Gerraty goto verify_exit_error;
258*0957b409SSimon J. Gerraty }
259*0957b409SSimon J. Gerraty br_x509_minimal_init(&mc, &br_sha256_vtable,
260*0957b409SSimon J. Gerraty &VEC_ELT(anchors, 0), VEC_LEN(anchors));
261*0957b409SSimon J. Gerraty br_x509_minimal_set_hash(&mc, br_sha1_ID, &br_sha1_vtable);
262*0957b409SSimon J. Gerraty br_x509_minimal_set_hash(&mc, br_sha224_ID, &br_sha224_vtable);
263*0957b409SSimon J. Gerraty br_x509_minimal_set_hash(&mc, br_sha256_ID, &br_sha256_vtable);
264*0957b409SSimon J. Gerraty br_x509_minimal_set_hash(&mc, br_sha384_ID, &br_sha384_vtable);
265*0957b409SSimon J. Gerraty br_x509_minimal_set_hash(&mc, br_sha512_ID, &br_sha512_vtable);
266*0957b409SSimon J. Gerraty br_x509_minimal_set_rsa(&mc, &br_rsa_i31_pkcs1_vrfy);
267*0957b409SSimon J. Gerraty br_x509_minimal_set_ecdsa(&mc,
268*0957b409SSimon J. Gerraty &br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1);
269*0957b409SSimon J. Gerraty
270*0957b409SSimon J. Gerraty mc.vtable->start_chain(&mc.vtable, sni);
271*0957b409SSimon J. Gerraty for (u = 0; u < VEC_LEN(chain); u ++) {
272*0957b409SSimon J. Gerraty br_x509_certificate *xc;
273*0957b409SSimon J. Gerraty
274*0957b409SSimon J. Gerraty xc = &VEC_ELT(chain, u);
275*0957b409SSimon J. Gerraty mc.vtable->start_cert(&mc.vtable, xc->data_len);
276*0957b409SSimon J. Gerraty mc.vtable->append(&mc.vtable, xc->data, xc->data_len);
277*0957b409SSimon J. Gerraty mc.vtable->end_cert(&mc.vtable);
278*0957b409SSimon J. Gerraty }
279*0957b409SSimon J. Gerraty err = mc.vtable->end_chain(&mc.vtable);
280*0957b409SSimon J. Gerraty tpk = mc.vtable->get_pkey(&mc.vtable, &usages);
281*0957b409SSimon J. Gerraty if (tpk != NULL) {
282*0957b409SSimon J. Gerraty pk = xpkeydup(tpk);
283*0957b409SSimon J. Gerraty }
284*0957b409SSimon J. Gerraty
285*0957b409SSimon J. Gerraty if (err == 0) {
286*0957b409SSimon J. Gerraty if (verbose) {
287*0957b409SSimon J. Gerraty int hkx;
288*0957b409SSimon J. Gerraty
289*0957b409SSimon J. Gerraty fprintf(stderr, "Validation success; usages:");
290*0957b409SSimon J. Gerraty hkx = 0;
291*0957b409SSimon J. Gerraty if (usages & BR_KEYTYPE_KEYX) {
292*0957b409SSimon J. Gerraty fprintf(stderr, " key exchange");
293*0957b409SSimon J. Gerraty hkx = 1;
294*0957b409SSimon J. Gerraty }
295*0957b409SSimon J. Gerraty if (usages & BR_KEYTYPE_SIGN) {
296*0957b409SSimon J. Gerraty if (hkx) {
297*0957b409SSimon J. Gerraty fprintf(stderr, ",");
298*0957b409SSimon J. Gerraty }
299*0957b409SSimon J. Gerraty fprintf(stderr, " signature");
300*0957b409SSimon J. Gerraty }
301*0957b409SSimon J. Gerraty fprintf(stderr, "\n");
302*0957b409SSimon J. Gerraty }
303*0957b409SSimon J. Gerraty } else {
304*0957b409SSimon J. Gerraty if (verbose) {
305*0957b409SSimon J. Gerraty const char *errname, *errmsg;
306*0957b409SSimon J. Gerraty
307*0957b409SSimon J. Gerraty fprintf(stderr, "Validation failed, err = %d", err);
308*0957b409SSimon J. Gerraty errname = find_error_name(err, &errmsg);
309*0957b409SSimon J. Gerraty if (errname != NULL) {
310*0957b409SSimon J. Gerraty fprintf(stderr, " (%s): %s\n", errname, errmsg);
311*0957b409SSimon J. Gerraty } else {
312*0957b409SSimon J. Gerraty fprintf(stderr, " (unknown)\n");
313*0957b409SSimon J. Gerraty }
314*0957b409SSimon J. Gerraty }
315*0957b409SSimon J. Gerraty retcode = -1;
316*0957b409SSimon J. Gerraty }
317*0957b409SSimon J. Gerraty if (pk != NULL) {
318*0957b409SSimon J. Gerraty switch (pk->key_type) {
319*0957b409SSimon J. Gerraty case BR_KEYTYPE_RSA:
320*0957b409SSimon J. Gerraty if (verbose) {
321*0957b409SSimon J. Gerraty fprintf(stderr, "Key type: RSA (%u bits)\n",
322*0957b409SSimon J. Gerraty rsa_bit_length(&pk->key.rsa));
323*0957b409SSimon J. Gerraty }
324*0957b409SSimon J. Gerraty print_rsa(&pk->key.rsa, print_text, print_C);
325*0957b409SSimon J. Gerraty break;
326*0957b409SSimon J. Gerraty case BR_KEYTYPE_EC:
327*0957b409SSimon J. Gerraty if (verbose) {
328*0957b409SSimon J. Gerraty fprintf(stderr, "Key type: EC (%s)\n",
329*0957b409SSimon J. Gerraty ec_curve_name(pk->key.ec.curve));
330*0957b409SSimon J. Gerraty }
331*0957b409SSimon J. Gerraty print_ec(&pk->key.ec, print_text, print_C);
332*0957b409SSimon J. Gerraty break;
333*0957b409SSimon J. Gerraty default:
334*0957b409SSimon J. Gerraty if (verbose) {
335*0957b409SSimon J. Gerraty fprintf(stderr, "Unknown key type\n");
336*0957b409SSimon J. Gerraty break;
337*0957b409SSimon J. Gerraty }
338*0957b409SSimon J. Gerraty }
339*0957b409SSimon J. Gerraty }
340*0957b409SSimon J. Gerraty
341*0957b409SSimon J. Gerraty /*
342*0957b409SSimon J. Gerraty * Release allocated structures.
343*0957b409SSimon J. Gerraty */
344*0957b409SSimon J. Gerraty verify_exit:
345*0957b409SSimon J. Gerraty VEC_CLEAREXT(anchors, &free_ta_contents);
346*0957b409SSimon J. Gerraty VEC_CLEAREXT(chain, &free_cert_contents);
347*0957b409SSimon J. Gerraty xfreepkey(pk);
348*0957b409SSimon J. Gerraty return retcode;
349*0957b409SSimon J. Gerraty
350*0957b409SSimon J. Gerraty verify_exit_error:
351*0957b409SSimon J. Gerraty retcode = -1;
352*0957b409SSimon J. Gerraty goto verify_exit;
353*0957b409SSimon J. Gerraty }
354