|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include "signtool.h" |
|
6 |
|
7 |
|
8 static int jar_cb(int status, JAR *jar, const char *metafile, |
|
9 char *pathname, char *errortext); |
|
10 static int verify_global (JAR *jar); |
|
11 |
|
12 /************************************************************************* |
|
13 * |
|
14 * V e r i f y J a r |
|
15 */ |
|
16 int |
|
17 VerifyJar(char *filename) |
|
18 { |
|
19 FILE * fp; |
|
20 |
|
21 int ret; |
|
22 int status; |
|
23 int failed = 0; |
|
24 char *err; |
|
25 |
|
26 JAR * jar; |
|
27 JAR_Context * ctx; |
|
28 |
|
29 JAR_Item * it; |
|
30 |
|
31 jar = JAR_new(); |
|
32 |
|
33 if ((fp = fopen (filename, "r")) == NULL) { |
|
34 perror (filename); |
|
35 exit (ERRX); |
|
36 } else |
|
37 fclose (fp); |
|
38 |
|
39 JAR_set_callback (JAR_CB_SIGNAL, jar, jar_cb); |
|
40 |
|
41 |
|
42 status = JAR_pass_archive (jar, jarArchGuess, filename, "some-url"); |
|
43 |
|
44 if (status < 0 || jar->valid < 0) { |
|
45 failed = 1; |
|
46 PR_fprintf(outputFD, |
|
47 "\nNOTE -- \"%s\" archive DID NOT PASS crypto verification.\n", |
|
48 filename); |
|
49 if (status < 0) { |
|
50 const char *errtext; |
|
51 |
|
52 if (status >= JAR_BASE && status <= JAR_BASE_END) { |
|
53 errtext = JAR_get_error (status); |
|
54 } else { |
|
55 errtext = SECU_Strerror(PORT_GetError()); |
|
56 } |
|
57 |
|
58 PR_fprintf(outputFD, " (reported reason: %s)\n\n", |
|
59 errtext); |
|
60 |
|
61 /* corrupt files should not have their contents listed */ |
|
62 |
|
63 if (status == JAR_ERR_CORRUPT) |
|
64 return - 1; |
|
65 } |
|
66 PR_fprintf(outputFD, |
|
67 "entries shown below will have their digests checked only.\n"); |
|
68 jar->valid = 0; |
|
69 } else |
|
70 PR_fprintf(outputFD, |
|
71 "archive \"%s\" has passed crypto verification.\n", filename); |
|
72 |
|
73 if (verify_global (jar)) |
|
74 failed = 1; |
|
75 |
|
76 PR_fprintf(outputFD, "\n"); |
|
77 PR_fprintf(outputFD, "%16s %s\n", "status", "path"); |
|
78 PR_fprintf(outputFD, "%16s %s\n", "------------", "-------------------"); |
|
79 |
|
80 ctx = JAR_find (jar, NULL, jarTypeMF); |
|
81 |
|
82 while (JAR_find_next (ctx, &it) >= 0) { |
|
83 if (it && it->pathname) { |
|
84 rm_dash_r(TMP_OUTPUT); |
|
85 ret = JAR_verified_extract (jar, it->pathname, TMP_OUTPUT); |
|
86 /* if (ret < 0) printf ("error %d on %s\n", ret, it->pathname); */ |
|
87 if (ret < 0) |
|
88 failed = 1; |
|
89 |
|
90 if (ret == JAR_ERR_PNF) |
|
91 err = "NOT PRESENT"; |
|
92 else if (ret == JAR_ERR_HASH) |
|
93 err = "HASH FAILED"; |
|
94 else |
|
95 err = "NOT VERIFIED"; |
|
96 |
|
97 PR_fprintf(outputFD, "%16s %s\n", |
|
98 ret >= 0 ? "verified" : err, it->pathname); |
|
99 |
|
100 if (ret != 0 && ret != JAR_ERR_PNF && ret != JAR_ERR_HASH) |
|
101 PR_fprintf(outputFD, " (reason: %s)\n", |
|
102 JAR_get_error (ret)); |
|
103 } |
|
104 } |
|
105 |
|
106 JAR_find_end (ctx); |
|
107 |
|
108 if (status < 0 || jar->valid < 0) { |
|
109 failed = 1; |
|
110 PR_fprintf(outputFD, |
|
111 "\nNOTE -- \"%s\" archive DID NOT PASS crypto verification.\n", |
|
112 filename); |
|
113 give_help (status); |
|
114 } |
|
115 |
|
116 JAR_destroy (jar); |
|
117 |
|
118 if (failed) |
|
119 return - 1; |
|
120 return 0; |
|
121 } |
|
122 |
|
123 |
|
124 /*************************************************************************** |
|
125 * |
|
126 * v e r i f y _ g l o b a l |
|
127 */ |
|
128 static int |
|
129 verify_global (JAR *jar) |
|
130 { |
|
131 FILE * fp; |
|
132 JAR_Context * ctx; |
|
133 JAR_Item * it; |
|
134 JAR_Digest * globaldig; |
|
135 char * ext; |
|
136 unsigned char *md5_digest, *sha1_digest; |
|
137 unsigned int sha1_length, md5_length; |
|
138 int retval = 0; |
|
139 char buf [BUFSIZ]; |
|
140 |
|
141 ctx = JAR_find (jar, "*", jarTypePhy); |
|
142 |
|
143 while (JAR_find_next (ctx, &it) >= 0) { |
|
144 if (!PORT_Strncmp (it->pathname, "META-INF", 8)) { |
|
145 for (ext = it->pathname; *ext; ext++) |
|
146 ; |
|
147 while (ext > it->pathname && *ext != '.') |
|
148 ext--; |
|
149 |
|
150 if (verbosity >= 0) { |
|
151 if (!PORT_Strcasecmp (ext, ".rsa")) { |
|
152 PR_fprintf(outputFD, "found a RSA signature file: %s\n", |
|
153 it->pathname); |
|
154 } |
|
155 |
|
156 if (!PORT_Strcasecmp (ext, ".dsa")) { |
|
157 PR_fprintf(outputFD, "found a DSA signature file: %s\n", |
|
158 it->pathname); |
|
159 } |
|
160 |
|
161 if (!PORT_Strcasecmp (ext, ".mf")) { |
|
162 PR_fprintf(outputFD, |
|
163 "found a MF master manifest file: %s\n", |
|
164 it->pathname); |
|
165 } |
|
166 } |
|
167 |
|
168 if (!PORT_Strcasecmp (ext, ".sf")) { |
|
169 if (verbosity >= 0) { |
|
170 PR_fprintf(outputFD, |
|
171 "found a SF signature manifest file: %s\n", |
|
172 it->pathname); |
|
173 } |
|
174 |
|
175 rm_dash_r(TMP_OUTPUT); |
|
176 if (JAR_extract (jar, it->pathname, TMP_OUTPUT) < 0) { |
|
177 PR_fprintf(errorFD, "%s: error extracting %s\n", |
|
178 PROGRAM_NAME, it->pathname); |
|
179 errorCount++; |
|
180 retval = -1; |
|
181 continue; |
|
182 } |
|
183 |
|
184 md5_digest = NULL; |
|
185 sha1_digest = NULL; |
|
186 |
|
187 if ((fp = fopen (TMP_OUTPUT, "rb")) != NULL) { |
|
188 while (fgets (buf, BUFSIZ, fp)) { |
|
189 char *s; |
|
190 |
|
191 if (*buf == 0 || *buf == '\n' || *buf == '\r') |
|
192 break; |
|
193 |
|
194 for (s = buf; *s && *s != '\n' && *s != '\r'; s++) |
|
195 ; |
|
196 *s = 0; |
|
197 |
|
198 if (!PORT_Strncmp (buf, "MD5-Digest: ", 12)) { |
|
199 md5_digest = |
|
200 ATOB_AsciiToData (buf + 12, &md5_length); |
|
201 } |
|
202 if (!PORT_Strncmp (buf, "SHA1-Digest: ", 13)) { |
|
203 sha1_digest = |
|
204 ATOB_AsciiToData (buf + 13, &sha1_length); |
|
205 } |
|
206 if (!PORT_Strncmp (buf, "SHA-Digest: ", 12)) { |
|
207 sha1_digest = |
|
208 ATOB_AsciiToData (buf + 12, &sha1_length); |
|
209 } |
|
210 } |
|
211 |
|
212 globaldig = jar->globalmeta; |
|
213 |
|
214 if (globaldig && md5_digest && verbosity >= 0) { |
|
215 PR_fprintf(outputFD, |
|
216 " md5 digest on global metainfo: %s\n", |
|
217 PORT_Memcmp(md5_digest, globaldig->md5, MD5_LENGTH) |
|
218 ? "no match" : "match"); |
|
219 } |
|
220 |
|
221 if (globaldig && sha1_digest && verbosity >= 0) { |
|
222 PR_fprintf(outputFD, |
|
223 " sha digest on global metainfo: %s\n", |
|
224 PORT_Memcmp(sha1_digest, globaldig->sha1, SHA1_LENGTH) |
|
225 ? "no match" : "match"); |
|
226 } |
|
227 |
|
228 if (globaldig == NULL && verbosity >= 0) { |
|
229 PR_fprintf(outputFD, |
|
230 "global metadigest is not available, strange.\n"); |
|
231 } |
|
232 |
|
233 fclose (fp); |
|
234 } |
|
235 } |
|
236 } |
|
237 } |
|
238 |
|
239 JAR_find_end (ctx); |
|
240 |
|
241 return retval; |
|
242 } |
|
243 |
|
244 |
|
245 /************************************************************************ |
|
246 * |
|
247 * J a r W h o |
|
248 */ |
|
249 int |
|
250 JarWho(char *filename) |
|
251 { |
|
252 FILE * fp; |
|
253 |
|
254 JAR * jar; |
|
255 JAR_Context * ctx; |
|
256 |
|
257 int status; |
|
258 int retval = 0; |
|
259 |
|
260 JAR_Item * it; |
|
261 JAR_Cert * fing; |
|
262 |
|
263 CERTCertificate * cert, *prev = NULL; |
|
264 |
|
265 jar = JAR_new(); |
|
266 |
|
267 if ((fp = fopen (filename, "r")) == NULL) { |
|
268 perror (filename); |
|
269 exit (ERRX); |
|
270 } |
|
271 fclose (fp); |
|
272 |
|
273 status = JAR_pass_archive (jar, jarArchGuess, filename, "some-url"); |
|
274 |
|
275 if (status < 0 || jar->valid < 0) { |
|
276 PR_fprintf(outputFD, |
|
277 "NOTE -- \"%s\" archive DID NOT PASS crypto verification.\n", |
|
278 filename); |
|
279 retval = -1; |
|
280 if (jar->valid < 0 || status != -1) { |
|
281 const char *errtext; |
|
282 |
|
283 if (status >= JAR_BASE && status <= JAR_BASE_END) { |
|
284 errtext = JAR_get_error (status); |
|
285 } else { |
|
286 errtext = SECU_Strerror(PORT_GetError()); |
|
287 } |
|
288 |
|
289 PR_fprintf(outputFD, " (reported reason: %s)\n\n", errtext); |
|
290 } |
|
291 } |
|
292 |
|
293 PR_fprintf(outputFD, "\nSigner information:\n\n"); |
|
294 |
|
295 ctx = JAR_find (jar, NULL, jarTypeSign); |
|
296 |
|
297 while (JAR_find_next (ctx, &it) >= 0) { |
|
298 fing = (JAR_Cert * ) it->data; |
|
299 cert = fing->cert; |
|
300 |
|
301 if (cert) { |
|
302 if (prev == cert) |
|
303 break; |
|
304 |
|
305 if (cert->nickname) |
|
306 PR_fprintf(outputFD, "nickname: %s\n", cert->nickname); |
|
307 if (cert->subjectName) |
|
308 PR_fprintf(outputFD, "subject name: %s\n", |
|
309 cert->subjectName); |
|
310 if (cert->issuerName) |
|
311 PR_fprintf(outputFD, "issuer name: %s\n", cert->issuerName); |
|
312 } else { |
|
313 PR_fprintf(outputFD, "no certificate could be found\n"); |
|
314 retval = -1; |
|
315 } |
|
316 |
|
317 prev = cert; |
|
318 } |
|
319 |
|
320 JAR_find_end (ctx); |
|
321 |
|
322 JAR_destroy (jar); |
|
323 return retval; |
|
324 } |
|
325 |
|
326 |
|
327 /************************************************************************ |
|
328 * j a r _ c b |
|
329 */ |
|
330 static int jar_cb(int status, JAR *jar, const char *metafile, |
|
331 char *pathname, char *errortext) |
|
332 { |
|
333 PR_fprintf(errorFD, "error %d: %s IN FILE %s\n", status, errortext, |
|
334 pathname); |
|
335 errorCount++; |
|
336 return 0; |
|
337 } |
|
338 |
|
339 |