xref: /illumos-gate/usr/src/tools/codesign/signit.pl (revision 2210853d)
1#!/usr/perl5/bin/perl
2#
3# CDDL HEADER START
4#
5# The contents of this file are subject to the terms of the
6# Common Development and Distribution License (the "License").
7# You may not use this file except in compliance with the License.
8#
9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10# or http://www.opensolaris.org/os/licensing.
11# See the License for the specific language governing permissions
12# and limitations under the License.
13#
14# When distributing Covered Code, include this CDDL HEADER in each
15# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16# If applicable, add the following below this CDDL HEADER, with the
17# fields enclosed by brackets "[]" replaced with your own identifying
18# information: Portions Copyright [yyyy] [name of copyright owner]
19#
20# CDDL HEADER END
21#
22#
23# ident	"%Z%%M%	%I%	%E% SMI"
24#
25# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
26# Use is subject to license terms.
27#
28
29# signit [-q] [-i dir][-o dir] [-l user]
30#
31# Client program for use with code signing server.
32# Reads a list of signing credential names and file pathnames
33# from standard input. Each file is read from the input directory,
34# sent to the signing server, signed with the specified credential,
35# and written to the output directory.
36#
37# Options:
38#	-q	quiet operation: avoid printing files successfully signed
39#	-i dir	input directory (defaults to current dir)
40#	-o dir	output directory (defautls to input dir)
41#	-l user	user account on signing server (defaults to current user)
42#
43# The CODESIGN_SERVER environment variable can be used to
44# specify the hostname or IP address of the signing server
45# (defaults to quill.sfbay).
46
47use strict;
48use Cwd;
49use File::Temp 'tempdir';
50use Getopt::Std;
51use IPC::Open2;
52
53#
54# Global variables
55#
56my ($Indir, $Outdir);	# Input and output directories (may be the same)
57my $Server;		# Signing server hostname
58my $Quiet;		# Suppress printing each file successfully signed
59my ($pid);		# Process id for ssh client
60my @cred_rules;		# Array of path prefixes and credentials to use
61my $Tmpdir = tempdir(CLEANUP => 1);	# Temporary directory
62my $Warnings = 0;	# Count of warnings returned
63
64
65#
66# Main program
67#
68
69$Server = $ENV{CODESIGN_SERVER} || "quill.sfbay";
70
71# Get command-line arguments
72our($opt_c, $opt_i, $opt_o, $opt_l, $opt_q);
73if (!getopts("i:o:c:l:q")) {
74	die "Usage: $0 [-i dir] [-o dir] [-l user]\n";
75}
76$Quiet = $opt_q;
77
78# Get input/output directories
79$Indir = $opt_i || getcwd();	# default to current dir
80$Outdir = $opt_o || $Indir;	# default to input dir
81$Indir = getcwd() . "/$Indir" if (substr($Indir, 0, 1) ne "/");
82$Outdir = getcwd() . "/$Outdir" if (substr($Outdir, 0, 1) ne "/");
83
84# Ignore SIGPIPE to allow proper error messages
85$SIG{PIPE} = 'IGNORE';
86
87# Create ssh connection to server
88my(@args);
89if (defined($opt_l)) {
90	push @args, "-l", $opt_l;
91}
92push @args, "-s", $Server, "codesign";
93$pid = open2(*SRV_OUT, *SRV_IN, "/usr/bin/ssh", @args) or
94	die "ERROR Connection to server $Server failed\n";
95select(SRV_IN); $| = 1; select(STDOUT);	# unbuffered writes
96
97# Sign each file with the specified credential
98chdir($Indir);
99while (<>) {
100	my ($cred, $path) = split;
101
102	sign_file($cred, $path);
103}
104exit($Warnings > 0);
105
106#
107# END()
108#
109# Clean up after normal or abnormal exit.
110#
111sub END {
112	my $old_status = $?;
113
114	$? = 0;
115	close(SRV_IN);
116	close(SRV_OUT);
117	waitpid($pid, 0) if ($pid);
118	if ($?) {
119		print STDERR "ERROR Connection to server $Server failed\n";
120		$? = 1;
121	}
122	$? = $old_status if ($? == 0);
123}
124
125#
126# debug(msg)
127#
128# Print debug message to standard error.
129#
130sub debug {
131	print STDERR "### @_";
132}
133
134#
135# check_response(str)
136#
137# Validate response from server. Print messages for warnings or errors,
138# and exit in the case of an error. If the response indicates a successful
139# signing operation, return the size of the output data.
140#
141sub check_response {
142	my ($str) = @_;
143
144	if ($str =~ /^OK SIGN (\d+)/) {
145		return ($1);
146	}
147	elsif ($str =~ /^OK/) {
148		return (0);
149	}
150	elsif ($str =~ /^WARNING/) {
151		print STDERR $str;
152		$Warnings++;
153		return (-1);
154	}
155	elsif ($str =~ /^ERROR/) {
156		print STDERR $str;
157		exit(1);
158	}
159	else {
160		printf STDERR "ERROR Protocol failure (%d)\n", length($str);
161		exit(1);
162	}
163}
164
165#
166# sign_file(credential, filename)
167#
168# Send the file to the server for signing. Package the file into a
169# ZIP archive, send to the server, and extract the ZIP archive that
170# is returned. The input ZIP archive always contains a single file,
171# but the returned archive may contain one or more files.
172#
173sub sign_file {
174	my ($cred, $path) = @_;
175	my ($res, $size);
176
177	$path =~ s:^\./::g; # remove leading "./"
178	unlink("$Tmpdir/in.zip");
179	system("cd $Indir; /usr/bin/zip -q $Tmpdir/in.zip $path");
180
181	sendfile("$Tmpdir/in.zip", "$cred $path") || return;
182
183	$res = <SRV_OUT>;
184	$size = check_response($res);
185	if ($size > 0) {
186		recvfile("$Tmpdir/out.zip", $size) || return;
187
188		if (system("cd $Outdir; /usr/bin/unzip -qo $Tmpdir/out.zip")) {
189			$Warnings++;
190		} else {
191			print "$cred\t$path\n" unless $Quiet;
192		}
193	}
194}
195
196#
197# sendfile(file, args)
198#
199# Send a ZIP archive file to the signing server. This involves
200# sending a SIGN command with the given arguments, followed by
201# the contents of the archive itself.
202#
203sub sendfile {
204	my ($file, $args) = @_;
205	my ($size, $bytes);
206
207	$size = -s $file;
208	print SRV_IN "SIGN $size $args\n";
209	if (!open(F, "<$file")) {
210		print STDERR "$file: $!\n";
211		return (0);
212	}
213	read(F, $bytes, $size);
214	close(F);
215	if (!syswrite(SRV_IN, $bytes, $size)) {
216		print STDERR "Can't send to server: $!\n";
217		return (0);
218	}
219	return (1);
220}
221
222#
223# recvfile(file, size)
224#
225# Receive a ZIP archive from the signing server. The caller
226# provides the size argument previously obtained from the
227# server response.
228#
229sub recvfile {
230	my ($file, $size) = @_;
231	my $bytes;
232
233	if (!read(SRV_OUT, $bytes, $size)) {
234		print STDERR "Can't read from server: $!\n";
235		return (0);
236	}
237	if (!open(F, ">$file")) {
238		print STDERR "$file: $!\n";
239		return (0);
240	}
241	syswrite(F, $bytes, $size);
242	close(F);
243	return (1);
244}
245