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# Create ssh connection to server 85my(@args); 86if (defined($opt_l)) { 87 push @args, "-l", $opt_l; 88} 89push @args, "-s", $Server, "codesign"; 90$pid = open2(*SRV_OUT, *SRV_IN, "/usr/bin/ssh", @args) or 91 die "Can't start server\n"; 92select(SRV_IN); $| = 1; select(STDOUT); # unbuffered writes 93 94# Sign each file with the specified credential 95chdir($Indir); 96while (<>) { 97 my ($cred, $path) = split; 98 99 sign_file($cred, $path); 100} 101exit($Warnings > 0); 102 103# 104# END() 105# 106# Clean up after normal or abnormal exit. 107# 108sub END { 109 close(SRV_IN); 110 close(SRV_OUT); 111 waitpid($pid, 0) if ($pid); 112} 113 114# 115# debug(msg) 116# 117# Print debug message to standard error. 118# 119sub debug { 120 print STDERR "### @_"; 121} 122 123# 124# check_response(str) 125# 126# Validate response from server. Print messages for warnings or errors, 127# and exit in the case of an error. If the response indicates a successful 128# signing operation, return the size of the output data. 129# 130sub check_response { 131 my ($str) = @_; 132 133 if ($str =~ /^OK SIGN (\d+)/) { 134 return ($1); 135 } 136 elsif ($str =~ /^OK/) { 137 return (0); 138 } 139 elsif ($str =~ /^WARNING/) { 140 print STDERR $str; 141 $Warnings++; 142 return (-1); 143 } 144 elsif ($str =~ /^ERROR/) { 145 print STDERR $str; 146 exit(1); 147 } 148 else { 149 print STDERR "Unrecognized response\n"; 150 exit(1); 151 } 152} 153 154# 155# sign_file(credential, filename) 156# 157# Send the file to the server for signing. Package the file into a 158# ZIP archive, send to the server, and extract the ZIP archive that 159# is returned. The input ZIP archive always contains a single file, 160# but the returned archive may contain one or more files. 161# 162sub sign_file { 163 my ($cred, $path) = @_; 164 my ($res, $size); 165 166 $path =~ s:^\./::g; # remove leading "./" 167 unlink("$Tmpdir/in.zip"); 168 system("cd $Indir; /usr/bin/zip -q $Tmpdir/in.zip $path"); 169 170 sendfile("$Tmpdir/in.zip", "$cred $path") || return; 171 172 $res = <SRV_OUT>; 173 $size = check_response($res); 174 if ($size > 0) { 175 recvfile("$Tmpdir/out.zip", $size) || return; 176 177 if (system("cd $Outdir; /usr/bin/unzip -qo $Tmpdir/out.zip")) { 178 $Warnings++; 179 } else { 180 print "$cred\t$path\n" unless $Quiet; 181 } 182 } 183} 184 185# 186# sendfile(file, args) 187# 188# Send a ZIP archive file to the signing server. This involves 189# sending a SIGN command with the given arguments, followed by 190# the contents of the archive itself. 191# 192sub sendfile { 193 my ($file, $args) = @_; 194 my ($size, $bytes); 195 196 $size = -s $file; 197 print SRV_IN "SIGN $size $args\n"; 198 if (!open(F, "<$file")) { 199 print STDERR "$file: $!\n"; 200 return (0); 201 } 202 read(F, $bytes, $size); 203 close(F); 204 if (!syswrite(SRV_IN, $bytes, $size)) { 205 print STDERR "Can't send to server: $!\n"; 206 return (0); 207 } 208 return (1); 209} 210 211# 212# recvfile(file, size) 213# 214# Receive a ZIP archive from the signing server. The caller 215# provides the size argument previously obtained from the 216# server response. 217# 218sub recvfile { 219 my ($file, $size) = @_; 220 my $bytes; 221 222 if (!read(SRV_OUT, $bytes, $size)) { 223 print STDERR "Can't read from server: $!\n"; 224 return (0); 225 } 226 if (!open(F, ">$file")) { 227 print STDERR "$file: $!\n"; 228 return (0); 229 } 230 syswrite(F, $bytes, $size); 231 close(F); 232 return (1); 233} 234