23259b79afff8cc5e183c5be57e05120f378fa72rotondo#!/usr/perl5/bin/perl
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# CDDL HEADER START
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# The contents of this file are subject to the terms of the
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Common Development and Distribution License (the "License").
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# You may not use this file except in compliance with the License.
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# or http://www.opensolaris.org/os/licensing.
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# See the License for the specific language governing permissions
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# and limitations under the License.
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# When distributing Covered Code, include this CDDL HEADER in each
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# If applicable, add the following below this CDDL HEADER, with the
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# fields enclosed by brackets "[]" replaced with your own identifying
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# information: Portions Copyright [yyyy] [name of copyright owner]
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# CDDL HEADER END
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# ident "%Z%%M% %I% %E% SMI"
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Use is subject to license terms.
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# signit [-q] [-i dir][-o dir] [-l user]
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Client program for use with code signing server.
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Reads a list of signing credential names and file pathnames
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# from standard input. Each file is read from the input directory,
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# sent to the signing server, signed with the specified credential,
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# and written to the output directory.
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Options:
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# -q quiet operation: avoid printing files successfully signed
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# -i dir input directory (defaults to current dir)
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# -o dir output directory (defautls to input dir)
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# -l user user account on signing server (defaults to current user)
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# The CODESIGN_SERVER environment variable can be used to
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# specify the hostname or IP address of the signing server
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# (defaults to quill.sfbay).
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondouse strict;
23259b79afff8cc5e183c5be57e05120f378fa72rotondouse Cwd;
23259b79afff8cc5e183c5be57e05120f378fa72rotondouse File::Temp 'tempdir';
23259b79afff8cc5e183c5be57e05120f378fa72rotondouse Getopt::Std;
23259b79afff8cc5e183c5be57e05120f378fa72rotondouse IPC::Open2;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Global variables
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondomy ($Indir, $Outdir); # Input and output directories (may be the same)
23259b79afff8cc5e183c5be57e05120f378fa72rotondomy $Server; # Signing server hostname
23259b79afff8cc5e183c5be57e05120f378fa72rotondomy $Quiet; # Suppress printing each file successfully signed
23259b79afff8cc5e183c5be57e05120f378fa72rotondomy ($pid); # Process id for ssh client
23259b79afff8cc5e183c5be57e05120f378fa72rotondomy @cred_rules; # Array of path prefixes and credentials to use
23259b79afff8cc5e183c5be57e05120f378fa72rotondomy $Tmpdir = tempdir(CLEANUP => 1); # Temporary directory
23259b79afff8cc5e183c5be57e05120f378fa72rotondomy $Warnings = 0; # Count of warnings returned
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Main program
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo$Server = $ENV{CODESIGN_SERVER} || "quill.sfbay";
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Get command-line arguments
23259b79afff8cc5e183c5be57e05120f378fa72rotondoour($opt_c, $opt_i, $opt_o, $opt_l, $opt_q);
23259b79afff8cc5e183c5be57e05120f378fa72rotondoif (!getopts("i:o:c:l:q")) {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo die "Usage: $0 [-i dir] [-o dir] [-l user]\n";
23259b79afff8cc5e183c5be57e05120f378fa72rotondo}
23259b79afff8cc5e183c5be57e05120f378fa72rotondo$Quiet = $opt_q;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Get input/output directories
23259b79afff8cc5e183c5be57e05120f378fa72rotondo$Indir = $opt_i || getcwd(); # default to current dir
23259b79afff8cc5e183c5be57e05120f378fa72rotondo$Outdir = $opt_o || $Indir; # default to input dir
23259b79afff8cc5e183c5be57e05120f378fa72rotondo$Indir = getcwd() . "/$Indir" if (substr($Indir, 0, 1) ne "/");
23259b79afff8cc5e183c5be57e05120f378fa72rotondo$Outdir = getcwd() . "/$Outdir" if (substr($Outdir, 0, 1) ne "/");
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
2210853d176a7da9835ef47df74fd66c3c1a3e55johnz# Ignore SIGPIPE to allow proper error messages
2210853d176a7da9835ef47df74fd66c3c1a3e55johnz$SIG{PIPE} = 'IGNORE';
2210853d176a7da9835ef47df74fd66c3c1a3e55johnz
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Create ssh connection to server
23259b79afff8cc5e183c5be57e05120f378fa72rotondomy(@args);
23259b79afff8cc5e183c5be57e05120f378fa72rotondoif (defined($opt_l)) {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo push @args, "-l", $opt_l;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo}
23259b79afff8cc5e183c5be57e05120f378fa72rotondopush @args, "-s", $Server, "codesign";
23259b79afff8cc5e183c5be57e05120f378fa72rotondo$pid = open2(*SRV_OUT, *SRV_IN, "/usr/bin/ssh", @args) or
2210853d176a7da9835ef47df74fd66c3c1a3e55johnz die "ERROR Connection to server $Server failed\n";
23259b79afff8cc5e183c5be57e05120f378fa72rotondoselect(SRV_IN); $| = 1; select(STDOUT); # unbuffered writes
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Sign each file with the specified credential
23259b79afff8cc5e183c5be57e05120f378fa72rotondochdir($Indir);
23259b79afff8cc5e183c5be57e05120f378fa72rotondowhile (<>) {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo my ($cred, $path) = split;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo sign_file($cred, $path);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo}
23259b79afff8cc5e183c5be57e05120f378fa72rotondoexit($Warnings > 0);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# END()
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Clean up after normal or abnormal exit.
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondosub END {
2210853d176a7da9835ef47df74fd66c3c1a3e55johnz my $old_status = $?;
2210853d176a7da9835ef47df74fd66c3c1a3e55johnz
2210853d176a7da9835ef47df74fd66c3c1a3e55johnz $? = 0;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo close(SRV_IN);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo close(SRV_OUT);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo waitpid($pid, 0) if ($pid);
2210853d176a7da9835ef47df74fd66c3c1a3e55johnz if ($?) {
2210853d176a7da9835ef47df74fd66c3c1a3e55johnz print STDERR "ERROR Connection to server $Server failed\n";
2210853d176a7da9835ef47df74fd66c3c1a3e55johnz $? = 1;
2210853d176a7da9835ef47df74fd66c3c1a3e55johnz }
2210853d176a7da9835ef47df74fd66c3c1a3e55johnz $? = $old_status if ($? == 0);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo}
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# debug(msg)
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Print debug message to standard error.
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondosub debug {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo print STDERR "### @_";
23259b79afff8cc5e183c5be57e05120f378fa72rotondo}
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# check_response(str)
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Validate response from server. Print messages for warnings or errors,
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# and exit in the case of an error. If the response indicates a successful
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# signing operation, return the size of the output data.
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondosub check_response {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo my ($str) = @_;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo if ($str =~ /^OK SIGN (\d+)/) {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo return ($1);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo }
23259b79afff8cc5e183c5be57e05120f378fa72rotondo elsif ($str =~ /^OK/) {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo return (0);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo }
23259b79afff8cc5e183c5be57e05120f378fa72rotondo elsif ($str =~ /^WARNING/) {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo print STDERR $str;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo $Warnings++;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo return (-1);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo }
23259b79afff8cc5e183c5be57e05120f378fa72rotondo elsif ($str =~ /^ERROR/) {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo print STDERR $str;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo exit(1);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo }
23259b79afff8cc5e183c5be57e05120f378fa72rotondo else {
2210853d176a7da9835ef47df74fd66c3c1a3e55johnz printf STDERR "ERROR Protocol failure (%d)\n", length($str);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo exit(1);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo }
23259b79afff8cc5e183c5be57e05120f378fa72rotondo}
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# sign_file(credential, filename)
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Send the file to the server for signing. Package the file into a
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# ZIP archive, send to the server, and extract the ZIP archive that
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# is returned. The input ZIP archive always contains a single file,
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# but the returned archive may contain one or more files.
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondosub sign_file {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo my ($cred, $path) = @_;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo my ($res, $size);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo $path =~ s:^\./::g; # remove leading "./"
23259b79afff8cc5e183c5be57e05120f378fa72rotondo unlink("$Tmpdir/in.zip");
23259b79afff8cc5e183c5be57e05120f378fa72rotondo system("cd $Indir; /usr/bin/zip -q $Tmpdir/in.zip $path");
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo sendfile("$Tmpdir/in.zip", "$cred $path") || return;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo $res = <SRV_OUT>;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo $size = check_response($res);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo if ($size > 0) {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo recvfile("$Tmpdir/out.zip", $size) || return;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo if (system("cd $Outdir; /usr/bin/unzip -qo $Tmpdir/out.zip")) {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo $Warnings++;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo } else {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo print "$cred\t$path\n" unless $Quiet;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo }
23259b79afff8cc5e183c5be57e05120f378fa72rotondo }
23259b79afff8cc5e183c5be57e05120f378fa72rotondo}
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# sendfile(file, args)
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Send a ZIP archive file to the signing server. This involves
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# sending a SIGN command with the given arguments, followed by
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# the contents of the archive itself.
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondosub sendfile {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo my ($file, $args) = @_;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo my ($size, $bytes);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo $size = -s $file;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo print SRV_IN "SIGN $size $args\n";
23259b79afff8cc5e183c5be57e05120f378fa72rotondo if (!open(F, "<$file")) {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo print STDERR "$file: $!\n";
23259b79afff8cc5e183c5be57e05120f378fa72rotondo return (0);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo }
23259b79afff8cc5e183c5be57e05120f378fa72rotondo read(F, $bytes, $size);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo close(F);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo if (!syswrite(SRV_IN, $bytes, $size)) {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo print STDERR "Can't send to server: $!\n";
23259b79afff8cc5e183c5be57e05120f378fa72rotondo return (0);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo }
23259b79afff8cc5e183c5be57e05120f378fa72rotondo return (1);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo}
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# recvfile(file, size)
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# Receive a ZIP archive from the signing server. The caller
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# provides the size argument previously obtained from the
23259b79afff8cc5e183c5be57e05120f378fa72rotondo# server response.
23259b79afff8cc5e183c5be57e05120f378fa72rotondo#
23259b79afff8cc5e183c5be57e05120f378fa72rotondosub recvfile {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo my ($file, $size) = @_;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo my $bytes;
23259b79afff8cc5e183c5be57e05120f378fa72rotondo
23259b79afff8cc5e183c5be57e05120f378fa72rotondo if (!read(SRV_OUT, $bytes, $size)) {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo print STDERR "Can't read from server: $!\n";
23259b79afff8cc5e183c5be57e05120f378fa72rotondo return (0);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo }
23259b79afff8cc5e183c5be57e05120f378fa72rotondo if (!open(F, ">$file")) {
23259b79afff8cc5e183c5be57e05120f378fa72rotondo print STDERR "$file: $!\n";
23259b79afff8cc5e183c5be57e05120f378fa72rotondo return (0);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo }
23259b79afff8cc5e183c5be57e05120f378fa72rotondo syswrite(F, $bytes, $size);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo close(F);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo return (1);
23259b79afff8cc5e183c5be57e05120f378fa72rotondo}