chiark / gitweb /
use libinn logging where applicable - compiles
[inn-innduct.git] / control / gpgverify.in
1 #!/usr/bin/perl -w
2 require '/etc/news/innshellvars.pl';
3
4 # written April 1996, tale@isc.org (David C Lawrence)
5 # mostly rewritten 2001-03-21 by Marco d'Itri <md@linux.it>
6 #
7 # requirements:
8 # - GnuPG
9 # - perl 5.004_03 and working Sys::Syslog
10 # - syslog daemon
11 #
12 # There is no locking because gpg is supposed to not need it and controlchan
13 # will serialize control messages processing anyway.
14
15 require 5.004_03;
16 use strict;
17
18 # if you keep your keyring somewhere that is not the default used by gpg,
19 # change the location below.
20 my $keyring;
21 if ($inn::newsetc && -d "$inn::newsetc/pgp") {
22   $keyring = $inn::newsetc . '/pgp/pubring.gpg';
23 }
24
25 # If you have INN and the script is able to successfully include your
26 # innshellvars.pl file, the value of the next two variables will be
27 # overridden.
28 my $tmpdir = '/var/log/news/';
29 my $syslog_facility = 'news';
30
31 # 1: print PGP output
32 my $debug = 0;
33 #$debug = 1 if -t 1;
34
35 ### Exit value:
36 ### 0  good signature
37 ### 1  no signature
38 ### 2  unknown signature
39 ### 3  bad signature
40 ### 255 problem not directly related to gpg analysis of signature
41
42 ##############################################################################
43 ################ NO USER SERVICEABLE PARTS BELOW THIS COMMENT ################
44 ##############################################################################
45 my $tmp = ($inn::pathtmp ? $inn::pathtmp : $tmpdir) . "/pgp$$";
46 $syslog_facility = $inn::syslog_facility if $inn::syslog_facility;
47
48 my $nntp_format = 0;
49 $0 =~ s#^.*/##;                # trim /path/to/prog to prog
50
51 die "Usage: $0 < message\n" if $#ARGV != -1;
52
53 # Path to gpg binary
54 my $gpg;
55 if ($inn::gpgv) { 
56     $gpg = $inn::gpgv;
57 } else {
58     foreach (split(/:/, $ENV{PATH}), qw(/usr/local/bin /opt/gnu/bin)) {
59         if (-x "$_/gpgv") {
60             $gpg = "$_/gpgv"; last;
61         }
62     }
63 }
64 fail('cannot find the gpgv binary') if not $gpg;
65
66 # this is, by design, case-sensitive with regards to the headers it checks.
67 # it's also insistent about the colon-space rule.
68 my ($label, $value, %dup, %header);
69 while (<STDIN>) {
70     # if a header line ends with \r\n, this article is in the encoding
71     # it would be in during an NNTP session. some article storage
72     # managers keep them this way for efficiency.
73     $nntp_format = /\r\n$/ if $. == 1;
74     s/\r?\n$//;
75
76     last if /^$/;
77     if (/^(\S+):[ \t](.+)/) {
78         ($label, $value) = ($1, $2);
79         $dup{$label} = 1 if $header{$label};
80         $header{$label} = $value;
81     } elsif (/^\s/) {
82         fail("non-header at line $.: $_") unless $label;
83         $header{$label} .= "\n$_";
84     } else {
85         fail("non-header at line $.: $_");
86     }
87 }
88
89 my $pgpheader = 'X-PGP-Sig';
90 $_ = $header{$pgpheader};
91 exit 1 if not $_; # no signature
92
93 # the $sep value means the separator between the radix64 signature lines
94 # can have any amount of spaces or tabs, but must have at least one space
95 # or tab, if there is a newline then the space or tab has to follow the
96 # newline. any number of newlines can appear as long as each is followed
97 # by at least one space or tab. *phew*
98 my $sep = "[ \t]*(\n?[ \t]+)+";
99 # match all of the characters in a radix64 string
100 my $r64 = '[a-zA-Z0-9+/]';
101 fail("$pgpheader not in expected format")
102     unless /^(\S+)$sep(\S+)(($sep$r64{64})+$sep$r64+=?=?$sep=$r64{4})$/;
103
104 my ($version, $signed_headers, $signature) = ($1, $3, $4);
105 $signature =~ s/$sep/\n/g;
106
107 my $message = "-----BEGIN PGP SIGNED MESSAGE-----\n\n"
108             . "X-Signed-Headers: $signed_headers\n";
109
110 foreach $label (split(',', $signed_headers)) {
111     fail("duplicate signed $label header, can't verify") if $dup{$label};
112     $message .= "$label: ";
113     $message .= $header{$label} if $header{$label};
114     $message .= "\n";
115 }
116 $message .= "\n";                # end of headers
117
118 while (<STDIN>) {                # read body lines
119     if ($nntp_format) {
120         # check for end of article; some news servers (eg, Highwind's
121         # "Breeze") include the dot-CRLF of the NNTP protocol in the
122         # article data passed to this script
123         last if $_ eq ".\r\n";
124
125         # remove NNTP encoding
126         s/^\.\./\./;
127         s/\r\n$/\n/;
128     }
129
130     s/^-/- -/;            # pgp quote ("ASCII armor") dashes
131     $message .= $_;    
132 }
133
134 $message .=
135     "\n-----BEGIN PGP SIGNATURE-----\n" .
136     "Version: $version\n" .
137     $signature .
138     "\n-----END PGP SIGNATURE-----\n";
139
140 open(TMP, ">$tmp") or fail("open $tmp: $!");
141 print TMP $message;
142 close TMP or errmsg("close $tmp: $!");
143
144 my $opts = '--quiet --status-fd=1 --logger-fd=1';
145 $opts .= " --keyring=$keyring" if $keyring;
146
147 open(PGP, "$gpg $opts $tmp |") or fail("failed to execute $gpg: $!");
148
149 undef $/;
150 $_ = <PGP>;
151
152 unlink $tmp or errmsg("unlink $tmp: $!");
153
154 if (not close PGP) {
155     if ($? >> 8) {
156         my $status = $? >> 8;
157         errmsg("gpg exited status $status") if $status > 1;
158     } else {
159         errmsg('gpg died on signal ' . ($? & 255));
160     }
161 }
162
163 print STDERR $_ if $debug;
164
165 my $ok = 255;        # default exit status
166 my $signer;
167 if (/^\[GNUPG:\]\s+GOODSIG\s+\S+\s+(\S+)/m) {
168     $ok = 0;
169     $signer = $1;
170 } elsif (/^\[GNUPG:\]\s+NODATA/m or /^\[GNUPG:\]\s+UNEXPECTED/m) {
171     $ok = 1;
172 } elsif (/^\[GNUPG:\]\s+NO_PUBKEY/m) {
173     $ok = 2;
174 } elsif (/^\[GNUPG:\]\s+BADSIG\s+/m) {
175     $ok = 3;
176 }
177
178 print "$signer\n" if $signer;
179 exit $ok;
180
181 sub errmsg {
182     my $msg = $_[0];
183
184     eval 'use Sys::Syslog qw(:DEFAULT setlogsock)';
185     die "$0: cannot use Sys::Syslog: $@ [$msg]\n" if $@;
186
187     die "$0: cannot set syslog method [$msg]\n"
188         if not (setlogsock('unix') or setlogsock('inet'));
189
190     $msg .= " processing $header{'Message-ID'}" if $header{'Message-ID'};
191
192     openlog($0, 'pid', $syslog_facility);
193     syslog('err', '%s', $msg);
194     closelog();
195 }
196
197 sub fail {
198     errmsg($_[0]);
199     unlink $tmp;
200     exit 255;
201 }
202
203 __END__
204
205 # Copyright 2000 by Marco d'Itri
206
207 # License of the original version distributed by David C. Lawrence:
208
209 # Copyright (c) 1996 UUNET Technologies, Inc.
210 # All rights reserved.
211 #
212 # Redistribution and use in source and binary forms, with or without
213 # modification, are permitted provided that the following conditions
214 # are met:
215 # 1. Redistributions of source code must retain the above copyright
216 #    notice, this list of conditions and the following disclaimer.
217 # 2. Redistributions in binary form must reproduce the above copyright
218 #    notice, this list of conditions and the following disclaimer in the
219 #    documentation and/or other materials provided with the distribution.
220 # 3. All advertising materials mentioning features or use of this software
221 #    must display the following acknowledgement:
222 #      This product includes software developed by UUNET Technologies, Inc.
223 # 4. The name of UUNET Technologies ("UUNET") may not be used to endorse or
224 #    promote products derived from this software without specific prior
225 #    written permission.
226 #
227 # THIS SOFTWARE IS PROVIDED BY UUNET ``AS IS'' AND ANY EXPRESS OR
228 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
229 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
230 # ARE DISCLAIMED.  IN NO EVENT SHALL UUNET BE LIABLE FOR ANY DIRECT,
231 # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
232 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
233 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
234 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
235 # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
236 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
237 # OF THE POSSIBILITY OF SUCH DAMAGE.