4 ### Check that the certificate authority database and files are consistent.
6 ### (c) 2011 Mark Wooding
9 ###----- Licensing notice ---------------------------------------------------
11 ### This program is free software; you can redistribute it and/or modify
12 ### it under the terms of the GNU General Public License as published by
13 ### the Free Software Foundation; either version 2 of the License, or
14 ### (at your option) any later version.
16 ### This program is distributed in the hope that it will be useful,
17 ### but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ### GNU General Public License for more details.
21 ### You should have received a copy of the GNU General Public License
22 ### along with this program; if not, write to the Free Software Foundation,
23 ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 ## Find the common utilities.
26 source [file join [file dirname $argv0] "../lib/func.tcl"]
29 sqlite3 db "$CERTROOT/state/ca.db"
32 ## Build a map of the active requests. Verify that active requests have
36 foreach {tag id} [db eval {
37 SELECT tag, id FROM request WHERE st = 'active';
39 if {[info exists actreq($tag)] && ![info exists complain(dup-req-$tag)]} {
40 bad 4 "multiple active requests with tag `$tag' ($id and $actreq($tag))"
41 set complain(dup-req-$tag) 1
47 ## Go through the active certificates. Each one should tie up to an active
48 ## request. We don't check here that the request exists at all: that gets
51 foreach {seq tag st} [db eval {
52 SELECT c.seq, r.tag, r.st
53 FROM certificate AS c JOIN request AS r ON c.req = r.id
54 WHERE c.st = 'active';
56 if {[info exists actcert($tag)] &&
57 ![info exists complain(dup-cert-$tag)]} {
59 "multiple active certificates with "
60 "tag `$tag' ($seq and $actcert($tag))"} ""]
63 set actcert($tag) $seq
64 if {![string equal $st active]} {
66 "active cert $seq associated with "
67 "request $id (`$tag') which is $st not active"} ""]
71 ## Check that the certificates for a revoked request are revoked or
73 foreach {seq id tag st} [db eval {
74 SELECT c.seq, r.id, r.tag, c.st
75 FROM certificate AS c JOIN request AS r ON c.req = r.id
76 WHERE r.st = 'revoked' AND c.st != 'revoked' AND c.st != 'expired';
78 bad 4 "cert $seq for revoked request $id (`$tag') is $st not revoked"
81 ## Similarly, check that revoked certificates match up with revoked
83 foreach {seq id tag st} [db eval {
84 SELECT c.seq, r.id, r.tag, c.st
85 FROM certificate AS c JOIN request AS r ON c.req = r.id
86 WHERE c.st = 'revoked' AND r.st != 'revoked';
89 "revoked cert $seq associated with "
90 "request $id (`$tag') which is $st not revoked"} ""]
93 ## Check that the active symlinks are correct.
94 foreach {what key dir actvar} {
95 "request" "id" "req" actreq
96 "certificate" "seq" "cert" actcert
100 ## Check that there's a symlink DIR/active/TAG for each active item, and
101 ## that it points to the correct item.
102 foreach tag [array names act] {
103 set link "$CERTROOT/$dir/active/$tag"
105 if {![file exists $link]} {
106 bad 1 "missing symlink for active $what `$tag' ($key = $id)"
107 } elseif {![string equal [file type $link] link]} {
108 bad 1 "entry for active $what `$tag' ($key = $id) isn't a link"
109 } elseif {![string equal [file readlink $link] "../by-$key/$id"]} {
110 bad 1 "link for active $what `$tag' ($key = $id) is wrong"
111 moan "\t(actually `[file readlink $link]'; should be `../by-$key/$id')"
115 ## Check that there aren't any other stray things.
117 [glob -tails -directory "$CERTROOT/$dir/active" -nocomplain *] {
118 if {![info exists act($tag)]} {
119 bad 1 "bogus file `$dir/active/$tag'"
124 ## Now run through all of the requests and check that they match the
125 ## corresponding request files.
127 foreach {id tag st dn hash} [db eval {
128 SELECT id, tag, st, dn, hash FROM request;
130 if {[info exists reqmap($id)]} {
131 bad 4 "duplicate request id $id"
136 switch -exact -- $st {
137 active - withdrawn - revoked { }
139 bad 2 "request $id (`$tag') has unknown state `$st'"
143 set reqfile "$CERTROOT/req/by-id/$id"
144 if {![file exists $reqfile]} {
145 bad 4 "missing file for request $id (`$tag')"
149 set req_dn [req-dn $reqfile]
150 if {![string equal $req_dn $dn]} {
151 bad 2 "request $id (`$tag') has DN mismatch"
152 moan "\t(db has dn = $dn)"
153 moan "\t(file has dn = $req_dn)"
156 set req_hash [req-key-hash $reqfile]
157 if {![string equal $req_hash $hash]} {
158 bad 2 "request $id (`$tag') has key hash mismatch"
159 moan "\t(db has hash = $hash)"
160 moan "\t(file has hash = $req_hash)"
164 ## Run through all of the certificates and check that they match the
165 ## correspoding certificate files. This is a good opportunity to verify that
166 ## the certificates match up with requests.
168 foreach {seq req tag st dn hash} [db eval {
169 SELECT c.seq, r.id, r.tag, c.st, r.cert_dn, r.hash
170 FROM certificate AS c LEFT OUTER JOIN request AS r ON c.req = r.id;
172 if {[info exists certmap($seq)]} {
173 bad 4 "duplicate certificate serial number $seq"
178 if {[string equal $req nil]} {
179 bad 2 "certificate $seq has no certificate request"
182 switch -exact -- $st {
183 active - withdrawn - superceded - revoked - expired { }
185 bad 2 "certificate $id (`$tag') has unknown state `$st'"
189 set certfile "$CERTROOT/cert/by-seq/$seq"
190 if {![file exists $certfile]} {
191 bad 4 "missing file for certficate $seq (`$tag')"
194 if {[string equal $req nil]} { continue }
196 set cert_dn [cert-dn $certfile]
197 if {![string equal $dn $cert_dn]} {
198 bad 2 "certificate $seq (`$tag') has DN mismatch"
199 moan "\t(db has dn = $dn)"
200 moan "\t(file has dn = $cert_dn)"
203 set cert_hash [cert-key-hash $certfile]
204 if {![string equal $cert_hash $hash]} {
205 bad 2 "certificate $seq (`$tag') has key hash mismatch"
206 moan "\t(db has hash = $hash)"
207 moan "\t(file has hash = $cert_hash)"
211 ## Finally, make sure that there aren't any stray files in those directories.
212 foreach {dir mapvar} {
214 "cert/by-seq" certmap
217 foreach file [glob -tails -directory "$CERTROOT/$dir" -nocomplain *] {
218 if {![info exists map($file)]} {
219 bad 1 "bogus file `$dir/$file'"
227 ###----- That's all, folks --------------------------------------------------