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 }