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 /*
6  *  JARSIGN
7  *
8  *  Routines used in signing archives.
9  */
10 
11 #include "jar.h"
12 #include "jarint.h"
13 #include "secpkcs7.h"
14 #include "pk11func.h"
15 #include "sechash.h"
16 
17 /* from libevent.h */
18 typedef void (*ETVoidPtrFunc)(void *data);
19 
20 /* key database wrapper */
21 /* static SECKEYKeyDBHandle *jar_open_key_database (void); */
22 /* CHUNQ is our bite size */
23 
24 #define CHUNQ 64000
25 #define FILECHUNQ 32768
26 
27 /*
28  *  J A R _ c a l c u l a t e _ d i g e s t
29  *
30  *  Quick calculation of a digest for
31  *  the specified block of memory. Will calculate
32  *  for all supported algorithms, now MD5.
33  *
34  *  This version supports huge pointers for WIN16.
35  *
36  */
37 JAR_Digest *PR_CALLBACK
JAR_calculate_digest(void * data,long length)38 JAR_calculate_digest(void *data, long length)
39 {
40     PK11Context *md5 = 0;
41     PK11Context *sha1 = 0;
42     JAR_Digest *dig = PORT_ZNew(JAR_Digest);
43     long chunq;
44     unsigned int md5_length, sha1_length;
45 
46     if (dig == NULL) {
47         /* out of memory allocating digest */
48         return NULL;
49     }
50 
51     md5 = PK11_CreateDigestContext(SEC_OID_MD5);
52     if (md5 == NULL) {
53         PORT_ZFree(dig, sizeof(JAR_Digest));
54         return NULL;
55     }
56     sha1 = PK11_CreateDigestContext(SEC_OID_SHA1);
57     if (sha1 == NULL) {
58         PK11_DestroyContext(md5, PR_TRUE);
59         /* added due to bug Bug 1250214 - prevent the 2nd memory leak */
60         PORT_ZFree(dig, sizeof(JAR_Digest));
61         return NULL;
62     }
63 
64     if (length >= 0) {
65         PK11_DigestBegin(md5);
66         PK11_DigestBegin(sha1);
67 
68         do {
69             chunq = length;
70 
71             PK11_DigestOp(md5, (unsigned char *)data, chunq);
72             PK11_DigestOp(sha1, (unsigned char *)data, chunq);
73             length -= chunq;
74             data = ((char *)data + chunq);
75         } while (length > 0);
76 
77         PK11_DigestFinal(md5, dig->md5, &md5_length, MD5_LENGTH);
78         PK11_DigestFinal(sha1, dig->sha1, &sha1_length, SHA1_LENGTH);
79 
80         PK11_DestroyContext(md5, PR_TRUE);
81         PK11_DestroyContext(sha1, PR_TRUE);
82     }
83     return dig;
84 }
85 
86 /*
87  *  J A R _ d i g e s t _ f i l e
88  *
89  *  Calculates the MD5 and SHA1 digests for a file
90  *  present on disk, and returns these in JAR_Digest struct.
91  *
92  */
93 int
JAR_digest_file(char * filename,JAR_Digest * dig)94 JAR_digest_file(char *filename, JAR_Digest *dig)
95 {
96     JAR_FILE fp;
97     PK11Context *md5 = 0;
98     PK11Context *sha1 = 0;
99     unsigned char *buf = (unsigned char *)PORT_ZAlloc(FILECHUNQ);
100     int num;
101     unsigned int md5_length, sha1_length;
102 
103     if (buf == NULL) {
104         /* out of memory */
105         return JAR_ERR_MEMORY;
106     }
107 
108     if ((fp = JAR_FOPEN(filename, "rb")) == 0) {
109         /* perror (filename); FIX XXX XXX XXX XXX XXX XXX */
110         PORT_Free(buf);
111         return JAR_ERR_FNF;
112     }
113 
114     md5 = PK11_CreateDigestContext(SEC_OID_MD5);
115     sha1 = PK11_CreateDigestContext(SEC_OID_SHA1);
116 
117     if (md5 == NULL || sha1 == NULL) {
118         if (md5) {
119             PK11_DestroyContext(md5, PR_TRUE);
120         }
121         if (sha1) {
122             PK11_DestroyContext(sha1, PR_TRUE);
123         }
124         /* can't generate digest contexts */
125         PORT_Free(buf);
126         JAR_FCLOSE(fp);
127         return JAR_ERR_GENERAL;
128     }
129 
130     PK11_DigestBegin(md5);
131     PK11_DigestBegin(sha1);
132 
133     while (1) {
134         if ((num = JAR_FREAD(fp, buf, FILECHUNQ)) == 0)
135             break;
136 
137         PK11_DigestOp(md5, buf, num);
138         PK11_DigestOp(sha1, buf, num);
139     }
140 
141     PK11_DigestFinal(md5, dig->md5, &md5_length, MD5_LENGTH);
142     PK11_DigestFinal(sha1, dig->sha1, &sha1_length, SHA1_LENGTH);
143 
144     PK11_DestroyContext(md5, PR_TRUE);
145     PK11_DestroyContext(sha1, PR_TRUE);
146 
147     PORT_Free(buf);
148     JAR_FCLOSE(fp);
149 
150     return 0;
151 }
152 
153 /*
154  *  J A R _ o p e n _ k e y _ d a t a b a s e
155  *
156  */
157 
158 void *
jar_open_key_database(void)159 jar_open_key_database(void)
160 {
161     return NULL;
162 }
163 
164 int
jar_close_key_database(void * keydb)165 jar_close_key_database(void *keydb)
166 {
167     /* We never do close it */
168     return 0;
169 }
170 
171 /*
172  *  j a r _ c r e a t e _ p k 7
173  *
174  */
175 
176 static void
jar_pk7_out(void * arg,const char * buf,unsigned long len)177 jar_pk7_out(void *arg, const char *buf, unsigned long len)
178 {
179     JAR_FWRITE((JAR_FILE)arg, buf, len);
180 }
181 
182 int
jar_create_pk7(CERTCertDBHandle * certdb,void * keydb,CERTCertificate * cert,char * password,JAR_FILE infp,JAR_FILE outfp)183 jar_create_pk7(CERTCertDBHandle *certdb, void *keydb, CERTCertificate *cert,
184                char *password, JAR_FILE infp, JAR_FILE outfp)
185 {
186     SEC_PKCS7ContentInfo *cinfo;
187     const SECHashObject *hashObj;
188     void *mw = NULL;
189     void *hashcx;
190     unsigned int len;
191     int status = 0;
192     SECStatus rv;
193     SECItem digest;
194     unsigned char digestdata[32];
195     unsigned char buffer[4096];
196 
197     if (outfp == NULL || infp == NULL || cert == NULL)
198         return JAR_ERR_GENERAL;
199 
200     /* we sign with SHA */
201     hashObj = HASH_GetHashObject(HASH_AlgSHA1);
202 
203     hashcx = (*hashObj->create)();
204     if (hashcx == NULL)
205         return JAR_ERR_GENERAL;
206 
207     (*hashObj->begin)(hashcx);
208     while (1) {
209         int nb = JAR_FREAD(infp, buffer, sizeof buffer);
210         if (nb == 0) { /* eof */
211             break;
212         }
213         (*hashObj->update)(hashcx, buffer, nb);
214     }
215     (*hashObj->end)(hashcx, digestdata, &len, 32);
216     (*hashObj->destroy)(hashcx, PR_TRUE);
217 
218     digest.data = digestdata;
219     digest.len = len;
220 
221     /* signtool must use any old context it can find since it's
222        calling from inside javaland. */
223     PORT_SetError(0);
224     cinfo = SEC_PKCS7CreateSignedData(cert, certUsageObjectSigner, NULL,
225                                       SEC_OID_SHA1, &digest, NULL, mw);
226     if (cinfo == NULL)
227         return JAR_ERR_PK7;
228 
229     rv = SEC_PKCS7IncludeCertChain(cinfo, NULL);
230     if (rv != SECSuccess) {
231         status = PORT_GetError();
232         SEC_PKCS7DestroyContentInfo(cinfo);
233         return status;
234     }
235 
236     /* Having this here forces signtool to always include signing time. */
237     rv = SEC_PKCS7AddSigningTime(cinfo);
238     /* don't check error */
239     PORT_SetError(0);
240 
241     /* if calling from mozilla thread*/
242     rv = SEC_PKCS7Encode(cinfo, jar_pk7_out, outfp, NULL, NULL, mw);
243     if (rv != SECSuccess)
244         status = PORT_GetError();
245     SEC_PKCS7DestroyContentInfo(cinfo);
246     if (rv != SECSuccess) {
247         return ((status < 0) ? status : JAR_ERR_GENERAL);
248     }
249     return 0;
250 }
251