8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / tools / codesign / codesign_server.pl
blob5d9999ebd94852b4444592f473cd3deef445dab9
1 #!/usr/perl5/bin/perl
3 # CDDL HEADER START
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]
20 # CDDL HEADER END
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.
49 use strict;
50 use File::Temp 'tempdir';
51 use File::Path;
53 my $Base = "/opt/signing";
54 my $Tmpdir = tempdir(CLEANUP => 1); # Temporary directory
55 my $Session = $$;
58 # Main program
61 # Set up
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`;
67 chomp($user);
68 my ($client) = split(/\s/, $ENV{SSH_CLIENT});
69 audit("START User=$user Client=$client");
71 # Process signing requests
72 while (<STDIN>) {
73 if (/^SIGN (\d+) (\S+) (\S+)/) {
74 sign($1, $2, $3);
75 } else {
76 abnormal("WARNING Unknown command");
79 exit(0);
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.
88 sub get_credential {
89 my $name = shift;
90 my $dir;
92 $dir = "$Base/cred/$2";
93 if (!open(F, "<$dir/private")) {
94 abnormal("WARNING Credential $name not available");
95 $dir = undef;
97 close(F);
98 return $dir;
102 # sign(size, cred, path)
104 # Sign an individual file.
106 sub sign {
107 my ($size, $cred, $path) = @_;
108 my ($cred_dir, $msg);
110 # Read input file
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");
118 return;
122 # Get credential directory
123 $cred_dir = get_credential($cred) || return;
125 # Create work area
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`;
135 if ($? != 0) {
136 chomp($msg);
137 abnormal("WARNING $msg");
138 return;
141 # Pack output file(s) in ZIP archive and return
142 unlink("../out.zip");
143 system("/usr/bin/zip -qr ../out.zip .");
144 chdir($Tmpdir);
145 my $hash = `digest -a md5 $Tmpdir/reloc/$path`;
146 sendfile("$Tmpdir/out.zip", $path) || return;
148 # Audit successful signing
149 chomp($hash);
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.
160 sub sendfile {
161 my ($file, $path) = @_;
162 my ($size, $bytes);
164 $size = -s $file;
165 if (!open(F, "<$file")) {
166 abnormal("ERROR Internal read error");
167 return (0);
169 read(F, $bytes, $size);
170 close(F);
171 print "OK SIGN $size $path\n";
172 syswrite(STDOUT, $bytes, $size);
173 return (1);
177 # recvfile(file, size)
179 # Receive a ZIP archive from the client. The caller
180 # provides the size argument previously obtained from the
181 # client request.
183 sub recvfile {
184 my ($file, $size) = @_;
185 my $bytes;
187 if (!read(STDIN, $bytes, $size)) {
188 abnormal("ERROR No input data");
189 return (0);
191 if (!open(F, ">$file")) {
192 abnormal("ERROR Internal write error");
193 return (0);
195 syswrite(F, $bytes, $size);
196 close(F);
197 return (1);
201 # audit(msg)
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.
207 sub audit {
208 my ($msg) = @_;
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";
217 # abnormal(msg)
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.
223 sub abnormal {
224 my $msg = shift;
226 audit($msg);
227 print("$msg\n");
228 exit(1) if ($msg =~ /^ERROR/);
232 # END()
234 # Clean up prior to normal or abnormal exit.
236 sub END {
237 audit("END");
238 close(AUDIT);
239 chdir(""); # so $Tmpdir can be removed