]> cygwin.com Git - cygwin-apps/setup.git/blame - crypto.cc
Added dpiAwareness element to manifest
[cygwin-apps/setup.git] / crypto.cc
CommitLineData
dbfe3c19
DK
1/*
2 * Copyright (c) 2008, Dave Korn.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
11 *
12 * Written by Dave Korn <dave.korn.cygwin@gmail.com>
13 *
14 */
15
dbfe3c19
DK
16#include <stdio.h>
17#include <stdlib.h>
18#include <unistd.h>
19#include <vector>
20#include "io_stream.h"
21#include "crypto.h"
22#include "compress.h"
23#include "gcrypt.h"
24#include "msg.h"
6f2a7375 25#include "LogSingleton.h"
dbfe3c19 26#include "resource.h"
7c05cfce 27#include "getopt++/StringArrayOption.h"
dbfe3c19
DK
28#include "getopt++/BoolOption.h"
29#include "KeysSetting.h"
30#include "gpg-packet.h"
31#include "geturl.h"
32
8eb0b0eb 33#ifndef CRYPTODEBUGGING
dbfe3c19 34#define CRYPTODEBUGGING (0)
8eb0b0eb 35#endif
dbfe3c19 36
8eb0b0eb 37#define ERRKIND note
dbfe3c19 38#if CRYPTODEBUGGING
6f2a7375 39#define MESSAGE LogBabblePrintf
8eb0b0eb 40#else
6f2a7375 41#define MESSAGE while (0) LogBabblePrintf
8eb0b0eb 42#endif
dbfe3c19
DK
43
44/* Command-line options for specifying and controlling extra keys. */
20f237b4
JT
45static StringArrayOption ExtraKeyOption ('K', "pubkey", IDS_HELPTEXT_PUBKEY);
46static StringArrayOption SexprExtraKeyOption ('S', "sexpr-pubkey", IDS_HELPTEXT_SEXPR_PUBKEY);
47static BoolOption UntrustedKeysOption (false, 'u', "untrusted-keys", IDS_HELPTEXT_UNTRUSTED_KEYS);
48static BoolOption KeepUntrustedKeysOption (false, 'U', "keep-untrusted-keys", IDS_HELPTEXT_KEEP_UNTRUSTED_KEYS);
49static BoolOption EnableOldKeysOption (false, '\0', "old-keys", IDS_HELPTEXT_OLD_KEYS,
2fd5251b 50 BoolOption::BoolOptionType::pairedAble);
dbfe3c19 51
28d401b5 52/* Embedded public half of Cygwin signing key. */
9096650d 53static const char *cygwin_pubkey_sexpr =
dbfe3c19
DK
54#include "cyg-pubkey.h"
55;
56
9096650d
JT
57static const char *cygwin_old_pubkey_sexpr =
58#include "cyg-old-pubkey.h"
59;
60
dbfe3c19
DK
61/* S-expr template for DSA pubkey. */
62static const char *dsa_pubkey_templ = "(public-key (dsa (p %m) (q %m) (g %m) (y %m)))";
63
03c0b4ad
JT
64/* S-expr template for RSA pubkey. */
65static const char *rsa_pubkey_templ = "(public-key (rsa (n %m) (e %m)))";
66
dbfe3c19
DK
67/* S-expr template for DSA signature. */
68static const char *dsa_sig_templ = "(sig-val (dsa (r %m) (s %m)))";
69
28d401b5
JT
70/* S-expr template for RSA signature. */
71static const char *rsa_sig_templ = "(sig-val (rsa (s %m)))";
72
73/* S-expr template for DSA data block to be signed. */
37b70a87 74static const char *dsa_data_hash_templ = "(data (flags raw) (hash %s %b))";
28d401b5
JT
75
76/* S-expr template for RSA data block to be signed. */
77static const char *rsa_data_hash_templ = "(data (flags pkcs1) (hash %s %b))";
dbfe3c19 78
5f4250b8
JT
79/* Information on a key to try */
80struct key_info
81{
82 key_info(std::string _name, bool _builtin, gcry_sexp_t _key, bool _owned=true) :
83 name(_name), builtin(_builtin), key(_key), owned(_owned)
84 {
85 }
86
87 std::string name;
88 bool builtin; // if true, we don't need to retain this key with add_key_from_sexpr()
89 gcry_sexp_t key;
90 bool owned; // if true, we own this key and should use gcry_sexp_release() on it
91};
92
dbfe3c19
DK
93/* User context data for sig packet walk. */
94struct sig_data
95{
96 /* MPI values of sig components. */
97 gcry_mpi_t dsa_mpi_r, dsa_mpi_s;
28d401b5 98 gcry_mpi_t rsa_mpi_s;
dbfe3c19
DK
99
100 /* Hash context. */
101 gcry_md_hd_t md;
102
103 /* Main data. */
104 io_stream *sign_data;
105
106 /* Auxiliary data. */
107 int sig_type;
108 int pk_alg;
109 int hash_alg;
110
111 /* Converted algo code. */
112 int algo;
113
5f4250b8
JT
114 /* Keys */
115 std::vector<struct key_info> *keys_to_try;
116
dbfe3c19 117 /* Final status. */
5f4250b8 118 bool valid;
dbfe3c19
DK
119};
120
121/* User context data for key packet walk. */
122struct key_data
123{
124 std::vector<gcry_sexp_t> keys;
125};
126
127/* Callback hook for walking packets in gpg key file. Extracts
03c0b4ad 128 the key coefficients from any public key packets encountered and
dbfe3c19
DK
129 converts them into s-expr pubkey format, returning the public
130 keys thus found to the caller in a vector in the userdata context. */
131static enum
132pkt_cb_resp key_file_walker (struct packet_walker *wlk, unsigned char tag,
03c0b4ad 133 size_t packetsize, size_t hdrpos)
dbfe3c19
DK
134{
135 struct key_data *kdat = (struct key_data *)(wlk->userdata);
136
137 MESSAGE ("key packet %d size %d at offs $%04x kdat $%08x\n", tag,
03c0b4ad 138 packetsize, hdrpos, kdat);
dbfe3c19
DK
139
140 if (tag != RFC4880_PT_PUBLIC_KEY)
141 return pktCONTINUE;
142
143 // So, get the data out. Version is first. In case of any errors during
144 // parsing, we just discard the key and continue, hoping to find a good one.
145 char ver = pkt_getch (wlk->pfile);
146 if (ver != 4)
147 {
148 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, ver, "unsupported key version.");
149 return pktCONTINUE;
150 }
151
152 // Only V4 accepted. Discard creation time.
153 if (pkt_getdword (wlk->pfile) == -1)
154 {
155 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, -1, "missing creation time.");
156 return pktCONTINUE;
157 }
158
159 char pkalg = pkt_getch (wlk->pfile);
03c0b4ad 160 if ((pkalg != RFC4880_PK_DSA) && (pkalg != RFC4880_PK_RSA))
dbfe3c19
DK
161 {
162 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, pkalg, "unsupported key alg.");
163 return pktCONTINUE;
164 }
165
03c0b4ad
JT
166 // Next, the key coefficient MPIs should be present. Read them out, convert
167 // to an s-expr and add that to the list of keys.
168 size_t erroff;
169 gcry_sexp_t new_key;
dbfe3c19 170
03c0b4ad 171 if (pkalg == RFC4880_PK_DSA)
dbfe3c19 172 {
03c0b4ad
JT
173 gcry_mpi_t p, q, g, y;
174 p = q = g = y = 0;
175
176 if ((pkt_get_mpi (&p, wlk->pfile) >= 0)
177 && (pkt_get_mpi (&q, wlk->pfile) >= 0)
178 && (pkt_get_mpi (&g, wlk->pfile) >= 0)
179 && (pkt_get_mpi (&y, wlk->pfile) >= 0))
180 {
181 gcry_error_t rv = gcry_sexp_build (&new_key, &erroff, dsa_pubkey_templ, p, q, g, y);
182 if (rv != GPG_ERR_NO_ERROR)
183 {
184 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
185 return pktCONTINUE;
186 }
187 }
188
189 // Release temps and continue.
190 if (p)
191 gcry_mpi_release (p);
192 if (q)
193 gcry_mpi_release (q);
194 if (g)
195 gcry_mpi_release (g);
196 if (y)
197 gcry_mpi_release (y);
198 }
199 else if (pkalg == RFC4880_PK_RSA)
200 {
201 gcry_mpi_t n, e;
202 n = e = 0;
203
204 if ((pkt_get_mpi (&n, wlk->pfile) >= 0)
205 && (pkt_get_mpi (&e, wlk->pfile) >= 0))
206 {
207 gcry_error_t rv = gcry_sexp_build (&new_key, &erroff, rsa_pubkey_templ, n, e);
208 if (rv != GPG_ERR_NO_ERROR)
209 {
210 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
211 return pktCONTINUE;
212 }
213 }
214
215 if (n)
216 gcry_mpi_release (n);
217 if (e)
218 gcry_mpi_release (e);
219 }
dbfe3c19
DK
220
221#if CRYPTODEBUGGING
03c0b4ad
JT
222 // Debugging
223 char sexprbuf[GPG_KEY_SEXPR_BUF_SIZE];
285a4617
JT
224 erroff = gcry_sexp_sprint (new_key, GCRYSEXP_FMT_ADVANCED, sexprbuf,
225 GPG_KEY_SEXPR_BUF_SIZE);
226 LogBabblePrintf ("key:%d\n'%s'", erroff, sexprbuf);
dbfe3c19
DK
227#endif /* CRYPTODEBUGGING */
228
03c0b4ad
JT
229 // Return it to caller in the vector.
230 kdat->keys.push_back (new_key);
dbfe3c19
DK
231
232 return pktCONTINUE;
233}
234
235/* Does what its name suggests: feeds a chosen amount of the data found
236 at the current seek position in an io_stream into the message digest
237 context passed in, using reasonably-sized chunks for efficiency. */
238static size_t
239shovel_stream_data_into_md (io_stream *stream, size_t nbytes, gcry_md_hd_t md)
240{
241 const size_t TMPBUFSZ = 1024;
242 unsigned char tmpbuf[TMPBUFSZ];
243 size_t this_time, total = 0;
244 ssize_t actual;
245 MESSAGE ("shovel %d bytes at pos $%08x\n", nbytes, stream->tell ());
246 while (nbytes)
247 {
248 this_time = (nbytes > TMPBUFSZ) ? TMPBUFSZ : nbytes;
249 actual = stream->read (tmpbuf, this_time);
250 if (actual <= 0)
251 break;
252 gcry_md_write (md, tmpbuf, actual);
253 total += actual;
254 nbytes -= actual;
255 if (actual != (ssize_t)this_time)
256 break;
257 }
258 return total;
259}
260
261/* Canonicalise an s-expr by converting LFs to spaces so that
262 it's all on one line and folding multiple spaces as we go. */
263static size_t
264fold_lfs_and_spaces (char *buf, size_t n)
265{
266 char *ptr1 = buf, *ptr2 = buf;
267
268 while (n--)
269 {
270 char ch = *ptr1++;
271 if (ch == 0x0a)
272 ch = ' ';
273 *ptr2++ = ch;
274 if (ch == 0x20)
275 while (n && ((*ptr1 == ' ') || (*ptr1 == 0x0a)))
276 {
277 --n;
278 ++ptr1;
279 }
280 }
281 return ptr2 - buf;
282}
283
5f4250b8
JT
284/* Size and allocate a temp buffer to print a representation
285 of a public key s-expr into, then add that to the extra keys
286 setting so it persists for the next run. */
287static void
288add_key_from_sexpr (gcry_sexp_t key)
289{
290 size_t n = gcry_sexp_sprint (key, GCRYSEXP_FMT_ADVANCED, 0, ~0);
291 char *sexprbuf = new char[n];
292 n = gcry_sexp_sprint (key, GCRYSEXP_FMT_ADVANCED, sexprbuf, n);
293 // +1 because we want to include the nul-terminator.
294 n = fold_lfs_and_spaces (sexprbuf, n + 1);
295 ExtraKeysSetting::instance().add_key (sexprbuf);
296 MESSAGE ("keep:%d\n'%s'", n, sexprbuf);
297 delete [] sexprbuf;
298}
299
300static bool
301verify_sig(struct sig_data *sigdat, HWND owner)
302{
303 gcry_error_t rv;
304 size_t n;
305 {
306 /* sig coefficients in s-expr format. */
307 gcry_sexp_t sig;
308
309 /* signature hash data in s-expr format. */
310 gcry_sexp_t hash;
311
312 /* Build everything into s-exprs, and call the libgcrypt verification
313 routine. */
314
315 if (sigdat->pk_alg == RFC4880_PK_DSA)
316 {
317 rv = gcry_sexp_build (&sig, &n, dsa_sig_templ, sigdat->dsa_mpi_r,
318 sigdat->dsa_mpi_s);
319 if (rv != GPG_ERR_NO_ERROR)
320 {
321 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
322 return false;
323 }
324
325 rv = gcry_sexp_build (&hash, &n, dsa_data_hash_templ,
326 gcry_md_algo_name(sigdat->algo),
327 gcry_md_get_algo_dlen (sigdat->algo),
328 gcry_md_read (sigdat->md, 0));
329 if (rv != GPG_ERR_NO_ERROR)
330 {
331 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating hash s-expr.");
332 return false;
333 }
334 }
335 else if (sigdat->pk_alg == RFC4880_PK_RSA)
336 {
337 rv = gcry_sexp_build (&sig, &n, rsa_sig_templ, sigdat->rsa_mpi_s);
338 if (rv != GPG_ERR_NO_ERROR)
339 {
340 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating sig s-expr.");
341 return false;
342 }
343
344 rv = gcry_sexp_build (&hash, &n, rsa_data_hash_templ,
345 gcry_md_algo_name(sigdat->algo),
346 gcry_md_get_algo_dlen (sigdat->algo),
347 gcry_md_read (sigdat->md, 0));
348 if (rv != GPG_ERR_NO_ERROR)
349 {
350 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating hash s-expr.");
351 return false;
352 }
353 }
354
355#if CRYPTODEBUGGING
285a4617 356 char sexprbuf[GPG_KEY_SEXPR_BUF_SIZE];
5f4250b8
JT
357 n = gcry_sexp_sprint (sig, GCRYSEXP_FMT_ADVANCED, sexprbuf,
358 GPG_KEY_SEXPR_BUF_SIZE);
359 LogBabblePrintf ("sig:%d\n'%s'", n, sexprbuf);
360 n = gcry_sexp_sprint (hash, GCRYSEXP_FMT_ADVANCED, sexprbuf,
361 GPG_KEY_SEXPR_BUF_SIZE);
362 LogBabblePrintf ("hash:%d\n'%s'", n, sexprbuf);
363#endif /* CRYPTODEBUGGING */
364
365 // Well, we're actually there!
366 // Try it against each key in turn
367
368 std::vector<key_info>::iterator it;
369 for (it = sigdat->keys_to_try->begin ();
370 it < sigdat->keys_to_try->end ();
371 ++it)
372 {
373 rv = gcry_pk_verify (sig, hash, it->key);
374
375 LogBabblePrintf("signature: tried key %s, returned 0x%08x %s\n",
376 it->name.c_str(), rv, gcry_strerror(rv));
377
378 if (rv != GPG_ERR_NO_ERROR)
379 continue;
380 // Found it! This key gets kept!
381 if (!it->builtin)
382 add_key_from_sexpr (it->key);
383 break;
384 }
385
386 gcry_sexp_release (sig);
387 gcry_sexp_release (hash);
388 }
389
390 return (rv == GPG_ERR_NO_ERROR);
391}
392
dbfe3c19
DK
393/* Do-nothing stubs called by the sig file walker to
394 walk over the embedded subpackets. In the event, we don't
395 actually need to do this as we aren't inspecting them. */
396static enum
397pkt_cb_resp hashed_subpkt_walker (struct packet_walker *wlk, unsigned char tag,
398 size_t packetsize, size_t hdrpos)
399{
400 return pktCONTINUE;
401}
402
403static enum
404pkt_cb_resp unhashed_subpkt_walker (struct packet_walker *wlk, unsigned char tag,
405 size_t packetsize, size_t hdrpos)
406{
407 return pktCONTINUE;
408}
409
410/* Callback to parse the packets found in the setup.ini/setup.bz2
411 signature file. We have to parse the header to get the hash type
412 and other details. Once we have that we can create a message
413 digest context and start pumping data through it; first the ini
414 file itself, then the portion of the packet itself that is
415 covered by the hash. */
416 static enum
417pkt_cb_resp sig_file_walker (struct packet_walker *wlk, unsigned char tag,
418 size_t packetsize, size_t hdrpos)
419{
420 struct sig_data *sigdat = (struct sig_data *)(wlk->userdata);
dbfe3c19
DK
421
422 if (tag != RFC4880_PT_SIGNATURE)
423 return pktCONTINUE;
424
425 // To add the trailers later, we hang on to the current pos.
426 size_t v34hdrofs = wlk->pfile->tell ();
427
428 // So, get the data out. Version is first.
429 char ver = pkt_getch (wlk->pfile);
430 if ((ver < 3) || (ver > 4))
431 {
432 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, ver, "unsupported sig version.");
433 return pktHALT;
434 }
435
436 // Only V3 and V4 accepted.
437 if (ver == 4)
438 {
439 sigdat->sig_type = pkt_getch (wlk->pfile);
440 sigdat->pk_alg = pkt_getch (wlk->pfile);
441 sigdat->hash_alg = pkt_getch (wlk->pfile);
442 }
443 else
444 {
445 int hmsize = pkt_getch (wlk->pfile);
446 if (hmsize != RFC4880_SIGV3_HASHED_SIZE)
447 {
448 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, hmsize, "wrong hashed material size.");
449 return pktHALT;
450 }
451 v34hdrofs = wlk->pfile->tell ();
452 if ((pkt_getch (wlk->pfile) < 0) || (pkt_getdword (wlk->pfile) == -1))
453 {
454 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, hmsize, "wrong hashed material size.");
455 return pktHALT;
456 }
457 if ((pkt_getdword (wlk->pfile) == -1) || (pkt_getdword (wlk->pfile) == -1))
458 {
459 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, -1, "missing signer ID.");
460 return pktHALT;
461 }
462
463 sigdat->sig_type = 0;
464 sigdat->pk_alg = pkt_getch (wlk->pfile);
465 sigdat->hash_alg = pkt_getch (wlk->pfile);
466 }
467
8eb0b0eb
JT
468 LogBabblePrintf("signature: sig_type %d, pk_alg %d, hash_alg %d\n",
469 sigdat->sig_type, sigdat->pk_alg, sigdat->hash_alg);
dbfe3c19
DK
470
471 // We only handle binary file signatures
472 if (sigdat->sig_type != RFC4880_ST_BINARY)
473 {
474 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->sig_type, "unsupported sig type.");
475 return pktHALT;
476 }
28d401b5
JT
477
478 // We only handle RSA and DSA keys
479 if ((sigdat->pk_alg != RFC4880_PK_DSA) && (sigdat->pk_alg != RFC4880_PK_RSA))
dbfe3c19
DK
480 {
481 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->pk_alg, "unsupported pk alg.");
482 return pktHALT;
483 }
484
485 // Start to hash all the data. Figure out what hash to use.
486 sigdat->algo = pkt_convert_hashcode (sigdat->hash_alg);
487 if (sigdat->algo == GCRY_MD_NONE)
488 {
489 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->hash_alg, "unconvertible hash.");
490 return pktHALT;
491 }
492
493 // Now we know hash algo, we can create md context.
5f4250b8 494 sigdat->md = 0;
dbfe3c19
DK
495 gcry_error_t rv = gcry_md_open (&sigdat->md, sigdat->algo, 0);
496 if (rv != GPG_ERR_NO_ERROR)
497 {
498 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, rv, "while initialising message digest.");
499 return pktHALT;
500 }
501
502 // Add all the sig_file data into the hash.
503 sigdat->sign_data->seek (0, IO_SEEK_SET);
504 size_t nbytes = sigdat->sign_data->get_size ();
505 if (nbytes != shovel_stream_data_into_md (sigdat->sign_data, nbytes, sigdat->md))
506 {
507 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->hash_alg, "internal buffer error.");
508 return pktHALT;
509 }
510 sigdat->sign_data->seek (0, IO_SEEK_SET);
511
512 // V4 now has some hashed subpackets
513 int hashed_subpkt_size = (ver == 4) ? pkt_getword (wlk->pfile) : 0;
514 if (hashed_subpkt_size)
515 pkt_walk_subpackets (wlk->pfile, hashed_subpkt_walker, wlk->owner,
516 wlk->pfile->tell (), hashed_subpkt_size, wlk->userdata);
517
518 // V4 now has some unhashed subpackets
519 int unhashed_subpkt_size = (ver == 4) ? pkt_getword (wlk->pfile) : 0;
520 if (unhashed_subpkt_size)
521 pkt_walk_subpackets (wlk->pfile, unhashed_subpkt_walker, wlk->owner,
522 wlk->pfile->tell (), unhashed_subpkt_size, wlk->userdata);
523
524 // Both formats now have 16 bits of the hash value.
525 int hash_first = pkt_getword (wlk->pfile);
526
8eb0b0eb 527 MESSAGE ("signature: hash leftmost 2 bytes 0x%04x\n", hash_first);
dbfe3c19 528
28d401b5 529 /* Algorithm-Specific Fields for signatures:
dbfe3c19 530
28d401b5
JT
531 for DSA:
532 - MPI of DSA value r
533 - MPI of DSA value s
dbfe3c19 534
28d401b5
JT
535 DSA signatures MUST use hashes that are equal in size to the number of
536 bits of q, the group generated by the DSA key's generator value.
dbfe3c19 537
28d401b5
JT
538 for RSA:
539 - MPI of RSA value m^d mod n (aka s)
540 */
5f4250b8
JT
541 sigdat->dsa_mpi_r = sigdat->dsa_mpi_s = 0;
542 sigdat->rsa_mpi_s = 0;
dbfe3c19 543
28d401b5 544 if (sigdat->pk_alg == RFC4880_PK_DSA)
dbfe3c19 545 {
28d401b5
JT
546 if ((pkt_get_mpi (&sigdat->dsa_mpi_r, wlk->pfile) < 0)
547 || (pkt_get_mpi (&sigdat->dsa_mpi_s, wlk->pfile) < 0))
548 {
549 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, "unpacking mpi.");
550 return pktHALT;
551 }
552 }
553 else if (sigdat->pk_alg == RFC4880_PK_RSA)
554 {
555 if (pkt_get_mpi (&sigdat->rsa_mpi_s, wlk->pfile) < 0)
556 {
557 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, "unpacking mpi.");
558 return pktHALT;
559 }
dbfe3c19
DK
560 }
561
562 MESSAGE ("Read sig packets succesfully!\n");
563
564 // Now we got all the data out ok, rewind and hash the first trailer.
565 wlk->pfile->seek (v34hdrofs, IO_SEEK_SET);
566 nbytes = (ver == 4) ? (RFC4880_SIGV4_HASHED_OVERHEAD + hashed_subpkt_size)
567 : (RFC4880_SIGV3_HASHED_SIZE);
568 if (nbytes != shovel_stream_data_into_md (wlk->pfile, nbytes, sigdat->md))
569 {
570 ERRKIND (wlk->owner, IDS_CRYPTO_ERROR, sigdat->hash_alg, "internal buffer error 2.");
571 return pktHALT;
572 }
573
574 if (ver == 4)
575 {
576 // And now the synthetic final trailer.
577 gcry_md_putc (sigdat->md, 4);
578 gcry_md_putc (sigdat->md, 0xff);
579 gcry_md_putc (sigdat->md, (nbytes >> 24) & 0xff);
580 gcry_md_putc (sigdat->md, (nbytes >> 16) & 0xff);
581 gcry_md_putc (sigdat->md, (nbytes >> 8) & 0xff);
582 gcry_md_putc (sigdat->md, nbytes & 0xff);
583 }
584
5f4250b8
JT
585 // finalize the hash
586 gcry_md_final (sigdat->md);
587 MESSAGE("digest length is %d\n",gcry_md_get_algo_dlen (sigdat->algo));
588
589 // we have hashed all the data, and found the sig coefficients.
590 // heck this signature
591 if (verify_sig (sigdat, wlk->owner))
592 sigdat->valid = true;
593
594 // discard hash
595 if (sigdat->md)
596 gcry_md_close (sigdat->md);
597
598 // discard sig coefffcients
599 if (sigdat->dsa_mpi_r)
600 gcry_mpi_release (sigdat->dsa_mpi_r);
601 if (sigdat->dsa_mpi_s)
602 gcry_mpi_release (sigdat->dsa_mpi_s);
603 if (sigdat->rsa_mpi_s)
604 gcry_mpi_release (sigdat->rsa_mpi_s);
605
606 // we can stop immediately if we found a good signature
607 return sigdat->valid ? pktHALT : pktCONTINUE;
dbfe3c19
DK
608}
609
8eb0b0eb
JT
610#if CRYPTODEBUGGING
611static void
612gcrypt_log_adaptor(void *priv, int level, const char *fmt, va_list args)
613{
614 static std::string collected;
615
616 char buf[GPG_KEY_SEXPR_BUF_SIZE];
617 vsnprintf (buf, GPG_KEY_SEXPR_BUF_SIZE, fmt, args);
618
619 char *start = buf;
620 char *end;
621
622 do
623 {
624 if (collected.length() == 0)
625 {
626 collected = "gcrypt: ";
627 }
628
629 end = strchr(start, '\n');
630 if (end)
631 *end = '\0';
632
633 collected += start;
634
635 if (end)
636 {
637 if (level == GCRY_LOG_DEBUG)
638 Log (LOG_BABBLE) << collected << endLog;
639 else
640 Log (LOG_PLAIN) << collected << endLog;
641
642 collected.clear();
643 start = end + 1;
644 }
645 }
646 while (end);
647}
648#endif
649
dbfe3c19
DK
650/* Verify the signature on an ini file. Takes care of all key-handling. */
651bool
652verify_ini_file_sig (io_stream *ini_file, io_stream *ini_sig_file, HWND owner)
653{
dbfe3c19
DK
654 /* Data returned from packet walker. */
655 struct sig_data sigdat;
656
98d3e45a 657 /* Vector of keys to use. */
98d3e45a 658 std::vector<struct key_info> keys_to_try;
dbfe3c19
DK
659
660 /* Overall status of signature. */
661 bool sig_ok = false;
662
663 // Temps for intermediate processing.
664 gcry_error_t rv;
665 size_t n;
666
667 /* Initialise the library. */
8eb0b0eb
JT
668 static bool gcrypt_init = false;
669 if (!gcrypt_init)
670 {
671#if CRYPTODEBUGGING
672 gcry_set_log_handler (gcrypt_log_adaptor, NULL);
8eb0b0eb
JT
673#endif
674 gcry_check_version (NULL);
f724f2f3
JT
675
676 if ((rv = gcry_control (GCRYCTL_SELFTEST)) != GPG_ERR_NO_ERROR)
677 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "libgcrypt selftest failed");
678
679#if CRYPTODEBUGGING
680 gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
681#endif
8eb0b0eb
JT
682 gcrypt_init = true;
683 }
dbfe3c19
DK
684
685 /* So first build the built-in key. */
9096650d
JT
686 gcry_sexp_t cygwin_key;
687 rv = gcry_sexp_new (&cygwin_key, cygwin_pubkey_sexpr, strlen (cygwin_pubkey_sexpr), 1);
dbfe3c19
DK
688 if (rv != GPG_ERR_NO_ERROR)
689 {
690 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating pubkey s-expr.");
691 }
98d3e45a
JT
692 else
693 {
9096650d 694 keys_to_try.push_back (key_info("cygwin", true, cygwin_key));
98d3e45a 695 }
dbfe3c19
DK
696
697#if CRYPTODEBUGGING
698 char sexprbuf[GPG_KEY_SEXPR_BUF_SIZE];
9096650d 699 n = gcry_sexp_sprint (cygwin_key, GCRYSEXP_FMT_ADVANCED, sexprbuf, GPG_KEY_SEXPR_BUF_SIZE);
1211705b 700 LogBabblePrintf ("key:%d\n'%s'", n, sexprbuf);
dbfe3c19
DK
701#endif /* CRYPTODEBUGGING */
702
9096650d
JT
703 /* If not disabled, also try the old built-in key */
704 gcry_sexp_t cygwin_old_key;
2fd5251b 705 if (EnableOldKeysOption)
9096650d
JT
706 {
707 rv = gcry_sexp_new (&cygwin_old_key, cygwin_old_pubkey_sexpr, strlen (cygwin_old_pubkey_sexpr), 1);
708 if (rv != GPG_ERR_NO_ERROR)
709 {
710 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "while creating old pubkey s-expr.");
711 }
712 else
713 {
714 keys_to_try.push_back (key_info ("cygwin-old", TRUE, cygwin_old_key));
715 }
716 }
98d3e45a
JT
717 /* Vector of cached extra keys from last run. */
718 static std::vector<gcry_sexp_t> input_keys;
719
b2aa53c2
KB
720 /* Next we should extract the keys from the extrakeys user
721 setting, and flush it; we'll only return them to it if they
722 get used. OTOH, should we do this at all? The user settings
dbfe3c19
DK
723 file isn't heavily protected. So we only trust the extra
724 keys if we're told to by the user. We still read them in
725 and write them back out, which canonicalises and eliminates
726 any duplicates or garbage lines that may have crept in. */
727 static bool input_keys_read = false;
728 if (!input_keys_read)
729 {
730 // We only want to do this once, first time through:
731 input_keys_read = true;
732 // Copy all valid keys from ExtraKeysSetting into a
733 // static vector where we can keep them throughout the
734 // remainder of the run.
f26f525f 735 for (size_t i = 0; i < ExtraKeysSetting::instance().num_keys (); i++)
dbfe3c19 736 {
f26f525f 737 const char *keystring = ExtraKeysSetting::instance().get_key (i, &n);
dbfe3c19
DK
738 gcry_sexp_t newkey;
739 rv = gcry_sexp_new (&newkey, keystring, n, 1);
740 if (rv == GPG_ERR_NO_ERROR)
741 input_keys.push_back (newkey);
742 }
743
744 // Now flush out the ExtraKeysSetting; from here on it
745 // will build up a list of the keys we want to retain.
f26f525f 746 ExtraKeysSetting::instance().flush ();
dbfe3c19
DK
747
748 // Which, if we aren't using them, means all the ones
749 // we just read.
750 if (KeepUntrustedKeysOption || !UntrustedKeysOption)
751 {
752 std::vector<gcry_sexp_t>::iterator it;
753 for (it = input_keys.begin (); it < input_keys.end (); ++it)
754 add_key_from_sexpr (*it);
755 }
756 }
757
98d3e45a
JT
758 // We only use the untrusted keys if told to.
759 if (KeepUntrustedKeysOption || UntrustedKeysOption)
760 for (std::vector<gcry_sexp_t>::const_iterator it = input_keys.begin ();
761 it < input_keys.end ();
762 ++it)
763 {
764 keys_to_try.push_back (key_info ("saved key", false, *it, false));
765 }
766
dbfe3c19 767 /* Next, there may have been command-line options. */
7c05cfce
JT
768 std::vector<std::string> SexprExtraKeyStrings = SexprExtraKeyOption;
769 for (std::vector<std::string>::const_iterator it
770 = SexprExtraKeyStrings.begin ();
771 it != SexprExtraKeyStrings.end (); ++it)
dbfe3c19 772 {
7c05cfce 773 MESSAGE ("key str is '%s'\n", it->c_str ());
dbfe3c19 774 gcry_sexp_t dsa_key2 = 0;
7c05cfce 775 rv = gcry_sexp_new (&dsa_key2, it->c_str (), it->size (), 1);
dbfe3c19
DK
776 if (rv == GPG_ERR_NO_ERROR)
777 {
778 // We probably want to add it to the extra keys setting
779 // if KeepUntrustedKeysOption is supplied.
780 if (KeepUntrustedKeysOption)
781 add_key_from_sexpr (dsa_key2);
782#if CRYPTODEBUGGING
783 n = gcry_sexp_sprint (dsa_key2, GCRYSEXP_FMT_ADVANCED, sexprbuf,
784 GPG_KEY_SEXPR_BUF_SIZE);
785 // +1 because we want to include the nul-terminator.
786 n = fold_lfs_and_spaces (sexprbuf, n + 1);
f26f525f 787 ExtraKeysSetting::instance().add_key (sexprbuf);
1211705b 788 LogBabblePrintf ("key2:%d\n'%s'", n, sexprbuf);
dbfe3c19 789#endif /* CRYPTODEBUGGING */
98d3e45a 790 keys_to_try.push_back (key_info ("from command-line option --sexpr-pubkey", false, dsa_key2));
dbfe3c19
DK
791 }
792 else
793 {
794 ERRKIND (owner, IDS_CRYPTO_ERROR, rv, "invalid command-line pubkey s-expr.");
795 }
796 }
797
798 /* Also, we may have to read a key(s) file. */
7c05cfce
JT
799 std::vector<std::string> ExtraKeysFiles = ExtraKeyOption;
800 for (std::vector<std::string>::const_iterator it
801 = ExtraKeysFiles.begin ();
802 it != ExtraKeysFiles.end (); ++it)
dbfe3c19 803 {
7c05cfce 804 io_stream *keys = get_url_to_membuf (*it, owner);
dbfe3c19
DK
805 if (keys)
806 {
807 struct key_data kdat;
808 pkt_walk_packets (keys, key_file_walker, owner, 0, keys->get_size (), &kdat);
809 // We now have a vector of (some/any?) keys returned from
810 // the walker; add them to the list to try.
811 while (!kdat.keys.empty ())
812 {
813 // We probably want to add it to the extra keys setting
814 // if KeepUntrustedKeysOption is supplied.
815 if (KeepUntrustedKeysOption)
816 add_key_from_sexpr (kdat.keys.back ());
817#if CRYPTODEBUGGING
818 n = gcry_sexp_sprint (kdat.keys.back (), GCRYSEXP_FMT_ADVANCED,
819 sexprbuf, GPG_KEY_SEXPR_BUF_SIZE);
820 // +1 because we want to include the nul-terminator.
821 n = fold_lfs_and_spaces (sexprbuf, n + 1);
f26f525f 822 ExtraKeysSetting::instance().add_key (sexprbuf);
1211705b 823 LogBabblePrintf ("key3:%d\n'%s'", n, sexprbuf);
dbfe3c19 824#endif /* CRYPTODEBUGGING */
98d3e45a 825 keys_to_try.push_back (key_info ("from command-line option --pubkey", false, kdat.keys.back ()));
dbfe3c19
DK
826 kdat.keys.pop_back ();
827 }
828 }
829 }
830
831 // We pass in a pointer to the ini file in the user context data,
832 // which the packet walker callback uses to create a new hash
833 // context preloaded with all the signature-covered data.
5f4250b8 834 sigdat.valid = false;
dbfe3c19 835 sigdat.sign_data = ini_file;
5f4250b8 836 sigdat.keys_to_try = &keys_to_try;
dbfe3c19 837
5f4250b8
JT
838 pkt_walk_packets (ini_sig_file, sig_file_walker, owner, 0,
839 ini_sig_file->get_size (), &sigdat);
dbfe3c19 840
5f4250b8 841 sig_ok = sigdat.valid;
dbfe3c19 842
dbfe3c19
DK
843 while (keys_to_try.size ())
844 {
98d3e45a
JT
845 if (keys_to_try.back ().owned)
846 gcry_sexp_release (keys_to_try.back ().key);
dbfe3c19
DK
847 keys_to_try.pop_back ();
848 }
849
850 return sig_ok;
851}
This page took 0.213166 seconds and 6 git commands to generate.