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.
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.
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]
23 # ident "%Z%%M% %I% %E% SMI"
25 # Copyright 2007 Sun Microsystems, Inc. All rights reserved.
26 # Use is subject to license terms.
29 # Server program for code signing server
31 # This program implements an ssh-based service to add digital
32 # signatures to files. The sshd_config file on the server
33 # contains an entry like the following to invoke this program:
35 # Subsystem codesign /opt/signing/bin/server
37 # The client program sends a ZIP archive of the file to be
38 # signed along with the name of a signing credential stored
39 # on the server. Each credential is a directory containing
40 # a public-key certificate, private key, and a script to
41 # perform the appropriate signing operation.
43 # This program unpacks the input ZIP archive, invokes the
44 # signing script for the specified credential, and sends
45 # back an output ZIP archive, which typically contains the
46 # (modified) input file but may also contain additional
47 # files created by the signing script.
50 use File
::Temp
'tempdir';
53 my $Base = "/opt/signing";
54 my $Tmpdir = tempdir
(CLEANUP
=> 1); # Temporary directory
62 open(AUDIT
, ">>$Base/audit/log");
63 $| = 1; # Flush output on every write
65 # Record user and client system
66 my $user = `/usr/ucb/whoami`;
68 my ($client) = split(/\s/, $ENV{SSH_CLIENT
});
69 audit
("START User=$user Client=$client");
71 # Process signing requests
73 if (/^SIGN (\d+) (\S+) (\S+)/) {
76 abnormal
("WARNING Unknown command");
82 # get_credential(name)
84 # Verify that the user is allowed to use the named credential and
85 # return the path to the credential directory. If the user is not
86 # authorized to use the credential, return undef.
92 $dir = "$Base/cred/$2";
93 if (!open(F
, "<$dir/private")) {
94 abnormal
("WARNING Credential $name not available");
102 # sign(size, cred, path)
104 # Sign an individual file.
107 my ($size, $cred, $path) = @_;
108 my ($cred_dir, $msg);
111 recvfile
("$Tmpdir/in.zip", $size) || return;
113 # Check path for use of .. or absolute pathname
114 my @comp = split(m
:/:, $path);
115 foreach my $elem (@comp) {
116 if ($elem eq "" || $elem eq "..") {
117 abnormal
("WARNING Invalid path $path");
122 # Get credential directory
123 $cred_dir = get_credential
($cred) || return;
126 rmtree
("$Tmpdir/reloc");
127 mkdir("$Tmpdir/reloc");
128 chdir("$Tmpdir/reloc");
130 # Read and unpack input ZIP archive
131 system("/usr/bin/unzip -qo ../in.zip $path");
133 # Sign input file using credential-specific script
134 $msg = `cd $cred_dir; ./sign $Tmpdir/reloc/$path`;
137 abnormal
("WARNING $msg");
141 # Pack output file(s) in ZIP archive and return
142 unlink("../out.zip");
143 system("/usr/bin/zip -qr ../out.zip .");
145 my $hash = `digest -a md5 $Tmpdir/reloc/$path`;
146 sendfile
("$Tmpdir/out.zip", $path) || return;
148 # Audit successful signing
150 audit
("SIGN $path $cred $hash");
154 # sendfile(file, path)
156 # Send a ZIP archive to the client. This involves sending
157 # an OK SIGN response that includes the file size, followed by
158 # the contents of the archive itself.
161 my ($file, $path) = @_;
165 if (!open(F
, "<$file")) {
166 abnormal
("ERROR Internal read error");
169 read(F
, $bytes, $size);
171 print "OK SIGN $size $path\n";
172 syswrite(STDOUT
, $bytes, $size);
177 # recvfile(file, size)
179 # Receive a ZIP archive from the client. The caller
180 # provides the size argument previously obtained from the
184 my ($file, $size) = @_;
187 if (!read(STDIN
, $bytes, $size)) {
188 abnormal
("ERROR No input data");
191 if (!open(F
, ">$file")) {
192 abnormal
("ERROR Internal write error");
195 syswrite(F
, $bytes, $size);
203 # Create an audit record. All records have this format:
204 # [date] [time] [session] [keyword] [other parameters]
205 # The keywords START and END mark the boundaries of a session.
209 my ($sec, $min, $hr, $day, $mon, $yr) = localtime(time);
210 my $timestamp = sprintf("%04d-%02d-%02d %02d:%02d:%02d",
211 $yr+1900, $mon+1, $day, $hr, $min, $sec);
213 print AUDIT
"$timestamp $Session $msg\n";
219 # Respond to an abnormal condition, which may be fatal (ERROR) or
220 # non-fatal (WARNING). Send the message to the audit error log
221 # and to the client program. Exit in case of fatal errors.
228 exit(1) if ($msg =~ /^ERROR/);
234 # Clean up prior to normal or abnormal exit.
239 chdir(""); # so $Tmpdir can be removed