Home | History | Annotate | Download | only in codesign
      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 
     47 use strict;
     48 use Cwd;
     49 use File::Temp 'tempdir';
     50 use Getopt::Std;
     51 use IPC::Open2;
     52 
     53 #
     54 # Global variables
     55 #
     56 my ($Indir, $Outdir);	# Input and output directories (may be the same)
     57 my $Server;		# Signing server hostname
     58 my $Quiet;		# Suppress printing each file successfully signed
     59 my ($pid);		# Process id for ssh client
     60 my @cred_rules;		# Array of path prefixes and credentials to use
     61 my $Tmpdir = tempdir(CLEANUP => 1);	# Temporary directory
     62 my $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
     72 our($opt_c, $opt_i, $opt_o, $opt_l, $opt_q);
     73 if (!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
     88 my(@args);
     89 if (defined($opt_l)) {
     90 	push @args, "-l", $opt_l;
     91 }
     92 push @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";
     95 select(SRV_IN); $| = 1; select(STDOUT);	# unbuffered writes
     96 
     97 # Sign each file with the specified credential
     98 chdir($Indir);
     99 while (<>) {
    100 	my ($cred, $path) = split;
    101 
    102 	sign_file($cred, $path);
    103 }
    104 exit($Warnings > 0);
    105 
    106 #
    107 # END()
    108 #
    109 # Clean up after normal or abnormal exit.
    110 #
    111 sub 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 #
    130 sub 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 #
    141 sub 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 #
    173 sub 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 #
    203 sub 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 #
    229 sub 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