* new version 2.11.8
[alpine.git] / pith / smkeys.c
blobe815a59a7b413d40ad4204301bbe15cd0d75e292
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: smkeys.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2008 University of Washington
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * ========================================================================
19 * This is based on a contribution from Jonathan Paisley, see smime.c
23 #include "../pith/headers.h"
25 #ifdef SMIME
27 #include "../pith/status.h"
28 #include "../pith/conf.h"
29 #include "../pith/remote.h"
30 #include "../pith/tempfile.h"
31 #include "../pith/busy.h"
32 #include "../pith/osdep/lstcmpnt.h"
33 #include "smkeys.h"
35 #ifdef APPLEKEYCHAIN
36 #include <Security/SecKeychain.h>
37 #include <Security/SecKeychainItem.h>
38 #include <Security/SecKeychainSearch.h>
39 #include <Security/SecCertificate.h>
40 #endif /* APPLEKEYCHAIN */
43 /* internal prototypes */
44 static char *emailstrclean(char *string);
45 static int add_certs_in_dir(X509_LOOKUP *lookup, char *path);
46 static int certlist_to_file(char *filename, CertList *certlist);
47 static int mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup);
51 * Remove leading whitespace, trailing whitespace and convert
52 * to lowercase. Also remove slash characters
54 * Args: s, -- The string to clean
56 * Result: the cleaned string
58 static char *
59 emailstrclean(char *string)
61 char *s = string, *sc = NULL, *p = NULL;
63 for(; *s; s++){ /* single pass */
64 if(!isspace((unsigned char) (*s))){
65 p = NULL; /* not start of blanks */
66 if(!sc) /* first non-blank? */
67 sc = string; /* start copying */
69 else if(!p) /* it's OK if sc == NULL */
70 p = sc; /* start of blanks? */
72 if(sc && *s!='/' && *s!='\\') /* if copying, copy */
73 *sc++ = isupper((unsigned char) (*s))
74 ? (unsigned char) tolower((unsigned char) (*s))
75 : (unsigned char) (*s);
78 if(p) /* if ending blanks */
79 *p = '\0'; /* tie off beginning */
80 else if(!sc) /* never saw a non-blank */
81 *string = '\0'; /* so tie whole thing off */
83 return(string);
88 * Add a lookup for each "*.crt" file in the given directory.
90 static int
91 add_certs_in_dir(X509_LOOKUP *lookup, char *path)
93 char buf[MAXPATH];
94 struct direct *d;
95 DIR *dirp;
96 int ret = 0;
98 dirp = opendir(path);
99 if(dirp){
101 while(!ret && (d=readdir(dirp)) != NULL){
102 if(srchrstr(d->d_name, ".crt")){
103 build_path(buf, path, d->d_name, sizeof(buf));
105 if(!X509_LOOKUP_load_file(lookup, buf, X509_FILETYPE_PEM)){
106 q_status_message1(SM_ORDER, 3, 3, _("Error loading file %s"), buf);
107 ret = -1;
113 closedir(dirp);
116 return ret;
121 * Get an X509_STORE. This consists of the system
122 * certs directory and any certificates in the user's
123 * ~/.alpine-smime/ca directory.
125 X509_STORE *
126 get_ca_store(void)
128 X509_LOOKUP *lookup;
129 X509_STORE *store = NULL;
131 dprint((9, "get_ca_store()"));
133 if(!(store=X509_STORE_new())){
134 dprint((9, "X509_STORE_new() failed"));
135 return store;
138 if(!(lookup=X509_STORE_add_lookup(store, X509_LOOKUP_file()))){
139 dprint((9, "X509_STORE_add_lookup() failed"));
140 X509_STORE_free(store);
141 return NULL;
144 if(ps_global->smime && ps_global->smime->catype == Container
145 && ps_global->smime->cacontent){
147 if(!mem_add_extra_cacerts(ps_global->smime->cacontent, lookup)){
148 X509_STORE_free(store);
149 return NULL;
152 else if(ps_global->smime && ps_global->smime->catype == Directory
153 && ps_global->smime->capath){
154 if(add_certs_in_dir(lookup, ps_global->smime->capath) < 0){
155 X509_STORE_free(store);
156 return NULL;
160 if(!(lookup=X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()))){
161 X509_STORE_free(store);
162 return NULL;
165 #ifdef SMIME_SSLCERTS
166 dprint((9, "get_ca_store(): adding cacerts from %s", SMIME_SSLCERTS));
167 X509_LOOKUP_add_dir(lookup, SMIME_SSLCERTS, X509_FILETYPE_PEM);
168 #endif
170 return store;
174 EVP_PKEY *
175 load_key(PERSONAL_CERT *pc, char *pass)
177 BIO *in;
178 EVP_PKEY *key = NULL;
179 char buf[MAXPATH], file[MAXPATH];
181 if(!(ps_global->smime && pc && pc->name))
182 return key;
184 if(ps_global->smime->privatetype == Container){
185 char *q;
187 if(pc->keytext && (q = strstr(pc->keytext, "-----END")) != NULL){
188 while(*q && *q != '\n')
189 q++;
191 if(*q == '\n')
192 q++;
194 if((in = BIO_new_mem_buf(pc->keytext, q-pc->keytext)) != NULL){
195 key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass);
196 BIO_free(in);
200 else if(ps_global->smime->privatetype == Directory){
201 /* filename is path/name.key */
202 strncpy(buf, pc->name, sizeof(buf)-5);
203 buf[sizeof(buf)-5] = '\0';
204 strncat(buf, ".key", 5);
205 build_path(file, ps_global->smime->privatepath, buf, sizeof(file));
207 if(!(in = BIO_new_file(file, "r")))
208 return NULL;
210 key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass);
211 BIO_free(in);
214 return key;
218 #ifdef notdef
219 static char *
220 get_x509_name_entry(const char *key, X509_NAME *name)
222 int i, c, n;
223 char buf[256];
224 char *id;
226 if(!name)
227 return NULL;
229 c = X509_NAME_entry_count(name);
231 for(i=0; i<c; i++){
232 X509_NAME_ENTRY *e;
234 e = X509_NAME_get_entry(name, i);
235 if(!e)
236 continue;
238 buf[0] = 0;
239 id = buf;
241 n = OBJ_obj2nid(e->object);
242 if((n == NID_undef) || ((id=(char*) OBJ_nid2sn(n)) == NULL)){
243 i2t_ASN1_OBJECT(buf, sizeof(buf), e->object);
244 id = buf;
247 if((strucmp(id, "email")==0) || (strucmp(id, "emailAddress")==0)){
248 X509_NAME_get_text_by_OBJ(name, e->object, buf, sizeof(buf)-1);
249 return cpystr(buf);
253 return NULL;
257 char *
258 get_x509_subject_email(X509 *x)
260 char* result;
261 result = get_x509_name_entry("email", X509_get_subject_name(x));
262 if( !result ){
263 result = get_x509_name_entry("emailAddress", X509_get_subject_name(x));
266 return result;
268 #endif /* notdef */
270 #include <openssl/x509v3.h>
272 * This newer version is from Adrian Vogel. It looks for the email
273 * address not only in the email address field, but also in an
274 * X509v3 extension field, Subject Altenative Name.
276 char **
277 get_x509_subject_email(X509 *x)
279 char **result = NULL;
280 int i, n;
281 STACK_OF(OPENSSL_STRING) *emails = X509_get1_email(x);
282 if ((n = sk_OPENSSL_STRING_num(emails)) > 0) {
283 result = fs_get((n+1)*sizeof(char *));
284 for(i = 0; i < n; i++)
285 result[i] = cpystr(sk_OPENSSL_STRING_value(emails, i));
286 result[i] = NULL;
288 X509_email_free(emails);
289 return result;
294 * Save the certificate for the given email address in
295 * ~/.alpine-smime/public.
297 * Should consider the security hazards in making a file with
298 * the email address that has come from the certificate.
300 * The argument email is destroyed.
302 void
303 save_cert_for(char *email, X509 *cert)
305 if(!ps_global->smime)
306 return;
308 dprint((9, "save_cert_for(%s)", email ? email : "?"));
309 emailstrclean(email);
311 if(ps_global->smime->publictype == Keychain){
312 #ifdef APPLEKEYCHAIN
314 OSStatus rc;
315 SecCertificateRef secCertificateRef;
316 CSSM_DATA certData;
318 memset((void *) &certData, 0, sizeof(certData));
319 memset((void *) &secCertificateRef, 0, sizeof(secCertificateRef));
321 /* convert OpenSSL X509 cert data to MacOS certData */
322 if((certData.Length = i2d_X509(cert, &(certData.Data))) > 0){
325 * Put that certData into a SecCertificateRef.
326 * Version 3 should work for versions 1-3.
328 if(!(rc=SecCertificateCreateFromData(&certData,
329 CSSM_CERT_X_509v3,
330 CSSM_CERT_ENCODING_DER,
331 &secCertificateRef))){
333 /* add it to the default keychain */
334 if(!(rc=SecCertificateAddToKeychain(secCertificateRef, NULL))){
335 /* ok */
337 else if(rc == errSecDuplicateItem){
338 dprint((9, "save_cert_for: certificate for %s already in keychain", email));
340 else{
341 dprint((9, "SecCertificateAddToKeychain failed"));
344 else{
345 dprint((9, "SecCertificateCreateFromData failed"));
348 else{
349 dprint((9, "i2d_X509 failed"));
352 #endif /* APPLEKEYCHAIN */
354 else if(ps_global->smime->publictype == Container){
355 REMDATA_S *rd = NULL;
356 char path[MAXPATH];
357 char *tempfile = NULL;
358 int err = 0;
360 add_to_end_of_certlist(&ps_global->smime->publiccertlist, email, X509_dup(cert));
362 if(!ps_global->smime->publicpath)
363 return;
365 if(IS_REMOTE(ps_global->smime->publicpath)){
366 rd = rd_create_remote(RemImap, ps_global->smime->publicpath, REMOTE_SMIME_SUBTYPE,
367 NULL, "Error: ",
368 _("Can't access remote smime configuration."));
369 if(!rd)
370 return;
372 (void) rd_read_metadata(rd);
374 if(rd->access == MaybeRorW){
375 if(rd->read_status == 'R')
376 rd->access = ReadOnly;
377 else
378 rd->access = ReadWrite;
381 if(rd->access != NoExists){
383 rd_check_remvalid(rd, 1L);
386 * If the cached info says it is readonly but
387 * it looks like it's been fixed now, change it to readwrite.
389 if(rd->read_status == 'R'){
390 rd_check_readonly_access(rd);
391 if(rd->read_status == 'W'){
392 rd->access = ReadWrite;
393 rd->flags |= REM_OUTOFDATE;
395 else
396 rd->access = ReadOnly;
400 if(rd->flags & REM_OUTOFDATE){
401 if(rd_update_local(rd) != 0){
403 dprint((1, "save_cert_for: rd_update_local failed\n"));
404 rd_close_remdata(&rd);
405 return;
408 else
409 rd_open_remote(rd);
411 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
412 rd_close_remdata(&rd);
413 return;
416 rd->flags |= DO_REMTRIM;
418 strncpy(path, rd->lf, sizeof(path)-1);
419 path[sizeof(path)-1] = '\0';
421 else{
422 strncpy(path, ps_global->smime->publicpath, sizeof(path)-1);
423 path[sizeof(path)-1] = '\0';
426 tempfile = tempfile_in_same_dir(path, "az", NULL);
427 if(tempfile){
428 if(certlist_to_file(tempfile, ps_global->smime->publiccertlist))
429 err++;
431 if(!err){
432 if(rename_file(tempfile, path) < 0){
433 q_status_message2(SM_ORDER, 3, 3,
434 _("Can't rename %s to %s"), tempfile, path);
435 err++;
439 if(!err && IS_REMOTE(ps_global->smime->publicpath)){
440 int e, we_cancel;
441 char datebuf[200];
443 datebuf[0] = '\0';
445 we_cancel = busy_cue(_("Copying to remote smime container"), NULL, 1);
446 if((e = rd_update_remote(rd, datebuf)) != 0){
447 if(e == -1){
448 q_status_message2(SM_ORDER | SM_DING, 3, 5,
449 _("Error opening temporary smime file %s: %s"),
450 rd->lf, error_description(errno));
451 dprint((1,
452 "write_remote_smime: error opening temp file %s\n",
453 rd->lf ? rd->lf : "?"));
455 else{
456 q_status_message2(SM_ORDER | SM_DING, 3, 5,
457 _("Error copying to %s: %s"),
458 rd->rn, error_description(errno));
459 dprint((1,
460 "write_remote_smime: error copying from %s to %s\n",
461 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
464 q_status_message(SM_ORDER | SM_DING, 5, 5,
465 _("Copy of smime cert to remote folder failed, changes NOT saved remotely"));
467 else{
468 rd_update_metadata(rd, datebuf);
469 rd->read_status = 'W';
472 rd_close_remdata(&rd);
474 if(we_cancel)
475 cancel_busy_cue(-1);
478 fs_give((void **) &tempfile);
481 else if(ps_global->smime->publictype == Directory){
482 char certfilename[MAXPATH];
483 BIO *bio_out;
485 build_path(certfilename, ps_global->smime->publicpath,
486 email, sizeof(certfilename));
487 strncat(certfilename, ".crt", sizeof(certfilename)-1-strlen(certfilename));
488 certfilename[sizeof(certfilename)-1] = 0;
490 bio_out = BIO_new_file(certfilename, "w");
491 if(bio_out){
492 PEM_write_bio_X509(bio_out, cert);
493 BIO_free(bio_out);
494 q_status_message1(SM_ORDER, 1, 1, _("Saved certificate for <%s>"), email);
496 else{
497 q_status_message1(SM_ORDER, 1, 1, _("Couldn't save certificate for <%s>"), email);
504 * Try to retrieve the certificate for the given email address.
505 * The caller should free the cert.
507 X509 *
508 get_cert_for(char *email)
510 char *path;
511 char certfilename[MAXPATH];
512 char emailaddr[MAXPATH];
513 X509 *cert = NULL;
514 BIO *in;
516 if(!ps_global->smime)
517 return cert;
519 dprint((9, "get_cert_for(%s)", email ? email : "?"));
521 strncpy(emailaddr, email, sizeof(emailaddr)-1);
522 emailaddr[sizeof(emailaddr)-1] = 0;
524 /* clean it up (lowercase, space removal) */
525 emailstrclean(emailaddr);
527 if(ps_global->smime->publictype == Keychain){
528 #ifdef APPLEKEYCHAIN
530 OSStatus rc;
531 SecKeychainItemRef itemRef = nil;
532 SecKeychainAttributeList attrList;
533 SecKeychainAttribute attrib;
534 SecKeychainSearchRef searchRef = nil;
535 CSSM_DATA certData;
537 /* low-level form of MacOS data */
538 memset((void *) &certData, 0, sizeof(certData));
540 attrList.count = 1;
541 attrList.attr = &attrib;
543 /* kSecAlias means email address for a certificate */
544 attrib.tag = kSecAlias;
545 attrib.data = emailaddr;
546 attrib.length = strlen(attrib.data);
548 /* Find the certificate in the default keychain */
549 if(!(rc=SecKeychainSearchCreateFromAttributes(NULL,
550 kSecCertificateItemClass,
551 &attrList,
552 &searchRef))){
554 if(!(rc=SecKeychainSearchCopyNext(searchRef, &itemRef))){
556 /* extract the data portion of the certificate */
557 if(!(rc=SecCertificateGetData((SecCertificateRef) itemRef, &certData))){
560 * Convert it from MacOS form to OpenSSL form.
561 * The input is certData from above and the output
562 * is the X509 *cert.
564 if(!d2i_X509(&cert, &(certData.Data), certData.Length)){
565 dprint((9, "d2i_X509 failed"));
568 else{
569 dprint((9, "SecCertificateGetData failed"));
572 else if(rc == errSecItemNotFound){
573 dprint((9, "get_cert_for: Public cert for %s not found", emailaddr));
575 else{
576 dprint((9, "SecKeychainSearchCopyNext failed"));
579 else{
580 dprint((9, "SecKeychainSearchCreateFromAttributes failed"));
583 if(searchRef)
584 CFRelease(searchRef);
586 #endif /* APPLEKEYCHAIN */
588 else if(ps_global->smime->publictype == Container){
589 if(ps_global->smime->publiccertlist){
590 CertList *cl;
592 for(cl = ps_global->smime->publiccertlist; cl; cl = cl->next){
593 if(cl->name && !strucmp(emailaddr, cl->name))
594 break;
597 if(cl)
598 cert = X509_dup((X509 *) cl->x509_cert);
601 else if(ps_global->smime->publictype == Directory){
602 path = ps_global->smime->publicpath;
603 build_path(certfilename, path, emailaddr, sizeof(certfilename));
604 strncat(certfilename, ".crt", sizeof(certfilename)-1-strlen(certfilename));
605 certfilename[sizeof(certfilename)-1] = 0;
607 if((in = BIO_new_file(certfilename, "r"))!=0){
609 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
611 if(cert){
612 /* could check email addr in cert matches */
615 BIO_free(in);
619 return cert;
623 PERSONAL_CERT *
624 mem_to_personal_certs(char *contents)
626 PERSONAL_CERT *result = NULL;
627 char *p, *q, *line, *name, *keytext, *save_p;
628 X509 *cert = NULL;
630 if(contents && *contents){
631 for(p = contents; *p != '\0';){
632 line = p;
634 while(*p && *p != '\n')
635 p++;
637 save_p = NULL;
638 if(*p == '\n'){
639 save_p = p;
640 *p++ = '\0';
643 if(strncmp(EMAILADDRLEADER, line, strlen(EMAILADDRLEADER)) == 0){
644 name = line + strlen(EMAILADDRLEADER);
645 cert = get_cert_for(name);
646 keytext = p;
648 /* advance p past this record */
649 if((q = strstr(keytext, "-----END")) != NULL){
650 while(*q && *q != '\n')
651 q++;
653 if(*q == '\n')
654 q++;
656 p = q;
658 else{
659 p = p + strlen(p);
660 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error in privatekey container, missing END"));
663 if(cert){
664 PERSONAL_CERT *pc;
666 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
667 pc->cert = cert;
668 pc->name = cpystr(name);
669 pc->keytext = keytext; /* a pointer into contents */
671 pc->key = load_key(pc, "");
673 pc->next = result;
674 result = pc;
678 if(save_p)
679 *save_p = '\n';
683 return result;
687 CertList *
688 mem_to_certlist(char *contents)
690 CertList *ret = NULL;
691 char *p, *q, *line, *name, *certtext, *save_p;
692 X509 *cert = NULL;
693 BIO *in;
695 if(contents && *contents){
696 for(p = contents; *p != '\0';){
697 line = p;
699 while(*p && *p != '\n')
700 p++;
702 save_p = NULL;
703 if(*p == '\n'){
704 save_p = p;
705 *p++ = '\0';
708 if(strncmp(EMAILADDRLEADER, line, strlen(EMAILADDRLEADER)) == 0){
709 name = line + strlen(EMAILADDRLEADER);
710 cert = NULL;
711 certtext = p;
712 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
713 if((q = strstr(certtext, "-----END")) != NULL){
714 while(*q && *q != '\n')
715 q++;
717 if(*q == '\n')
718 q++;
720 p = q;
722 if((in = BIO_new_mem_buf(certtext, q-certtext)) != 0){
723 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
725 BIO_free(in);
729 else{
730 p = p + strlen(p);
731 q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in publiccert container, missing BEGIN, certtext=%s"), certtext);
734 if(name && cert){
735 add_to_end_of_certlist(&ret, name, cert);
739 if(save_p)
740 *save_p = '\n';
744 return ret;
749 * Add the CACert Container contents into the CACert store.
751 * Returns > 0 for success, 0 for failure
754 mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup)
756 char *p, *q, *line, *certtext, *save_p;
757 BIO *in, *out;
758 int len, failed = 0;
759 char *tempfile;
760 char iobuf[4096];
763 * The most straight-forward way to do this is to write
764 * the container contents to a temp file and then load the
765 * contents of the file with X509_LOOKUP_load_file(), like
766 * is done in add_certs_in_dir(). What we don't know is if
767 * each file should consist of one cacert or if they can all
768 * just be jammed together into one file. To be safe, we'll use
769 * one file per and do each in a separate operation.
772 if(contents && *contents){
773 for(p = contents; *p != '\0';){
774 line = p;
776 while(*p && *p != '\n')
777 p++;
779 save_p = NULL;
780 if(*p == '\n'){
781 save_p = p;
782 *p++ = '\0';
785 /* look for separator line */
786 if(strncmp(CACERTSTORELEADER, line, strlen(CACERTSTORELEADER)) == 0){
787 /* certtext is the content that should go in a file */
788 certtext = p;
789 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
790 if((q = strstr(certtext, CACERTSTORELEADER)) != NULL){
791 p = q;
793 else{ /* end of file */
794 q = certtext + strlen(certtext);
795 p = q;
798 in = BIO_new_mem_buf(certtext, q-certtext);
799 if(in){
800 tempfile = temp_nam(NULL, "az");
801 out = NULL;
802 if(tempfile)
803 out = BIO_new_file(tempfile, "w");
805 if(out){
806 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
807 BIO_write(out, iobuf, len);
809 BIO_free(out);
810 if(!X509_LOOKUP_load_file(lookup, tempfile, X509_FILETYPE_PEM))
811 failed++;
813 fs_give((void **) &tempfile);
816 BIO_free(in);
819 else{
820 p = p + strlen(p);
821 q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in cacert container, missing BEGIN, certtext=%s"), certtext);
824 else{
825 p = p + strlen(p);
826 q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in cacert container, missing separator, line=%s"), line);
829 if(save_p)
830 *save_p = '\n';
834 return(!failed);
839 certlist_to_file(char *filename, CertList *certlist)
841 CertList *cl;
842 BIO *bio_out = NULL;
843 int ret = -1;
845 if(filename && (bio_out=BIO_new_file(filename, "w")) != NULL){
846 ret = 0;
847 for(cl = certlist; cl; cl = cl->next){
848 if(cl->name && cl->name[0] && cl->x509_cert){
849 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
850 && (BIO_puts(bio_out, cl->name) > 0)
851 && (BIO_puts(bio_out, "\n") > 0)))
852 ret = -1;
854 if(!PEM_write_bio_X509(bio_out, (X509 *) cl->x509_cert))
855 ret = -1;
859 BIO_free(bio_out);
862 return ret;
866 void
867 add_to_end_of_certlist(CertList **cl, char *name, X509 *cert)
869 CertList *new, *clp;
871 if(!cl)
872 return;
874 new = (CertList *) fs_get(sizeof(*new));
875 memset((void *) new, 0, sizeof(*new));
876 new->x509_cert = cert;
877 new->name = name ? cpystr(name) : NULL;
879 if(!*cl){
880 *cl = new;
882 else{
883 for(clp = (*cl); clp->next; clp = clp->next)
886 clp->next = new;
891 void
892 free_certlist(CertList **cl)
894 if(cl && *cl){
895 free_certlist(&(*cl)->next);
896 if((*cl)->name)
897 fs_give((void **) &(*cl)->name);
899 if((*cl)->x509_cert)
900 X509_free((X509 *) (*cl)->x509_cert);
902 fs_give((void **) cl);
907 void
908 free_personal_certs(PERSONAL_CERT **pc)
910 if(pc && *pc){
911 free_personal_certs(&(*pc)->next);
912 if((*pc)->name)
913 fs_give((void **) &(*pc)->name);
915 if((*pc)->name)
916 fs_give((void **) &(*pc)->name);
918 if((*pc)->cert)
919 X509_free((*pc)->cert);
921 if((*pc)->key)
922 EVP_PKEY_free((*pc)->key);
924 fs_give((void **) pc);
928 #endif /* SMIME */