|
1 /* pk7_smime.c */ |
|
2 /* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL |
|
3 * project. |
|
4 */ |
|
5 /* ==================================================================== |
|
6 * Copyright (c) 1999-2004 The OpenSSL Project. All rights reserved. |
|
7 * |
|
8 * Redistribution and use in source and binary forms, with or without |
|
9 * modification, are permitted provided that the following conditions |
|
10 * are met: |
|
11 * |
|
12 * 1. Redistributions of source code must retain the above copyright |
|
13 * notice, this list of conditions and the following disclaimer. |
|
14 * |
|
15 * 2. Redistributions in binary form must reproduce the above copyright |
|
16 * notice, this list of conditions and the following disclaimer in |
|
17 * the documentation and/or other materials provided with the |
|
18 * distribution. |
|
19 * |
|
20 * 3. All advertising materials mentioning features or use of this |
|
21 * software must display the following acknowledgment: |
|
22 * "This product includes software developed by the OpenSSL Project |
|
23 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" |
|
24 * |
|
25 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to |
|
26 * endorse or promote products derived from this software without |
|
27 * prior written permission. For written permission, please contact |
|
28 * licensing@OpenSSL.org. |
|
29 * |
|
30 * 5. Products derived from this software may not be called "OpenSSL" |
|
31 * nor may "OpenSSL" appear in their names without prior written |
|
32 * permission of the OpenSSL Project. |
|
33 * |
|
34 * 6. Redistributions of any form whatsoever must retain the following |
|
35 * acknowledgment: |
|
36 * "This product includes software developed by the OpenSSL Project |
|
37 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" |
|
38 * |
|
39 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY |
|
40 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
42 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR |
|
43 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
44 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
45 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
46 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
|
48 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
49 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
|
50 * OF THE POSSIBILITY OF SUCH DAMAGE. |
|
51 * ==================================================================== |
|
52 * |
|
53 * This product includes cryptographic software written by Eric Young |
|
54 * (eay@cryptsoft.com). This product includes software written by Tim |
|
55 * Hudson (tjh@cryptsoft.com). |
|
56 * |
|
57 */ |
|
58 /* |
|
59 © Portions copyright (c) 2006 Nokia Corporation. All rights reserved. |
|
60 */ |
|
61 /* Simple PKCS#7 processing functions */ |
|
62 |
|
63 #include <stdio.h> |
|
64 #include "cryptlib.h" |
|
65 #include <openssl/x509.h> |
|
66 #include <openssl/x509v3.h> |
|
67 |
|
68 EXPORT_C PKCS7 *PKCS7_sign(X509 *signcert, EVP_PKEY *pkey, STACK_OF(X509) *certs, |
|
69 BIO *data, int flags) |
|
70 { |
|
71 PKCS7 *p7 = NULL; |
|
72 PKCS7_SIGNER_INFO *si; |
|
73 BIO *p7bio = NULL; |
|
74 STACK_OF(X509_ALGOR) *smcap = NULL; |
|
75 int i; |
|
76 |
|
77 if(!X509_check_private_key(signcert, pkey)) { |
|
78 PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE); |
|
79 return NULL; |
|
80 } |
|
81 |
|
82 if(!(p7 = PKCS7_new())) { |
|
83 PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE); |
|
84 return NULL; |
|
85 } |
|
86 |
|
87 if (!PKCS7_set_type(p7, NID_pkcs7_signed)) |
|
88 goto err; |
|
89 |
|
90 if (!PKCS7_content_new(p7, NID_pkcs7_data)) |
|
91 goto err; |
|
92 |
|
93 if (!(si = PKCS7_add_signature(p7,signcert,pkey,EVP_sha1()))) { |
|
94 PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR); |
|
95 goto err; |
|
96 } |
|
97 |
|
98 if(!(flags & PKCS7_NOCERTS)) { |
|
99 if (!PKCS7_add_certificate(p7, signcert)) |
|
100 goto err; |
|
101 if(certs) for(i = 0; i < sk_X509_num(certs); i++) |
|
102 if (!PKCS7_add_certificate(p7, sk_X509_value(certs, i))) |
|
103 goto err; |
|
104 } |
|
105 |
|
106 if(!(flags & PKCS7_NOATTR)) { |
|
107 if (!PKCS7_add_signed_attribute(si, NID_pkcs9_contentType, |
|
108 V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data))) |
|
109 goto err; |
|
110 /* Add SMIMECapabilities */ |
|
111 if(!(flags & PKCS7_NOSMIMECAP)) |
|
112 { |
|
113 if(!(smcap = sk_X509_ALGOR_new_null())) { |
|
114 PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE); |
|
115 goto err; |
|
116 } |
|
117 #ifndef OPENSSL_NO_DES |
|
118 if (!PKCS7_simple_smimecap (smcap, NID_des_ede3_cbc, -1)) |
|
119 goto err; |
|
120 #endif |
|
121 #ifndef OPENSSL_NO_RC2 |
|
122 if (!PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 128)) |
|
123 goto err; |
|
124 if (!PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 64)) |
|
125 goto err; |
|
126 #endif |
|
127 #ifndef OPENSSL_NO_DES |
|
128 if (!PKCS7_simple_smimecap (smcap, NID_des_cbc, -1)) |
|
129 goto err; |
|
130 #endif |
|
131 #ifndef OPENSSL_NO_RC2 |
|
132 if (!PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 40)) |
|
133 goto err; |
|
134 #endif |
|
135 if (!PKCS7_add_attrib_smimecap (si, smcap)) |
|
136 goto err; |
|
137 sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free); |
|
138 smcap = NULL; |
|
139 } |
|
140 } |
|
141 if(flags & PKCS7_DETACHED)PKCS7_set_detached(p7, 1); |
|
142 |
|
143 if (flags & PKCS7_STREAM) |
|
144 return p7; |
|
145 |
|
146 if (!(p7bio = PKCS7_dataInit(p7, NULL))) { |
|
147 PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE); |
|
148 goto err; |
|
149 } |
|
150 |
|
151 SMIME_crlf_copy(data, p7bio, flags); |
|
152 |
|
153 if (!PKCS7_dataFinal(p7,p7bio)) { |
|
154 PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PKCS7_DATASIGN); |
|
155 goto err; |
|
156 } |
|
157 |
|
158 BIO_free_all(p7bio); |
|
159 return p7; |
|
160 err: |
|
161 sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free); |
|
162 BIO_free_all(p7bio); |
|
163 PKCS7_free(p7); |
|
164 return NULL; |
|
165 } |
|
166 |
|
167 EXPORT_C int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store, |
|
168 BIO *indata, BIO *out, int flags) |
|
169 { |
|
170 STACK_OF(X509) *signers; |
|
171 X509 *signer; |
|
172 STACK_OF(PKCS7_SIGNER_INFO) *sinfos; |
|
173 PKCS7_SIGNER_INFO *si; |
|
174 X509_STORE_CTX cert_ctx; |
|
175 #ifndef SYMBIAN |
|
176 char buf[4096]; |
|
177 #else |
|
178 char buf[512]; |
|
179 #endif |
|
180 |
|
181 int i, j=0, k, ret = 0; |
|
182 BIO *p7bio; |
|
183 BIO *tmpin, *tmpout; |
|
184 |
|
185 if(!p7) { |
|
186 PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_INVALID_NULL_POINTER); |
|
187 return 0; |
|
188 } |
|
189 |
|
190 if(!PKCS7_type_is_signed(p7)) { |
|
191 PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_WRONG_CONTENT_TYPE); |
|
192 return 0; |
|
193 } |
|
194 |
|
195 /* Check for no data and no content: no data to verify signature */ |
|
196 if(PKCS7_get_detached(p7) && !indata) { |
|
197 PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_NO_CONTENT); |
|
198 return 0; |
|
199 } |
|
200 #if 0 |
|
201 /* NB: this test commented out because some versions of Netscape |
|
202 * illegally include zero length content when signing data. |
|
203 */ |
|
204 |
|
205 /* Check for data and content: two sets of data */ |
|
206 if(!PKCS7_get_detached(p7) && indata) { |
|
207 PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_CONTENT_AND_DATA_PRESENT); |
|
208 return 0; |
|
209 } |
|
210 #endif |
|
211 |
|
212 sinfos = PKCS7_get_signer_info(p7); |
|
213 |
|
214 if(!sinfos || !sk_PKCS7_SIGNER_INFO_num(sinfos)) { |
|
215 PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_NO_SIGNATURES_ON_DATA); |
|
216 return 0; |
|
217 } |
|
218 |
|
219 |
|
220 signers = PKCS7_get0_signers(p7, certs, flags); |
|
221 |
|
222 if(!signers) return 0; |
|
223 |
|
224 /* Now verify the certificates */ |
|
225 |
|
226 if (!(flags & PKCS7_NOVERIFY)) for (k = 0; k < sk_X509_num(signers); k++) { |
|
227 signer = sk_X509_value (signers, k); |
|
228 if (!(flags & PKCS7_NOCHAIN)) { |
|
229 if(!X509_STORE_CTX_init(&cert_ctx, store, signer, |
|
230 p7->d.sign->cert)) |
|
231 { |
|
232 PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_X509_LIB); |
|
233 sk_X509_free(signers); |
|
234 return 0; |
|
235 } |
|
236 X509_STORE_CTX_set_purpose(&cert_ctx, |
|
237 X509_PURPOSE_SMIME_SIGN); |
|
238 } else if(!X509_STORE_CTX_init (&cert_ctx, store, signer, NULL)) { |
|
239 PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_X509_LIB); |
|
240 sk_X509_free(signers); |
|
241 return 0; |
|
242 } |
|
243 if (!(flags & PKCS7_NOCRL)) |
|
244 X509_STORE_CTX_set0_crls(&cert_ctx, p7->d.sign->crl); |
|
245 i = X509_verify_cert(&cert_ctx); |
|
246 if (i <= 0) j = X509_STORE_CTX_get_error(&cert_ctx); |
|
247 X509_STORE_CTX_cleanup(&cert_ctx); |
|
248 if (i <= 0) { |
|
249 PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR); |
|
250 ERR_add_error_data(2, "Verify error:", |
|
251 X509_verify_cert_error_string(j)); |
|
252 sk_X509_free(signers); |
|
253 return 0; |
|
254 } |
|
255 /* Check for revocation status here */ |
|
256 } |
|
257 |
|
258 /* Performance optimization: if the content is a memory BIO then |
|
259 * store its contents in a temporary read only memory BIO. This |
|
260 * avoids potentially large numbers of slow copies of data which will |
|
261 * occur when reading from a read write memory BIO when signatures |
|
262 * are calculated. |
|
263 */ |
|
264 |
|
265 if (indata && (BIO_method_type(indata) == BIO_TYPE_MEM)) |
|
266 { |
|
267 char *ptr; |
|
268 long len; |
|
269 len = BIO_get_mem_data(indata, &ptr); |
|
270 tmpin = BIO_new_mem_buf(ptr, len); |
|
271 if (tmpin == NULL) |
|
272 { |
|
273 PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_MALLOC_FAILURE); |
|
274 return 0; |
|
275 } |
|
276 } |
|
277 else |
|
278 tmpin = indata; |
|
279 |
|
280 |
|
281 if (!(p7bio=PKCS7_dataInit(p7,tmpin))) |
|
282 goto err; |
|
283 |
|
284 if(flags & PKCS7_TEXT) { |
|
285 if(!(tmpout = BIO_new(BIO_s_mem()))) { |
|
286 PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_MALLOC_FAILURE); |
|
287 goto err; |
|
288 } |
|
289 } else tmpout = out; |
|
290 |
|
291 /* We now have to 'read' from p7bio to calculate digests etc. */ |
|
292 for (;;) |
|
293 { |
|
294 i=BIO_read(p7bio,buf,sizeof(buf)); |
|
295 if (i <= 0) break; |
|
296 if (tmpout) BIO_write(tmpout, buf, i); |
|
297 } |
|
298 |
|
299 if(flags & PKCS7_TEXT) { |
|
300 if(!SMIME_text(tmpout, out)) { |
|
301 PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_SMIME_TEXT_ERROR); |
|
302 BIO_free(tmpout); |
|
303 goto err; |
|
304 } |
|
305 BIO_free(tmpout); |
|
306 } |
|
307 |
|
308 /* Now Verify All Signatures */ |
|
309 if (!(flags & PKCS7_NOSIGS)) |
|
310 for (i=0; i<sk_PKCS7_SIGNER_INFO_num(sinfos); i++) |
|
311 { |
|
312 si=sk_PKCS7_SIGNER_INFO_value(sinfos,i); |
|
313 signer = sk_X509_value (signers, i); |
|
314 j=PKCS7_signatureVerify(p7bio,p7,si, signer); |
|
315 if (j <= 0) { |
|
316 PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_SIGNATURE_FAILURE); |
|
317 goto err; |
|
318 } |
|
319 } |
|
320 |
|
321 ret = 1; |
|
322 |
|
323 err: |
|
324 |
|
325 if (tmpin == indata) |
|
326 { |
|
327 if (indata) BIO_pop(p7bio); |
|
328 } |
|
329 BIO_free_all(p7bio); |
|
330 |
|
331 sk_X509_free(signers); |
|
332 |
|
333 return ret; |
|
334 } |
|
335 |
|
336 EXPORT_C STACK_OF(X509) *PKCS7_get0_signers(PKCS7 *p7, STACK_OF(X509) *certs, int flags) |
|
337 { |
|
338 STACK_OF(X509) *signers; |
|
339 STACK_OF(PKCS7_SIGNER_INFO) *sinfos; |
|
340 PKCS7_SIGNER_INFO *si; |
|
341 PKCS7_ISSUER_AND_SERIAL *ias; |
|
342 X509 *signer; |
|
343 int i; |
|
344 |
|
345 if(!p7) { |
|
346 PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,PKCS7_R_INVALID_NULL_POINTER); |
|
347 return NULL; |
|
348 } |
|
349 |
|
350 if(!PKCS7_type_is_signed(p7)) { |
|
351 PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,PKCS7_R_WRONG_CONTENT_TYPE); |
|
352 return NULL; |
|
353 } |
|
354 |
|
355 /* Collect all the signers together */ |
|
356 |
|
357 sinfos = PKCS7_get_signer_info(p7); |
|
358 |
|
359 if(sk_PKCS7_SIGNER_INFO_num(sinfos) <= 0) { |
|
360 PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,PKCS7_R_NO_SIGNERS); |
|
361 return NULL; |
|
362 } |
|
363 |
|
364 if(!(signers = sk_X509_new_null())) { |
|
365 PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,ERR_R_MALLOC_FAILURE); |
|
366 return NULL; |
|
367 } |
|
368 |
|
369 for (i = 0; i < sk_PKCS7_SIGNER_INFO_num(sinfos); i++) |
|
370 { |
|
371 si = sk_PKCS7_SIGNER_INFO_value(sinfos, i); |
|
372 ias = si->issuer_and_serial; |
|
373 signer = NULL; |
|
374 /* If any certificates passed they take priority */ |
|
375 if (certs) signer = X509_find_by_issuer_and_serial (certs, |
|
376 ias->issuer, ias->serial); |
|
377 if (!signer && !(flags & PKCS7_NOINTERN) |
|
378 && p7->d.sign->cert) signer = |
|
379 X509_find_by_issuer_and_serial (p7->d.sign->cert, |
|
380 ias->issuer, ias->serial); |
|
381 if (!signer) { |
|
382 PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND); |
|
383 sk_X509_free(signers); |
|
384 return NULL; |
|
385 } |
|
386 |
|
387 if (!sk_X509_push(signers, signer)) { |
|
388 sk_X509_free(signers); |
|
389 return NULL; |
|
390 } |
|
391 } |
|
392 return signers; |
|
393 } |
|
394 |
|
395 |
|
396 /* Build a complete PKCS#7 enveloped data */ |
|
397 |
|
398 EXPORT_C PKCS7 *PKCS7_encrypt(STACK_OF(X509) *certs, BIO *in, const EVP_CIPHER *cipher, |
|
399 int flags) |
|
400 { |
|
401 PKCS7 *p7; |
|
402 BIO *p7bio = NULL; |
|
403 int i; |
|
404 X509 *x509; |
|
405 if(!(p7 = PKCS7_new())) { |
|
406 PKCS7err(PKCS7_F_PKCS7_ENCRYPT,ERR_R_MALLOC_FAILURE); |
|
407 return NULL; |
|
408 } |
|
409 |
|
410 if (!PKCS7_set_type(p7, NID_pkcs7_enveloped)) |
|
411 goto err; |
|
412 if(!PKCS7_set_cipher(p7, cipher)) { |
|
413 PKCS7err(PKCS7_F_PKCS7_ENCRYPT,PKCS7_R_ERROR_SETTING_CIPHER); |
|
414 goto err; |
|
415 } |
|
416 |
|
417 for(i = 0; i < sk_X509_num(certs); i++) { |
|
418 x509 = sk_X509_value(certs, i); |
|
419 if(!PKCS7_add_recipient(p7, x509)) { |
|
420 PKCS7err(PKCS7_F_PKCS7_ENCRYPT, |
|
421 PKCS7_R_ERROR_ADDING_RECIPIENT); |
|
422 goto err; |
|
423 } |
|
424 } |
|
425 |
|
426 if(!(p7bio = PKCS7_dataInit(p7, NULL))) { |
|
427 PKCS7err(PKCS7_F_PKCS7_ENCRYPT,ERR_R_MALLOC_FAILURE); |
|
428 goto err; |
|
429 } |
|
430 |
|
431 SMIME_crlf_copy(in, p7bio, flags); |
|
432 |
|
433 (void)BIO_flush(p7bio); |
|
434 |
|
435 if (!PKCS7_dataFinal(p7,p7bio)) { |
|
436 PKCS7err(PKCS7_F_PKCS7_ENCRYPT,PKCS7_R_PKCS7_DATAFINAL_ERROR); |
|
437 goto err; |
|
438 } |
|
439 BIO_free_all(p7bio); |
|
440 |
|
441 return p7; |
|
442 |
|
443 err: |
|
444 |
|
445 BIO_free_all(p7bio); |
|
446 PKCS7_free(p7); |
|
447 return NULL; |
|
448 |
|
449 } |
|
450 |
|
451 EXPORT_C int PKCS7_decrypt(PKCS7 *p7, EVP_PKEY *pkey, X509 *cert, BIO *data, int flags) |
|
452 { |
|
453 BIO *tmpmem; |
|
454 int ret, i; |
|
455 #ifndef SYMBIAN |
|
456 char buf[4096]; |
|
457 #else |
|
458 char buf[512]; |
|
459 #endif |
|
460 |
|
461 if(!p7) { |
|
462 PKCS7err(PKCS7_F_PKCS7_DECRYPT,PKCS7_R_INVALID_NULL_POINTER); |
|
463 return 0; |
|
464 } |
|
465 |
|
466 if(!PKCS7_type_is_enveloped(p7)) { |
|
467 PKCS7err(PKCS7_F_PKCS7_DECRYPT,PKCS7_R_WRONG_CONTENT_TYPE); |
|
468 return 0; |
|
469 } |
|
470 |
|
471 if(cert && !X509_check_private_key(cert, pkey)) { |
|
472 PKCS7err(PKCS7_F_PKCS7_DECRYPT, |
|
473 PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE); |
|
474 return 0; |
|
475 } |
|
476 |
|
477 if(!(tmpmem = PKCS7_dataDecode(p7, pkey, NULL, cert))) { |
|
478 PKCS7err(PKCS7_F_PKCS7_DECRYPT, PKCS7_R_DECRYPT_ERROR); |
|
479 return 0; |
|
480 } |
|
481 |
|
482 if (flags & PKCS7_TEXT) { |
|
483 BIO *tmpbuf, *bread; |
|
484 /* Encrypt BIOs can't do BIO_gets() so add a buffer BIO */ |
|
485 if(!(tmpbuf = BIO_new(BIO_f_buffer()))) { |
|
486 PKCS7err(PKCS7_F_PKCS7_DECRYPT, ERR_R_MALLOC_FAILURE); |
|
487 BIO_free_all(tmpmem); |
|
488 return 0; |
|
489 } |
|
490 if(!(bread = BIO_push(tmpbuf, tmpmem))) { |
|
491 PKCS7err(PKCS7_F_PKCS7_DECRYPT, ERR_R_MALLOC_FAILURE); |
|
492 BIO_free_all(tmpbuf); |
|
493 BIO_free_all(tmpmem); |
|
494 return 0; |
|
495 } |
|
496 ret = SMIME_text(bread, data); |
|
497 BIO_free_all(bread); |
|
498 return ret; |
|
499 } else { |
|
500 for(;;) { |
|
501 i = BIO_read(tmpmem, buf, sizeof(buf)); |
|
502 if(i <= 0) break; |
|
503 BIO_write(data, buf, i); |
|
504 } |
|
505 BIO_free_all(tmpmem); |
|
506 return 1; |
|
507 } |
|
508 } |