1#! /usr/bin/perl 2# ex:ts=8 sw=4: 3# $OpenBSD: PkgSign.pm,v 1.17 2019/07/08 10:55:39 espie Exp $ 4# 5# Copyright (c) 2003-2014 Marc Espie <espie@openbsd.org> 6# 7# Permission to use, copy, modify, and distribute this software for any 8# purpose with or without fee is hereby granted, provided that the above 9# copyright notice and this permission notice appear in all copies. 10# 11# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 19use strict; 20use warnings; 21 22use OpenBSD::AddCreateDelete; 23use OpenBSD::Signer; 24 25package OpenBSD::PkgSign::State; 26our @ISA = qw(OpenBSD::CreateSign::State); 27 28sub handle_options 29{ 30 my $state = shift; 31 32 $state->{extra_stats} = 0; 33 $state->{opt} = { 34 'o' => 35 sub { 36 $state->{output_dir} = shift; 37 }, 38 'S' => 39 sub { 40 $state->{source} = shift; 41 }, 42 's' => 43 sub { 44 push(@{$state->{signature_params}}, shift); 45 }, 46 'V' => 47 sub { 48 $state->{extra_stats}++; 49 }, 50 }; 51 $state->{signature_style} = 'unsigned'; 52 53 $state->SUPER::handle_options('Cij:o:S:s:V', 54 '[-CvV] [-D name[=value]] -s signify2 -s priv', 55 '[-o dir] [-S source] [pkg-name...]'); 56 if (defined $state->{signature_params}) { 57 $state->{signer} = OpenBSD::Signer->factory($state); 58 } 59 if (!defined $state->{signer}) { 60 $state->usage("Can't invoke command without valid signing parameters"); 61 } 62 $state->{output_dir} //= "."; 63 if (!-d $state->{output_dir}) { 64 require File::Path; 65 File::Path::make_path($state->{output_dir}) 66 or $state->usage("can't create dir"); 67 } 68 $state->{wantntogo} = $state->{extra_stats}; 69} 70 71package OpenBSD::PkgSign; 72use OpenBSD::Temp; 73use OpenBSD::PackingList; 74use OpenBSD::PackageInfo; 75 76sub sign_existing_package 77{ 78 my ($self, $state, $pkg) = @_; 79 my $output = $state->{output_dir}; 80 my $dest = $output.'/'.$pkg->name.".tgz"; 81 if ($state->opt('i')) { 82 if (-f $dest) { 83 return; 84 } 85 } 86 my (undef, $tmp) = OpenBSD::Temp::permanent_file($output, "pkg") or 87 die $state->fatal(OpenBSD::Temp->last_error); 88 $state->{signer}->sign($pkg, $state, $tmp); 89 90 chmod((0666 & ~umask), $tmp); 91 rename($tmp, $dest) or 92 $state->fatal("Can't create final signed package: #1", $!); 93 if ($state->opt('C')) { 94 $state->system(sub { 95 chdir($output); 96 open(STDOUT, '>>', 'SHA256'); 97 }, 98 OpenBSD::Paths->sha256, '-b', $pkg->name.".tgz"); 99 } 100} 101 102sub sign_list 103{ 104 my ($self, $l, $repo, $maxjobs, $state) = @_; 105 $state->{total} = scalar @$l; 106 $maxjobs //= 1; 107 my $code = sub { 108 my $name = shift; 109 my $pkg = $repo->find($name); 110 if (!defined $pkg) { 111 $state->errsay("#1 not found", $name); 112 } else { 113 $self->sign_existing_package($state, $pkg); 114 } 115 }; 116 my $display = $state->verbose ? 117 sub { 118 $state->progress->set_header("Signed ".shift); 119 $state->{done}++; 120 $state->progress->next($state->ntogo); 121 } : 122 sub { 123 }; 124 if ($maxjobs > 1) { 125 my $jobs = {}; 126 my $n = 0; 127 my $reap_job = sub { 128 my $pid = wait; 129 if (!defined $jobs->{$pid}) { 130 $state->fatal("Wait returned #1: unknown process", $pid); 131 } 132 if ($? != 0) { 133 $state->fatal("Signature of #1 failed\n", 134 $jobs->{$pid}); 135 } 136 $n--; 137 &$display($jobs->{$pid}); 138 delete $state->{signer}{pubkey}; 139 delete $jobs->{$pid}; 140 }; 141 142 while (@$l > 0) { 143 my $name = shift @$l; 144 my $pid = fork(); 145 if ($pid == 0) { 146 $repo->reinitialize; 147 &$code($name); 148 exit(0); 149 } else { 150 $jobs->{$pid} = $name; 151 $n++; 152 } 153 if ($n >= $maxjobs) { 154 &$reap_job; 155 } 156 } 157 while ($n != 0) { 158 &$reap_job; 159 } 160 } else { 161 for my $name (@$l) { 162 &$code($name); 163 &$display($name); 164 delete $state->{signer}{pubkey}; 165 } 166 } 167 if ($state->opt('C')) { 168 $state->system(sub { 169 chdir($state->{output_dir}); 170 open(STDOUT, '>', 'SHA256.new'); 171 }, 'sort', 'SHA256'); 172 rename($state->{output_dir}.'/SHA256.new', 173 $state->{output_dir}.'/SHA256'); 174 } 175} 176 177sub sign_existing_repository 178{ 179 my ($self, $state, $source) = @_; 180 require OpenBSD::PackageRepository; 181 my $repo = OpenBSD::PackageRepository->new($source, $state); 182 if ($state->{signer}->want_local && !$repo->is_local_file) { 183 $state->fatal("Signing distant source is not supported"); 184 } 185 my @list = sort @{$repo->list}; 186 if (@list == 0) { 187 $state->errsay('Source repository "#1" is empty', $source); 188 } 189 $self->sign_list(\@list, $repo, $state->opt('j'), $state); 190} 191 192 193sub parse_and_run 194{ 195 my ($self, $cmd) = @_; 196 my $state = OpenBSD::PkgSign::State->new($cmd); 197 $state->handle_options; 198 if (!defined $state->{source} && @ARGV == 0) { 199 $state->usage("Nothing to sign"); 200 } 201 if (defined $state->{source}) { 202 $self->sign_existing_repository($state, 203 $state->{source}); 204 } 205 $self->sign_list(\@ARGV, $state->repo, $state->opt('j'), 206 $state); 207 return 0; 208} 209 210