File: | Dpkg/OpenPGP/Backend/GnuPG.pm |
Coverage: | 73.6% |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | # Copyright © 2007, 2022 Guillem Jover <guillem@debian.org> | ||||||
2 | # | ||||||
3 | # This program is free software; you can redistribute it and/or modify | ||||||
4 | # it under the terms of the GNU General Public License as published by | ||||||
5 | # the Free Software Foundation; either version 2 of the License, or | ||||||
6 | # (at your option) any later version. | ||||||
7 | # | ||||||
8 | # This program is distributed in the hope that it will be useful, | ||||||
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
11 | # GNU General Public License for more details. | ||||||
12 | # | ||||||
13 | # You should have received a copy of the GNU General Public License | ||||||
14 | # along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||||
15 | |||||||
16 | =encoding utf8 | ||||||
17 | |||||||
18 - 29 | =head1 NAME Dpkg::OpenPGP::Backend::GnuPG - OpenPGP backend for GnuPG =head1 DESCRIPTION This module provides a class that implements the OpenPGP backend for GnuPG. B<Note>: This is a private module, its API can change at any time. =cut | ||||||
30 | |||||||
31 | package Dpkg::OpenPGP::Backend::GnuPG 0.01; | ||||||
32 | |||||||
33 | 174 174 174 | 696 348 5220 | use strict; | ||||
34 | 174 174 174 | 406 232 11948 | use warnings; | ||||
35 | |||||||
36 | 174 174 174 | 754 580 1160 | use POSIX qw(:sys_wait_h); | ||||
37 | 174 174 174 | 33176 348 12470 | use File::Basename; | ||||
38 | 174 174 174 | 95584 1166670 10846 | use File::Temp; | ||||
39 | 174 174 174 | 34800 314592 9860 | use File::Copy; | ||||
40 | 174 174 174 | 101848 113854 9280 | use MIME::Base64; | ||||
41 | |||||||
42 | 174 174 174 | 696 174 10324 | use Dpkg::ErrorHandling; | ||||
43 | 174 174 174 | 406 174 4640 | use Dpkg::IPC; | ||||
44 | 174 174 174 | 52838 232 7366 | use Dpkg::File; | ||||
45 | 174 174 174 | 754 290 5394 | use Dpkg::Path qw(find_command); | ||||
46 | 174 174 174 | 464 232 12354 | use Dpkg::OpenPGP::ErrorCodes; | ||||
47 | |||||||
48 | 174 174 174 | 522 116 638 | use parent qw(Dpkg::OpenPGP::Backend); | ||||
49 | |||||||
50 | sub DEFAULT_CMDV { | ||||||
51 | 312 | 0 | 1722 | return [ qw(gpgv-sq gpgv) ]; | |||
52 | } | ||||||
53 | |||||||
54 | sub DEFAULT_CMDSTORE { | ||||||
55 | 312 | 0 | 1110 | return [ qw(gpg-agent) ]; | |||
56 | } | ||||||
57 | |||||||
58 | sub DEFAULT_CMD { | ||||||
59 | 312 | 0 | 1534 | return [ qw(gpg-sq gpg) ]; | |||
60 | } | ||||||
61 | |||||||
62 | sub has_backend_cmd { | ||||||
63 | 528 | 0 | 940 | my $self = shift; | |||
64 | |||||||
65 | 528 | 4340 | return defined $self->{cmd} && defined $self->{cmdstore}; | ||||
66 | } | ||||||
67 | |||||||
68 | sub has_keystore { | ||||||
69 | 264 | 0 | 514 | my $self = shift; | |||
70 | |||||||
71 | 264 | 840 | return 0 if not defined $self->{cmdstore}; | ||||
72 | return 1 if ($ENV{GNUPGHOME} && -e $ENV{GNUPGHOME}) || | ||||||
73 | 264 | 3880 | ($ENV{HOME} && -e "$ENV{HOME}/.gnupg"); | ||||
74 | 0 | 0 | return 0; | ||||
75 | } | ||||||
76 | |||||||
77 | sub can_use_key { | ||||||
78 | 264 | 0 | 452 | my ($self, $key) = @_; | |||
79 | |||||||
80 | # With gpg, a secret key always requires gpg-agent (the key store). | ||||||
81 | 264 | 966 | return $self->has_keystore(); | ||||
82 | } | ||||||
83 | |||||||
84 | sub has_verify_cmd { | ||||||
85 | 1740 | 0 | 2974 | my $self = shift; | |||
86 | |||||||
87 | 1740 | 10234 | return defined $self->{cmdv} || defined $self->{cmd}; | ||||
88 | } | ||||||
89 | |||||||
90 | sub get_trusted_keyrings { | ||||||
91 | 0 | 0 | 0 | my $self = shift; | |||
92 | |||||||
93 | 0 | 0 | my $keystore; | ||||
94 | 0 | 0 | if ($ENV{GNUPGHOME} && -e $ENV{GNUPGHOME}) { | ||||
95 | 0 | 0 | $keystore = $ENV{GNUPGHOME}; | ||||
96 | } elsif ($ENV{HOME} && -e "$ENV{HOME}/.gnupg") { | ||||||
97 | 0 | 0 | $keystore = "$ENV{HOME}/.gnupg"; | ||||
98 | } else { | ||||||
99 | 0 | 0 | return; | ||||
100 | } | ||||||
101 | |||||||
102 | 0 | 0 | my @keyrings; | ||||
103 | 0 | 0 | foreach my $keyring (qw(trustedkeys.kbx trustedkeys.gpg)) { | ||||
104 | 0 | 0 | push @keyrings, "$keystore/$keyring" if -r "$keystore/$keyring"; | ||||
105 | } | ||||||
106 | 0 | 0 | return @keyrings; | ||||
107 | } | ||||||
108 | |||||||
109 | # _pgp_* functions are strictly for applying or removing ASCII armor. | ||||||
110 | # See <https://datatracker.ietf.org/doc/html/rfc4880#section-6> for more | ||||||
111 | # details. | ||||||
112 | # | ||||||
113 | # Note that these _pgp_* functions are only necessary while relying on | ||||||
114 | # gpgv, and gpgv itself does not verify multiple signatures correctly | ||||||
115 | # (see https://bugs.debian.org/1010955). | ||||||
116 | |||||||
117 | sub _pgp_dearmor_data { | ||||||
118 | 1560 | 3884 | my ($type, $data) = @_; | ||||
119 | |||||||
120 | # Note that we ignore an incorrect or absent checksum, following the | ||||||
121 | # guidance of | ||||||
122 | # <https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/>. | ||||||
123 | 1560 | 84942 | my $armor_regex = qr{ | ||||
124 | -----BEGIN\ PGP\ \Q$type\E-----[\r\t ]*\n | ||||||
125 | (?:[^:]+:\ [^\n]*[\r\t ]*\n)* | ||||||
126 | [\r\t ]*\n | ||||||
127 | ([a-zA-Z0-9/+\n]+={0,2})[\r\t ]*\n | ||||||
128 | (?:=[a-zA-Z0-9/+]{4}[\r\t ]*\n)? | ||||||
129 | -----END\ PGP\ \Q$type\E----- | ||||||
130 | }xm; | ||||||
131 | |||||||
132 | 1560 | 22676 | if ($data =~ m/$armor_regex/) { | ||||
133 | 936 | 9716 | return decode_base64($1); | ||||
134 | } | ||||||
135 | 624 | 6604 | return; | ||||
136 | } | ||||||
137 | |||||||
138 | sub _pgp_armor_checksum { | ||||||
139 | 936 | 1756 | my ($data) = @_; | ||||
140 | |||||||
141 | # From the upcoming revision to RFC 4880 | ||||||
142 | # <https://datatracker.ietf.org/doc/draft-ietf-openpgp-crypto-refresh/>. | ||||||
143 | # | ||||||
144 | # The resulting three-octet-wide value then gets base64-encoded into | ||||||
145 | # four base64 ASCII characters. | ||||||
146 | |||||||
147 | 936 | 1260 | my $CRC24_INIT = 0xB704CE; | ||||
148 | 936 | 924 | my $CRC24_GENERATOR = 0x864CFB; | ||||
149 | |||||||
150 | 936 | 25570 | my @bytes = unpack 'C*', $data; | ||||
151 | 936 | 2844 | my $crc = $CRC24_INIT; | ||||
152 | 936 | 2024 | for my $b (@bytes) { | ||||
153 | 414336 | 297262 | $crc ^= ($b << 16); | ||||
154 | 414336 | 329632 | for (1 .. 8) { | ||||
155 | 3314688 | 2082484 | $crc <<= 1; | ||||
156 | 3314688 | 3004576 | if ($crc & 0x1000000) { | ||||
157 | # Clear bit 25 to avoid overflow. | ||||||
158 | 1664520 | 1064520 | $crc &= 0xffffff; | ||||
159 | 1664520 | 1226678 | $crc ^= $CRC24_GENERATOR; | ||||
160 | } | ||||||
161 | } | ||||||
162 | } | ||||||
163 | 936 | 7230 | my $sum = pack 'CCC', ($crc >> 16) & 0xff, ($crc >> 8) & 0xff, $crc & 0xff; | ||||
164 | 936 | 16944 | return encode_base64($sum, q{}); | ||||
165 | } | ||||||
166 | |||||||
167 | sub _pgp_armor_data { | ||||||
168 | 936 | 1780 | my ($type, $data) = @_; | ||||
169 | |||||||
170 | 936 | 23884 | my $out = encode_base64($data, q{}) =~ s/(.{1,64})/$1\n/gr; | ||||
171 | 936 | 3848 | chomp $out; | ||||
172 | 936 | 2044 | my $crc = _pgp_armor_checksum($data); | ||||
173 | 936 | 5206 | my $armor = <<~"ARMOR"; | ||||
174 | -----BEGIN PGP $type----- | ||||||
175 | |||||||
176 | $out | ||||||
177 | =$crc | ||||||
178 | -----END PGP $type----- | ||||||
179 | ARMOR | ||||||
180 | 936 | 2298 | return $armor; | ||||
181 | } | ||||||
182 | |||||||
183 | sub armor { | ||||||
184 | 936 | 0 | 9492 | my ($self, $type, $in, $out) = @_; | |||
185 | |||||||
186 | 936 | 2936 | my $raw_data = file_slurp($in); | ||||
187 | 936 | 3318 | my $data = _pgp_dearmor_data($type, $raw_data) // $raw_data; | ||||
188 | 936 | 2462 | my $armor = _pgp_armor_data($type, $data); | ||||
189 | 936 | 2622 | return OPENPGP_BAD_DATA unless defined $armor; | ||||
190 | 936 | 3522 | file_dump($out, $armor); | ||||
191 | |||||||
192 | 936 | 8734 | return OPENPGP_OK; | ||||
193 | } | ||||||
194 | |||||||
195 | sub dearmor { | ||||||
196 | 624 | 0 | 1410 | my ($self, $type, $in, $out) = @_; | |||
197 | |||||||
198 | 624 | 2046 | my $armor = file_slurp($in); | ||||
199 | 624 | 1838 | my $data = _pgp_dearmor_data($type, $armor); | ||||
200 | 624 | 1698 | return OPENPGP_BAD_DATA unless defined $data; | ||||
201 | 624 | 3634 | file_dump($out, $data); | ||||
202 | |||||||
203 | 624 | 5236 | return OPENPGP_OK; | ||||
204 | } | ||||||
205 | |||||||
206 | sub _gpg_exec | ||||||
207 | { | ||||||
208 | 3348 | 12556 | my ($self, @exec) = @_; | ||||
209 | |||||||
210 | 3348 | 4098 | my ($stdout, $stderr); | ||||
211 | 3348 | 17506 | spawn(exec => \@exec, wait_child => 1, nocheck => 1, timeout => 10, | ||||
212 | to_string => \$stdout, error_to_string => \$stderr); | ||||||
213 | 3276 | 27926 | if (WIFEXITED($?)) { | ||||
214 | 3276 | 7295 | my $status = WEXITSTATUS($?); | ||||
215 | 3276 0 | 6664 0 | print { *STDERR } "$stdout$stderr" if $status; | ||||
216 | 3276 | 21190 | return $status; | ||||
217 | } else { | ||||||
218 | 0 | 0 | subprocerr("@exec"); | ||||
219 | } | ||||||
220 | } | ||||||
221 | |||||||
222 | sub _gpg_options_weak_digests { | ||||||
223 | my @gpg_weak_digests = map { | ||||||
224 | 1692 3384 | 2550 5424 | (qw(--weak-digest), $_) | ||||
225 | } qw(SHA1 RIPEMD160); | ||||||
226 | |||||||
227 | 1692 | 3390 | return @gpg_weak_digests; | ||||
228 | } | ||||||
229 | |||||||
230 | sub _gpg_verify { | ||||||
231 | 1428 | 3318 | my ($self, $signeddata, $sig, $data, @certs) = @_; | ||||
232 | |||||||
233 | 1428 | 7344 | return OPENPGP_MISSING_CMD if ! $self->has_verify_cmd(); | ||||
234 | |||||||
235 | 1428 | 12188 | my $gpg_home = File::Temp->newdir('dpkg-gpg-verify.XXXXXXXX', TMPDIR => 1); | ||||
236 | 1428 | 441906 | my @cmd_opts = qw(--no-options --no-default-keyring --batch --quiet); | ||||
237 | 1428 | 1920 | my @gpg_opts; | ||||
238 | 1428 | 2832 | push @gpg_opts, _gpg_options_weak_digests(); | ||||
239 | 1428 | 1854 | push @gpg_opts, '--homedir', $gpg_home; | ||||
240 | 1428 | 2072 | push @cmd_opts, @gpg_opts; | ||||
241 | |||||||
242 | 1428 | 1326 | my @exec; | ||||
243 | 1428 | 3510 | if ($self->{cmdv}) { | ||||
244 | 1428 | 2286 | push @exec, $self->{cmdv}; | ||||
245 | 1428 | 2618 | push @exec, @gpg_opts; | ||||
246 | # We need to touch the trustedkeys.gpg keyring, otherwise gpgv will | ||||||
247 | # emit an error about the trustedkeys.kbx file being of unknown type. | ||||||
248 | 1428 | 8070 | file_touch("$gpg_home/trustedkeys.gpg"); | ||||
249 | } else { | ||||||
250 | 0 | 0 | push @exec, $self->{cmd}; | ||||
251 | 0 | 0 | push @exec, @cmd_opts; | ||||
252 | } | ||||||
253 | 1428 | 4342 | foreach my $cert (@certs) { | ||||
254 | 1428 | 6296 | my $certring = File::Temp->new(UNLINK => 1, SUFFIX => '.pgp'); | ||||
255 | 1428 | 338714 | my $rc; | ||||
256 | # XXX: The internal dearmor() does not handle concatenated ASCII Armor, | ||||||
257 | # but the old implementation handled such certificate keyrings, so to | ||||||
258 | # avoid regressing for now, we fallback to use the GnuPG dearmor. | ||||||
259 | 1428 | 5078 | if ($cert =~ m{\.kbx$}) { | ||||
260 | # Accept GnuPG apparent keybox-format keyrings as-is. | ||||||
261 | 0 | 0 | $rc = 1; | ||||
262 | } elsif (defined $self->{cmd}) { | ||||||
263 | 1428 | 4318 | $rc = $self->_gpg_exec($self->{cmd}, @cmd_opts, '--yes', | ||||
264 | '--output', $certring, | ||||||
265 | '--dearmor', $cert); | ||||||
266 | } else { | ||||||
267 | 0 | 0 | $rc = $self->dearmor('PUBLIC KEY BLOCK', $cert, $certring); | ||||
268 | } | ||||||
269 | 1398 | 2360 | $certring = $cert if $rc; | ||||
270 | 1398 | 6445 | push @exec, '--keyring', $certring; | ||||
271 | } | ||||||
272 | 1398 | 2399 | push @exec, '--output', $data if defined $data; | ||||
273 | 1398 | 4863 | if (! $self->{cmdv}) { | ||||
274 | 0 | 0 | push @exec, '--verify'; | ||||
275 | } | ||||||
276 | 1398 | 2869 | push @exec, $sig if defined $sig; | ||||
277 | 1398 | 2133 | push @exec, $signeddata; | ||||
278 | |||||||
279 | 1398 | 3858 | my $rc = $self->_gpg_exec(@exec); | ||||
280 | 1368 | 3740 | return OPENPGP_NO_SIG if $rc; | ||||
281 | 1368 | 8116 | return OPENPGP_OK; | ||||
282 | } | ||||||
283 | |||||||
284 | sub inline_verify { | ||||||
285 | 864 | 0 | 2146 | my ($self, $inlinesigned, $data, @certs) = @_; | |||
286 | |||||||
287 | 864 | 2630 | return $self->_gpg_verify($inlinesigned, undef, $data, @certs); | ||||
288 | } | ||||||
289 | |||||||
290 | sub verify { | ||||||
291 | 564 | 0 | 1664 | my ($self, $data, $sig, @certs) = @_; | |||
292 | |||||||
293 | 564 | 1910 | return $self->_gpg_verify($data, $sig, undef, @certs); | ||||
294 | } | ||||||
295 | |||||||
296 | sub inline_sign { | ||||||
297 | 264 | 0 | 652 | my ($self, $data, $inlinesigned, $key) = @_; | |||
298 | |||||||
299 | 264 | 552 | return OPENPGP_MISSING_CMD if ! $self->has_backend_cmd(); | ||||
300 | |||||||
301 | 264 | 14056 | my $file = basename($data); | ||||
302 | 264 | 2298 | my $signdir = File::Temp->newdir('dpkg-sign.XXXXXXXX', TMPDIR => 1); | ||||
303 | 264 | 78198 | my $signfile = "$signdir/$file"; | ||||
304 | |||||||
305 | # Make sure the file to sign ends with a newline, as GnuPG does not adhere | ||||||
306 | # to the OpenPGP specification (see <https://dev.gnupg.org/T7106>). | ||||||
307 | 264 | 2676 | copy($data, $signfile); | ||||
308 | 264 | 43000 | open my $signfh, '>>', $signfile | ||||
309 | or syserr(g_('cannot open %s'), $signfile); | ||||||
310 | 264 264 | 314 866 | print { $signfh } "\n"; | ||||
311 | 264 | 1306 | close $signfh or syserr(g_('cannot close %s'), $signfile); | ||||
312 | |||||||
313 | 264 | 778 | my @exec = ($self->{cmd}); | ||||
314 | 264 | 540 | push @exec, _gpg_options_weak_digests(); | ||||
315 | 264 | 640 | push @exec, qw(--utf8-strings --textmode --armor); | ||||
316 | # Set conformance level. | ||||||
317 | 264 | 514 | push @exec, '--openpgp'; | ||||
318 | # Set secure algorithm preferences. | ||||||
319 | 264 | 502 | push @exec, '--personal-digest-preferences', 'SHA512 SHA384 SHA256 SHA224'; | ||||
320 | 264 | 778 | if ($key->type eq 'keyfile') { | ||||
321 | # Promote the keyfile keyhandle to a keystore, this way we share the | ||||||
322 | # same gpg-agent and can get any password cached. | ||||||
323 | 264 | 916 | my $gpg_home = File::Temp->newdir('dpkg-sign.XXXXXXXX', TMPDIR => 1); | ||||
324 | |||||||
325 | 264 | 53050 | push @exec, '--homedir', $gpg_home; | ||||
326 | 264 | 728 | $self->_gpg_exec(@exec, qw(--quiet --no-tty --batch --import), $key->handle); | ||||
327 | 258 | 2627 | $key->set('keystore', $gpg_home); | ||||
328 | } elsif ($key->type eq 'keystore') { | ||||||
329 | 0 | 0 | push @exec, '--homedir', $key->handle; | ||||
330 | } else { | ||||||
331 | 0 | 0 | push @exec, '--local-user', $key->handle; | ||||
332 | } | ||||||
333 | 258 | 933 | push @exec, '--output', $inlinesigned; | ||||
334 | |||||||
335 | 258 | 860 | my $rc = $self->_gpg_exec(@exec, '--clearsign', $data); | ||||
336 | 252 | 1020 | return OPENPGP_CMD_CANNOT_SIGN if $rc; | ||||
337 | 252 | 3408 | return OPENPGP_OK; | ||||
338 | } | ||||||
339 | |||||||
340 - 346 | =head1 CHANGES =head2 Version 0.xx This is a private module. =cut | ||||||
347 | |||||||
348 | 1; |