# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # SHFILES= \ findcrypto \ signproto PERLFILES= \ signit MAN1ONBLDFILES= \ signit.1onbld \ signproto.1onbld CLEANFILES = $(SHFILES) $(PERLFILES) include ../Makefile.tools $(ROOTONBLDMAN1ONBLDFILES) : FILEMODE= 644 .KEEP_STATE: all: $(SHFILES) $(PERLFILES) $(MAN1ONBLDFILES) install: all .WAIT $(ROOTONBLDSHFILES) $(ROOTONBLDPERLFILES) \ $(ROOTONBLDMAN1ONBLDFILES) clean: $(RM) $(CLEANFILES) include ../Makefile.targ #!/usr/bin/perl # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # ident "%Z%%M% %I% %E% SMI" # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Server program for code signing server # # This program implements an ssh-based service to add digital # signatures to files. The sshd_config file on the server # contains an entry like the following to invoke this program: # # Subsystem codesign /opt/signing/bin/server # # The client program sends a ZIP archive of the file to be # signed along with the name of a signing credential stored # on the server. Each credential is a directory containing # a public-key certificate, private key, and a script to # perform the appropriate signing operation. # # This program unpacks the input ZIP archive, invokes the # signing script for the specified credential, and sends # back an output ZIP archive, which typically contains the # (modified) input file but may also contain additional # files created by the signing script. use strict; use File::Temp 'tempdir'; use File::Path; my $Base = "/opt/signing"; my $Tmpdir = tempdir(CLEANUP => 1); # Temporary directory my $Session = $$; # # Main program # # Set up open(AUDIT, ">>$Base/audit/log"); $| = 1; # Flush output on every write # Record user and client system my $user = `/usr/ucb/whoami`; chomp($user); my ($client) = split(/\s/, $ENV{SSH_CLIENT}); audit("START User=$user Client=$client"); # Process signing requests while () { if (/^SIGN (\d+) (\S+) (\S+)/) { sign($1, $2, $3); } else { abnormal("WARNING Unknown command"); } } exit(0); # # get_credential(name) # # Verify that the user is allowed to use the named credential and # return the path to the credential directory. If the user is not # authorized to use the credential, return undef. # sub get_credential { my $name = shift; my $dir; $dir = "$Base/cred/$2"; if (!open(F, "<$dir/private")) { abnormal("WARNING Credential $name not available"); $dir = undef; } close(F); return $dir; } # # sign(size, cred, path) # # Sign an individual file. # sub sign { my ($size, $cred, $path) = @_; my ($cred_dir, $msg); # Read input file recvfile("$Tmpdir/in.zip", $size) || return; # Check path for use of .. or absolute pathname my @comp = split(m:/:, $path); foreach my $elem (@comp) { if ($elem eq "" || $elem eq "..") { abnormal("WARNING Invalid path $path"); return; } } # Get credential directory $cred_dir = get_credential($cred) || return; # Create work area rmtree("$Tmpdir/reloc"); mkdir("$Tmpdir/reloc"); chdir("$Tmpdir/reloc"); # Read and unpack input ZIP archive system("/usr/bin/unzip -qo ../in.zip $path"); # Sign input file using credential-specific script $msg = `cd $cred_dir; ./sign $Tmpdir/reloc/$path`; if ($? != 0) { chomp($msg); abnormal("WARNING $msg"); return; } # Pack output file(s) in ZIP archive and return unlink("../out.zip"); system("/usr/bin/zip -qr ../out.zip ."); chdir($Tmpdir); my $hash = `digest -a md5 $Tmpdir/reloc/$path`; sendfile("$Tmpdir/out.zip", $path) || return; # Audit successful signing chomp($hash); audit("SIGN $path $cred $hash"); } # # sendfile(file, path) # # Send a ZIP archive to the client. This involves sending # an OK SIGN response that includes the file size, followed by # the contents of the archive itself. # sub sendfile { my ($file, $path) = @_; my ($size, $bytes); $size = -s $file; if (!open(F, "<$file")) { abnormal("ERROR Internal read error"); return (0); } read(F, $bytes, $size); close(F); print "OK SIGN $size $path\n"; syswrite(STDOUT, $bytes, $size); return (1); } # # recvfile(file, size) # # Receive a ZIP archive from the client. The caller # provides the size argument previously obtained from the # client request. # sub recvfile { my ($file, $size) = @_; my $bytes; if (!read(STDIN, $bytes, $size)) { abnormal("ERROR No input data"); return (0); } if (!open(F, ">$file")) { abnormal("ERROR Internal write error"); return (0); } syswrite(F, $bytes, $size); close(F); return (1); } # # audit(msg) # # Create an audit record. All records have this format: # [date] [time] [session] [keyword] [other parameters] # The keywords START and END mark the boundaries of a session. # sub audit { my ($msg) = @_; my ($sec, $min, $hr, $day, $mon, $yr) = localtime(time); my $timestamp = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $yr+1900, $mon+1, $day, $hr, $min, $sec); print AUDIT "$timestamp $Session $msg\n"; } # # abnormal(msg) # # Respond to an abnormal condition, which may be fatal (ERROR) or # non-fatal (WARNING). Send the message to the audit error log # and to the client program. Exit in case of fatal errors. # sub abnormal { my $msg = shift; audit($msg); print("$msg\n"); exit(1) if ($msg =~ /^ERROR/); } # # END() # # Clean up prior to normal or abnormal exit. # sub END { audit("END"); close(AUDIT); chdir(""); # so $Tmpdir can be removed } # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. # # The following is a list of regular expressions that are matched against # the (temporary) signature on a crypto module created during the build # process. The first regular expression that matches is used to select the # signing credential to use for the file. # # Credential name Regular expression # --------------- ------------------ Crypto_v2 Solaris Cryptographic Framework #!/bin/ksh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # findcrypto cred_file # # Utility to find cryptographic modules in the proto area. Prints out # one line for each binary, using the form # # cred path # # where "path" identifies the binary (relative to $ROOT), and "cred" # says how the binary should get signed. # # The cred_file argument is the same as for signproto.sh. # # Directories in proto area that may contain crypto objects DIRS="platform kernel usr/lib/security" # Read list of credentials and regular expressions n=0 grep -v "^#" $1 | while read c r do cred[$n]=$c regex[$n]=$r (( n = n + 1 )) done # Search proto area for crypto modules cd $ROOT find $DIRS -type f -print | while read f; do s=`elfsign list -f signer -e $f 2>/dev/null` if [[ $? != 0 ]]; then continue fi # Determine credential based on signature i=0 while [[ i -lt n ]]; do if expr "$s" : ".*${regex[i]}" >/dev/null; then echo "${cred[i]} $f" break fi (( i = i + 1 )) done done exit 0 .\" .\" CDDL HEADER START .\" .\" The contents of this file are subject to the terms of the .\" Common Development and Distribution License (the "License"). .\" You may not use this file except in compliance with the License. .\" .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE .\" or http://www.opensolaris.org/os/licensing. .\" See the License for the specific language governing permissions .\" and limitations under the License. .\" .\" When distributing Covered Code, include this CDDL HEADER in each .\" file and include the License file at usr/src/OPENSOLARIS.LICENSE. .\" If applicable, add the following below this CDDL HEADER, with the .\" fields enclosed by brackets "[]" replaced with your own identifying .\" information: Portions Copyright [yyyy] [name of copyright owner] .\" .\" CDDL HEADER END .\" .\" Copyright 2007 Sun Microsystems, Inc. All rights reserved. .\" Use is subject to license terms. .\" .TH SIGNIT 1ONBLD "Jun 13, 2007" .SH NAME .I signit \- sign files using code signing server .SH SYNOPSIS \fBsignit [-q] [-i dir] [-o dir] [-l user]\fP .SH DESCRIPTION .LP .I signit is the client program for use with Sun's code signing server. It reads a list of signing credential names and relative pathnames from standard input. Each file is read from the input directory, sent to the signing server, signed with the specified credential, and written to the output directory. .SH OPTIONS .LP The following options are supported: .TP 4 .B \-q Quiet operation: This option suppresses printing the names of files that are signed successfully. .TP 4 .B -i \fIdir\fP Specifies the base input directory from which the relative pathnames of files to be signed are interpreted. If not specified, the input directory defaults to the current directory. .TP 4 .B -o \fIdir\fP Specifies the base output directory to which signed files are written. If not specified, the output directory matches the input directory. .TP 4 .B -l \fIuser\fP Specifies the user login name on the code signing server. If not specified, the login name of the user running .I signit is used. .SH ENVIRONMENT .TP 4 .B CODESIGN_SERVER Specifies the hostname or IP address of the code signing server. If this variable is not set, it defaults to quill.sfbay. .SH EXIT STATUS .LP The following exit status values are returned: .IP "\fB0\fR" 4 All specified files were signed successfully. .IP "\fB1\fR" 4 One or more files were not signed successfully. .SH SEE ALSO .LP signproto(1ONBLD) #!/usr/bin/perl # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # ident "%Z%%M% %I% %E% SMI" # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # signit [-q] [-i dir][-o dir] [-l user] # # Client program for use with code signing server. # Reads a list of signing credential names and file pathnames # from standard input. Each file is read from the input directory, # sent to the signing server, signed with the specified credential, # and written to the output directory. # # Options: # -q quiet operation: avoid printing files successfully signed # -i dir input directory (defaults to current dir) # -o dir output directory (defautls to input dir) # -l user user account on signing server (defaults to current user) # # The CODESIGN_SERVER environment variable can be used to # specify the hostname or IP address of the signing server # (defaults to quill.sfbay). use strict; use Cwd; use File::Temp 'tempdir'; use Getopt::Std; use IPC::Open2; # # Global variables # my ($Indir, $Outdir); # Input and output directories (may be the same) my $Server; # Signing server hostname my $Quiet; # Suppress printing each file successfully signed my ($pid); # Process id for ssh client my @cred_rules; # Array of path prefixes and credentials to use my $Tmpdir = tempdir(CLEANUP => 1); # Temporary directory my $Warnings = 0; # Count of warnings returned # # Main program # $Server = $ENV{CODESIGN_SERVER} || "quill.sfbay"; # Get command-line arguments our($opt_c, $opt_i, $opt_o, $opt_l, $opt_q); if (!getopts("i:o:c:l:q")) { die "Usage: $0 [-i dir] [-o dir] [-l user]\n"; } $Quiet = $opt_q; # Get input/output directories $Indir = $opt_i || getcwd(); # default to current dir $Outdir = $opt_o || $Indir; # default to input dir $Indir = getcwd() . "/$Indir" if (substr($Indir, 0, 1) ne "/"); $Outdir = getcwd() . "/$Outdir" if (substr($Outdir, 0, 1) ne "/"); # Ignore SIGPIPE to allow proper error messages $SIG{PIPE} = 'IGNORE'; # Create ssh connection to server my(@args); if (defined($opt_l)) { push @args, "-l", $opt_l; } push @args, "-s", $Server, "codesign"; $pid = open2(*SRV_OUT, *SRV_IN, "/usr/bin/ssh", @args) or die "ERROR Connection to server $Server failed\n"; select(SRV_IN); $| = 1; select(STDOUT); # unbuffered writes # Sign each file with the specified credential chdir($Indir); while (<>) { my ($cred, $path) = split; sign_file($cred, $path); } exit($Warnings > 0); # # END() # # Clean up after normal or abnormal exit. # sub END { my $old_status = $?; $? = 0; close(SRV_IN); close(SRV_OUT); waitpid($pid, 0) if ($pid); if ($?) { print STDERR "ERROR Connection to server $Server failed\n"; $? = 1; } $? = $old_status if ($? == 0); } # # debug(msg) # # Print debug message to standard error. # sub debug { print STDERR "### @_"; } # # check_response(str) # # Validate response from server. Print messages for warnings or errors, # and exit in the case of an error. If the response indicates a successful # signing operation, return the size of the output data. # sub check_response { my ($str) = @_; if ($str =~ /^OK SIGN (\d+)/) { return ($1); } elsif ($str =~ /^OK/) { return (0); } elsif ($str =~ /^WARNING/) { print STDERR $str; $Warnings++; return (-1); } elsif ($str =~ /^ERROR/) { print STDERR $str; exit(1); } else { printf STDERR "ERROR Protocol failure (%d)\n", length($str); exit(1); } } # # sign_file(credential, filename) # # Send the file to the server for signing. Package the file into a # ZIP archive, send to the server, and extract the ZIP archive that # is returned. The input ZIP archive always contains a single file, # but the returned archive may contain one or more files. # sub sign_file { my ($cred, $path) = @_; my ($res, $size); $path =~ s:^\./::g; # remove leading "./" unlink("$Tmpdir/in.zip"); system("cd $Indir; /usr/bin/zip -q $Tmpdir/in.zip $path"); sendfile("$Tmpdir/in.zip", "$cred $path") || return; $res = ; $size = check_response($res); if ($size > 0) { recvfile("$Tmpdir/out.zip", $size) || return; if (system("cd $Outdir; /usr/bin/unzip -qo $Tmpdir/out.zip")) { $Warnings++; } else { print "$cred\t$path\n" unless $Quiet; } } } # # sendfile(file, args) # # Send a ZIP archive file to the signing server. This involves # sending a SIGN command with the given arguments, followed by # the contents of the archive itself. # sub sendfile { my ($file, $args) = @_; my ($size, $bytes); $size = -s $file; print SRV_IN "SIGN $size $args\n"; if (!open(F, "<$file")) { print STDERR "$file: $!\n"; return (0); } read(F, $bytes, $size); close(F); if (!syswrite(SRV_IN, $bytes, $size)) { print STDERR "Can't send to server: $!\n"; return (0); } return (1); } # # recvfile(file, size) # # Receive a ZIP archive from the signing server. The caller # provides the size argument previously obtained from the # server response. # sub recvfile { my ($file, $size) = @_; my $bytes; if (!read(SRV_OUT, $bytes, $size)) { print STDERR "Can't read from server: $!\n"; return (0); } if (!open(F, ">$file")) { print STDERR "$file: $!\n"; return (0); } syswrite(F, $bytes, $size); close(F); return (1); } .\" .\" CDDL HEADER START .\" .\" The contents of this file are subject to the terms of the .\" Common Development and Distribution License (the "License"). .\" You may not use this file except in compliance with the License. .\" .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE .\" or http://www.opensolaris.org/os/licensing. .\" See the License for the specific language governing permissions .\" and limitations under the License. .\" .\" When distributing Covered Code, include this CDDL HEADER in each .\" file and include the License file at usr/src/OPENSOLARIS.LICENSE. .\" If applicable, add the following below this CDDL HEADER, with the .\" fields enclosed by brackets "[]" replaced with your own identifying .\" information: Portions Copyright [yyyy] [name of copyright owner] .\" .\" CDDL HEADER END .\" .\" Copyright 2007 Sun Microsystems, Inc. All rights reserved. .\" Use is subject to license terms. .\" .TH SIGNPROTO 1ONBLD "Jun 13, 2007" .SH NAME .I signproto \- sign ELF objects in proto area .SH SYNOPSIS \fBsignproto \fIcred_file\fP .SH DESCRIPTION .LP .I signproto finds ELF objects in the ON proto area and re-signs them using .IR signit (1ONBLD). This operation is normally invoked only for release builds, as it replaces the internal development signatures with official Sun signatures. The actual signing using Sun's private key is performed by a code signing server which is accessed via .IR signit . .LP Cryptographic modules are identified by examining the signature embedded by .IR elfsign (1) during the build process. .I signproto requires a single command-line argument, which is a file containing the mapping between each signing server credential name and the Subject Distinguished Name (DN) of the certificate used to sign the ELF file. Each line in the file contains a credential name followed by a regular expression. The first regular expression that matches the Subject DN embedded in the ELF file determines the credential name passed to .I signit to re-sign the file. .SH ENVIRONMENT .TP 4 .B CODESIGN_USER Login name for the code signing server passed to .I signit . If this variable is not set, the value in LOGNAME is used instead. .TP 4 .B ROOT Location of ON proto area containing files to be signed. .SH SEE ALSO .LP signit(1ONBLD), elfsign(1) #!/bin/ksh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # signproto cred_file # # Utility to find cryptographic modules in the proto area and # sign them using signit. Since the binaries have already been # signed (using development keys) during the build process, # we determine the correct signing credential to use based on # the existing signature. The cred_file argument contains a # list of signing server credentials and the corresponding # regular expressions to match against the file signatures. # Get absolute path of current directory; used later to invoke signit cd . dir=`dirname $0` dir=`[[ $dir = /* ]] && print $dir || print $PWD/$dir` findcrypto $1 | $dir/signit -i $ROOT -l ${CODESIGN_USER:-${LOGNAME}} stat=$? if [ $stat != 0 ]; then echo "ERROR failure in signing operation" exit $stat fi exit 0