* Forwarding messages with attachments of content-type multipart,
[alpine.git] / pith / smkeys.c
blob7aac6e4c33f487c5909bd155cb59cfd5da0d57c4
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 2013-2014 Eduardo Chappa
8 * Copyright 2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
20 * This is based on a contribution from Jonathan Paisley, see smime.c
24 #include "../pith/headers.h"
26 #ifdef SMIME
28 #include "../pith/status.h"
29 #include "../pith/conf.h"
30 #include "../pith/remote.h"
31 #include "../pith/tempfile.h"
32 #include "../pith/busy.h"
33 #include "../pith/osdep/lstcmpnt.h"
34 #include "../pith/util.h"
35 #include "smkeys.h"
37 #ifdef APPLEKEYCHAIN
38 #include <Security/SecKeychain.h>
39 #include <Security/SecKeychainItem.h>
40 #include <Security/SecKeychainSearch.h>
41 #include <Security/SecCertificate.h>
42 #endif /* APPLEKEYCHAIN */
45 /* internal prototypes */
46 static char *emailstrclean(char *string);
47 static int certlist_to_file(char *filename, CertList *certlist);
48 static int mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup);
50 /* smime_expunge_cert.
51 * Return values: < 0 there was an error.
52 * >=0 the number of messages expunged
54 int
55 smime_expunge_cert(WhichCerts ctype)
57 int count;
58 CertList *cl, *dummy, *data;
59 char *path, *ext, buf[MAXPATH+1];
61 if(DATACERT(ctype)== NULL)
62 return -1;
64 /* data cert is the way we unify certificate management across functions, but it is
65 * not where we really save the information in the case ctype is equal to Private.
66 * What we will do is to update the datacert, and in the case of ctype equal to Private
67 * use the updated certdata to update the personal_certs data.
70 path = PATHCERTDIR(ctype);
71 ext = EXTCERT(ctype);
73 if(path){
74 /* add a fake certificate at the beginning of the list */
75 dummy = fs_get(sizeof(CertList));
76 memset((void *)dummy, 0, sizeof(CertList));
77 dummy->next = DATACERT(ctype);
79 for(cl = dummy, count = 0; cl && cl->next;){
80 if(cl->next->data.deleted == 0){
81 cl = cl->next;
82 continue;
85 build_path(buf, path, cl->next->name, sizeof(buf));
86 if(ctype == Private && strlen(buf) + strlen(EXTCERT(Private)) < sizeof(buf))
87 strcat(buf, EXTCERT(Private));
89 if(our_unlink(buf) < 0)
90 q_status_message1(SM_ORDER, 3, 3, _("Error removing certificate %s"), cl->next->name);
91 else {
92 count++; /* count it! */
93 data = cl->next;
94 cl->next = data->next;
95 if(data->name) fs_give((void **)&data->name);
96 fs_give((void **)&data);
99 } else
100 q_status_message1(SM_ORDER, 3, 3, _("Error removing certificate %s"), cl->name);
102 switch(ctype){
103 case Private: ps_global->smime->privatecertdata = dummy->next; break;
104 case Public : ps_global->smime->publiccertdata = dummy->next; break;
105 case CACert : ps_global->smime->cacertdata = dummy->next; break;
106 default : break;
108 fs_give((void **)&dummy);
109 if(count > 0)
110 q_status_message2(SM_ORDER, 3, 3, _("Removed %s certificate%s"), comatose(count), plural(count));
111 else
112 q_status_message(SM_ORDER, 3, 3, _("Error: No certificates were removed"));
113 return count;
116 void
117 mark_cert_deleted(WhichCerts ctype, char *email, unsigned state)
119 CertList *cl;
120 char tmp[200];
122 snprintf(tmp, sizeof(tmp), "%s%s", email, ctype == Private ? "" : ".crt");
123 tmp[sizeof(tmp)-1] = '\0';
124 for(cl = DATACERT(ctype); cl != NULL && strcmp(cl->name, tmp); cl = cl->next);
125 cl->data.deleted = state;
128 unsigned
129 get_cert_deleted(WhichCerts ctype, char *email)
131 CertList *cl;
133 for(cl = DATACERT(ctype); cl != NULL && strcmp(cl->name, email); cl = cl->next);
134 return (cl && cl->data.deleted) ? 1 : 0;
137 void
138 get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen)
140 unsigned char md[128];
141 char *b;
142 unsigned int len, i;
144 len = sizeof(md);
146 X509_digest(cert, type, md, &len);
148 b = buf;
149 *b = 0;
150 for(i=0; i<len; i++){
151 if(b-buf+3>=maxLen)
152 break;
154 if(i != 0)
155 *b++ = ':';
157 snprintf(b, maxLen - (b-buf), "%02x", md[i]);
158 b+=2;
164 * Remove leading whitespace, trailing whitespace and convert
165 * to lowercase. Also remove slash characters
167 * Args: s, -- The string to clean
169 * Result: the cleaned string
171 static char *
172 emailstrclean(char *string)
174 char *s = string, *sc = NULL, *p = NULL;
176 for(; *s; s++){ /* single pass */
177 if(!isspace((unsigned char) (*s))){
178 p = NULL; /* not start of blanks */
179 if(!sc) /* first non-blank? */
180 sc = string; /* start copying */
182 else if(!p) /* it's OK if sc == NULL */
183 p = sc; /* start of blanks? */
185 if(sc && *s!='/' && *s!='\\') /* if copying, copy */
186 *sc++ = isupper((unsigned char) (*s))
187 ? (unsigned char) tolower((unsigned char) (*s))
188 : (unsigned char) (*s);
191 if(p) /* if ending blanks */
192 *p = '\0'; /* tie off beginning */
193 else if(!sc) /* never saw a non-blank */
194 *string = '\0'; /* so tie whole thing off */
196 return(string);
201 * Add a lookup for each "*.crt" file in the given directory.
204 add_certs_in_dir(X509_LOOKUP *lookup, char *path, char *ext, CertList **cdata)
206 char buf[MAXPATH];
207 struct direct *d;
208 DIR *dirp;
209 CertList *cert, *cl;
210 int ret = 0;
212 if((dirp = opendir(path)) != NULL){
213 while(!ret && (d=readdir(dirp)) != NULL){
214 if(srchrstr(d->d_name, ext)){
215 build_path(buf, path, d->d_name, sizeof(buf));
217 if(!X509_LOOKUP_load_file(lookup, buf, X509_FILETYPE_PEM)){
218 q_status_message1(SM_ORDER, 3, 3, _("Error loading file %s"), buf);
219 ret = -1;
220 } else {
221 if(cdata){
222 cert = fs_get(sizeof(CertList));
223 memset((void *)cert, 0, sizeof(CertList));
224 cert->name = cpystr(d->d_name);
225 if(*cdata == NULL)
226 *cdata = cert;
227 else{
228 for (cl = *cdata; cl && cl->next; cl = cl->next);
229 cl->next = cert;
238 closedir(dirp);
241 return ret;
246 * Get an X509_STORE. This consists of the system
247 * certs directory and any certificates in the user's
248 * ~/.alpine-smime/ca directory.
250 X509_STORE *
251 get_ca_store(void)
253 X509_LOOKUP *lookup;
254 X509_STORE *store = NULL;
256 dprint((9, "get_ca_store()"));
258 if(!(store=X509_STORE_new())){
259 dprint((9, "X509_STORE_new() failed"));
260 return store;
263 if(!(lookup=X509_STORE_add_lookup(store, X509_LOOKUP_file()))){
264 dprint((9, "X509_STORE_add_lookup() failed"));
265 X509_STORE_free(store);
266 return NULL;
269 if(ps_global->smime && ps_global->smime->catype == Container
270 && ps_global->smime->cacontent){
272 if(!mem_add_extra_cacerts(ps_global->smime->cacontent, lookup)){
273 X509_STORE_free(store);
274 return NULL;
277 else if(ps_global->smime && ps_global->smime->catype == Directory
278 && ps_global->smime->capath){
279 if(add_certs_in_dir(lookup, ps_global->smime->capath, ".crt", &ps_global->smime->cacertdata) < 0){
280 X509_STORE_free(store);
281 return NULL;
285 if(!(lookup=X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()))){
286 X509_STORE_free(store);
287 return NULL;
290 #ifdef SMIME_SSLCERTS
291 dprint((9, "get_ca_store(): adding cacerts from %s", SMIME_SSLCERTS));
292 X509_LOOKUP_add_dir(lookup, SMIME_SSLCERTS, X509_FILETYPE_PEM);
293 #endif
295 return store;
299 EVP_PKEY *
300 load_key(PERSONAL_CERT *pc, char *pass)
302 BIO *in;
303 EVP_PKEY *key = NULL;
304 char buf[MAXPATH], file[MAXPATH];
306 if(!(ps_global->smime && pc && pc->name))
307 return key;
309 if(ps_global->smime->privatetype == Container){
310 char *q;
312 if(pc->keytext && (q = strstr(pc->keytext, "-----END")) != NULL){
313 while(*q && *q != '\n')
314 q++;
316 if(*q == '\n')
317 q++;
319 if((in = BIO_new_mem_buf(pc->keytext, q-pc->keytext)) != NULL){
320 key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass);
321 BIO_free(in);
325 else if(ps_global->smime->privatetype == Directory){
326 /* filename is path/name.key */
327 strncpy(buf, pc->name, sizeof(buf)-5);
328 buf[sizeof(buf)-5] = '\0';
329 strncat(buf, ".key", 5);
330 build_path(file, ps_global->smime->privatepath, buf, sizeof(file));
332 if(!(in = BIO_new_file(file, "r")))
333 return NULL;
335 key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass);
336 BIO_free(in);
339 return key;
343 #ifdef notdef
344 static char *
345 get_x509_name_entry(const char *key, X509_NAME *name)
347 int i, c, n;
348 char buf[256];
349 char *id;
351 if(!name)
352 return NULL;
354 c = X509_NAME_entry_count(name);
356 for(i=0; i<c; i++){
357 X509_NAME_ENTRY *e;
359 e = X509_NAME_get_entry(name, i);
360 if(!e)
361 continue;
363 buf[0] = 0;
364 id = buf;
366 n = OBJ_obj2nid(e->object);
367 if((n == NID_undef) || ((id=(char*) OBJ_nid2sn(n)) == NULL)){
368 i2t_ASN1_OBJECT(buf, sizeof(buf), e->object);
369 id = buf;
372 if((strucmp(id, "email")==0) || (strucmp(id, "emailAddress")==0)){
373 X509_NAME_get_text_by_OBJ(name, e->object, buf, sizeof(buf)-1);
374 return cpystr(buf);
378 return NULL;
382 char *
383 get_x509_subject_email(X509 *x)
385 char* result;
386 result = get_x509_name_entry("email", X509_get_subject_name(x));
387 if( !result ){
388 result = get_x509_name_entry("emailAddress", X509_get_subject_name(x));
391 return result;
393 #endif /* notdef */
395 #include <openssl/x509v3.h>
397 * This newer version is from Adrian Vogel. It looks for the email
398 * address not only in the email address field, but also in an
399 * X509v3 extension field, Subject Altenative Name.
401 char **
402 get_x509_subject_email(X509 *x)
404 char **result = NULL;
405 int i, n;
406 STACK_OF(OPENSSL_STRING) *emails = X509_get1_email(x);
407 if ((n = sk_OPENSSL_STRING_num(emails)) > 0) {
408 result = fs_get((n+1)*sizeof(char *));
409 for(i = 0; i < n; i++)
410 result[i] = cpystr(sk_OPENSSL_STRING_value(emails, i));
411 result[i] = NULL;
413 X509_email_free(emails);
414 return result;
419 * Save the certificate for the given email address in
420 * ~/.alpine-smime/public.
422 * Should consider the security hazards in making a file with
423 * the email address that has come from the certificate.
425 * The argument email is destroyed.
427 * args: ctype says where the user wants to save the certificate
429 void
430 save_cert_for(char *email, X509 *cert, WhichCerts ctype)
432 if(!ps_global->smime || ctype == Private)
433 return;
435 dprint((9, "save_cert_for(%s, %s)", email ? email : "?", ctype == Public ? _("Public") : ctype == Private ? _("Private") : "CACert"));
436 emailstrclean(email);
438 if(ps_global->smime->publictype == Keychain){
439 #ifdef APPLEKEYCHAIN
441 OSStatus rc;
442 SecCertificateRef secCertificateRef;
443 CSSM_DATA certData;
445 memset((void *) &certData, 0, sizeof(certData));
446 memset((void *) &secCertificateRef, 0, sizeof(secCertificateRef));
448 /* convert OpenSSL X509 cert data to MacOS certData */
449 if((certData.Length = i2d_X509(cert, &(certData.Data))) > 0){
452 * Put that certData into a SecCertificateRef.
453 * Version 3 should work for versions 1-3.
455 if(!(rc=SecCertificateCreateFromData(&certData,
456 CSSM_CERT_X_509v3,
457 CSSM_CERT_ENCODING_DER,
458 &secCertificateRef))){
460 /* add it to the default keychain */
461 if(!(rc=SecCertificateAddToKeychain(secCertificateRef, NULL))){
462 /* ok */
464 else if(rc == errSecDuplicateItem){
465 dprint((9, "save_cert_for: certificate for %s already in keychain", email));
467 else{
468 dprint((9, "SecCertificateAddToKeychain failed"));
471 else{
472 dprint((9, "SecCertificateCreateFromData failed"));
475 else{
476 dprint((9, "i2d_X509 failed"));
479 #endif /* APPLEKEYCHAIN */
481 else if(ps_global->smime->publictype == Container){
482 REMDATA_S *rd = NULL;
483 char path[MAXPATH];
484 char *upath = PATHCERTDIR(ctype);
485 char *tempfile = NULL;
486 int err = 0;
488 add_to_end_of_certlist(&ps_global->smime->publiccertlist, email, X509_dup(cert));
490 if(!upath)
491 return;
493 if(IS_REMOTE(upath)){
494 rd = rd_create_remote(RemImap, upath, REMOTE_SMIME_SUBTYPE,
495 NULL, "Error: ",
496 _("Can't access remote smime configuration."));
497 if(!rd){
498 return;
501 (void) rd_read_metadata(rd);
503 if(rd->access == MaybeRorW){
504 if(rd->read_status == 'R')
505 rd->access = ReadOnly;
506 else
507 rd->access = ReadWrite;
510 if(rd->access != NoExists){
512 rd_check_remvalid(rd, 1L);
515 * If the cached info says it is readonly but
516 * it looks like it's been fixed now, change it to readwrite.
518 if(rd->read_status == 'R'){
519 rd_check_readonly_access(rd);
520 if(rd->read_status == 'W'){
521 rd->access = ReadWrite;
522 rd->flags |= REM_OUTOFDATE;
524 else
525 rd->access = ReadOnly;
529 if(rd->flags & REM_OUTOFDATE){
530 if(rd_update_local(rd) != 0){
532 dprint((1, "save_cert_for: rd_update_local failed\n"));
533 rd_close_remdata(&rd);
534 return;
537 else
538 rd_open_remote(rd);
540 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
541 rd_close_remdata(&rd);
542 return;
545 rd->flags |= DO_REMTRIM;
547 strncpy(path, rd->lf, sizeof(path)-1);
548 path[sizeof(path)-1] = '\0';
550 else{
551 strncpy(path, upath, sizeof(path)-1);
552 path[sizeof(path)-1] = '\0';
555 tempfile = tempfile_in_same_dir(path, "az", NULL);
556 if(tempfile){
557 if(certlist_to_file(tempfile, ps_global->smime->publiccertlist))
558 err++;
560 if(!err){
561 if(rename_file(tempfile, path) < 0){
562 q_status_message2(SM_ORDER, 3, 3,
563 _("Can't rename %s to %s"), tempfile, path);
564 err++;
568 if(!err && IS_REMOTE(upath)){
569 int e, we_cancel;
570 char datebuf[200];
572 datebuf[0] = '\0';
574 we_cancel = busy_cue(_("Copying to remote smime container"), NULL, 1);
575 if((e = rd_update_remote(rd, datebuf)) != 0){
576 if(e == -1){
577 q_status_message2(SM_ORDER | SM_DING, 3, 5,
578 _("Error opening temporary smime file %s: %s"),
579 rd->lf, error_description(errno));
580 dprint((1,
581 "write_remote_smime: error opening temp file %s\n",
582 rd->lf ? rd->lf : "?"));
584 else{
585 q_status_message2(SM_ORDER | SM_DING, 3, 5,
586 _("Error copying to %s: %s"),
587 rd->rn, error_description(errno));
588 dprint((1,
589 "write_remote_smime: error copying from %s to %s\n",
590 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
593 q_status_message(SM_ORDER | SM_DING, 5, 5,
594 _("Copy of smime cert to remote folder failed, changes NOT saved remotely"));
596 else{
597 rd_update_metadata(rd, datebuf);
598 rd->read_status = 'W';
601 rd_close_remdata(&rd);
603 if(we_cancel)
604 cancel_busy_cue(-1);
607 fs_give((void **) &tempfile);
610 else if(ps_global->smime->publictype == Directory){
611 char *path = PATHCERTDIR(ctype);
612 char certfilename[MAXPATH];
613 BIO *bio_out;
615 build_path(certfilename, path, email, sizeof(certfilename));
616 strncat(certfilename, ".crt", sizeof(certfilename)-1-strlen(certfilename));
617 certfilename[sizeof(certfilename)-1] = 0;
619 bio_out = BIO_new_file(certfilename, "w");
620 if(bio_out){
621 PEM_write_bio_X509(bio_out, cert);
622 BIO_free(bio_out);
623 q_status_message1(SM_ORDER, 1, 1, _("Saved certificate for <%s>"), email);
625 else{
626 q_status_message1(SM_ORDER, 1, 1, _("Couldn't save certificate for <%s>"), email);
633 * Try to retrieve the certificate for the given email address.
634 * The caller should free the cert.
636 X509 *
637 get_cert_for(char *email, WhichCerts ctype)
639 char *path;
640 char certfilename[MAXPATH];
641 char emailaddr[MAXPATH];
642 X509 *cert = NULL;
643 BIO *in;
645 if(!ps_global->smime)
646 return cert;
648 dprint((9, "get_cert_for(%s, %s)", email ? email : "?", "none yet"));
650 if(ctype == Private) /* there is no private certificate info */
651 ctype = Public; /* return public information instead */
652 strncpy(emailaddr, email, sizeof(emailaddr)-1);
653 emailaddr[sizeof(emailaddr)-1] = 0;
655 /* clean it up (lowercase, space removal) */
656 emailstrclean(emailaddr);
658 if(ps_global->smime->publictype == Keychain){
659 #ifdef APPLEKEYCHAIN
661 OSStatus rc;
662 SecKeychainItemRef itemRef = nil;
663 SecKeychainAttributeList attrList;
664 SecKeychainAttribute attrib;
665 SecKeychainSearchRef searchRef = nil;
666 CSSM_DATA certData;
668 /* low-level form of MacOS data */
669 memset((void *) &certData, 0, sizeof(certData));
671 attrList.count = 1;
672 attrList.attr = &attrib;
674 /* kSecAlias means email address for a certificate */
675 attrib.tag = kSecAlias;
676 attrib.data = emailaddr;
677 attrib.length = strlen(attrib.data);
679 /* Find the certificate in the default keychain */
680 if(!(rc=SecKeychainSearchCreateFromAttributes(NULL,
681 kSecCertificateItemClass,
682 &attrList,
683 &searchRef))){
685 if(!(rc=SecKeychainSearchCopyNext(searchRef, &itemRef))){
687 /* extract the data portion of the certificate */
688 if(!(rc=SecCertificateGetData((SecCertificateRef) itemRef, &certData))){
691 * Convert it from MacOS form to OpenSSL form.
692 * The input is certData from above and the output
693 * is the X509 *cert.
695 if(!d2i_X509(&cert, &(certData.Data), certData.Length)){
696 dprint((9, "d2i_X509 failed"));
699 else{
700 dprint((9, "SecCertificateGetData failed"));
703 else if(rc == errSecItemNotFound){
704 dprint((9, "get_cert_for: Public cert for %s not found", emailaddr));
706 else{
707 dprint((9, "SecKeychainSearchCopyNext failed"));
710 else{
711 dprint((9, "SecKeychainSearchCreateFromAttributes failed"));
714 if(searchRef)
715 CFRelease(searchRef);
717 #endif /* APPLEKEYCHAIN */
719 else if(SMHOLDERTYPE(ctype) == Container){
720 CertList *cl;
722 for(cl = SMCERTLIST(ctype); cl; cl = cl->next){
723 if(cl->name && !strucmp(emailaddr, cl->name))
724 break;
727 if(cl)
728 cert = X509_dup((X509 *) cl->x509_cert);
730 else if(SMHOLDERTYPE(ctype) == Directory){
731 build_path(certfilename, PATHCERTDIR(ctype), emailaddr, sizeof(certfilename));
732 strncat(certfilename, EXTCERT(ctype), sizeof(certfilename)-1-strlen(certfilename));
733 certfilename[sizeof(certfilename)-1] = 0;
735 if((in = BIO_new_file(certfilename, "r"))!=0){
737 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
739 if(cert){
740 /* could check email addr in cert matches */
743 BIO_free(in);
747 return cert;
751 PERSONAL_CERT *
752 mem_to_personal_certs(char *contents)
754 PERSONAL_CERT *result = NULL;
755 char *p, *q, *line, *name, *keytext, *save_p;
756 X509 *cert = NULL;
758 if(contents && *contents){
759 for(p = contents; *p != '\0';){
760 line = p;
762 while(*p && *p != '\n')
763 p++;
765 save_p = NULL;
766 if(*p == '\n'){
767 save_p = p;
768 *p++ = '\0';
771 if(strncmp(EMAILADDRLEADER, line, strlen(EMAILADDRLEADER)) == 0){
772 name = line + strlen(EMAILADDRLEADER);
773 cert = get_cert_for(name, Public);
774 keytext = p;
776 /* advance p past this record */
777 if((q = strstr(keytext, "-----END")) != NULL){
778 while(*q && *q != '\n')
779 q++;
781 if(*q == '\n')
782 q++;
784 p = q;
786 else{
787 p = p + strlen(p);
788 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error in privatekey container, missing END"));
791 if(cert){
792 PERSONAL_CERT *pc;
794 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
795 pc->cert = cert;
796 pc->name = cpystr(name);
797 pc->keytext = keytext; /* a pointer into contents */
799 pc->key = load_key(pc, "");
801 pc->next = result;
802 result = pc;
806 if(save_p)
807 *save_p = '\n';
811 return result;
815 CertList *
816 mem_to_certlist(char *contents)
818 CertList *ret = NULL;
819 char *p, *q, *line, *name, *certtext, *save_p;
820 X509 *cert = NULL;
821 BIO *in;
823 if(contents && *contents){
824 for(p = contents; *p != '\0';){
825 line = p;
827 while(*p && *p != '\n')
828 p++;
830 save_p = NULL;
831 if(*p == '\n'){
832 save_p = p;
833 *p++ = '\0';
836 if(strncmp(EMAILADDRLEADER, line, strlen(EMAILADDRLEADER)) == 0){
837 name = line + strlen(EMAILADDRLEADER);
838 cert = NULL;
839 certtext = p;
840 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
841 if((q = strstr(certtext, "-----END")) != NULL){
842 while(*q && *q != '\n')
843 q++;
845 if(*q == '\n')
846 q++;
848 p = q;
850 if((in = BIO_new_mem_buf(certtext, q-certtext)) != 0){
851 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
853 BIO_free(in);
857 else{
858 p = p + strlen(p);
859 q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in publiccert container, missing BEGIN, certtext=%s"), certtext);
862 if(name && cert){
863 add_to_end_of_certlist(&ret, name, cert);
867 if(save_p)
868 *save_p = '\n';
872 return ret;
877 * Add the CACert Container contents into the CACert store.
879 * Returns > 0 for success, 0 for failure
882 mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup)
884 char *p, *q, *line, *certtext, *save_p;
885 BIO *in, *out;
886 int len, failed = 0;
887 char *tempfile;
888 char iobuf[4096];
891 * The most straight-forward way to do this is to write
892 * the container contents to a temp file and then load the
893 * contents of the file with X509_LOOKUP_load_file(), like
894 * is done in add_certs_in_dir(). What we don't know is if
895 * each file should consist of one cacert or if they can all
896 * just be jammed together into one file. To be safe, we'll use
897 * one file per and do each in a separate operation.
900 if(contents && *contents){
901 for(p = contents; *p != '\0';){
902 line = p;
904 while(*p && *p != '\n')
905 p++;
907 save_p = NULL;
908 if(*p == '\n'){
909 save_p = p;
910 *p++ = '\0';
913 /* look for separator line */
914 if(strncmp(CACERTSTORELEADER, line, strlen(CACERTSTORELEADER)) == 0){
915 /* certtext is the content that should go in a file */
916 certtext = p;
917 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
918 if((q = strstr(certtext, CACERTSTORELEADER)) != NULL){
919 p = q;
921 else{ /* end of file */
922 q = certtext + strlen(certtext);
923 p = q;
926 in = BIO_new_mem_buf(certtext, q-certtext);
927 if(in){
928 tempfile = temp_nam(NULL, "az");
929 out = NULL;
930 if(tempfile)
931 out = BIO_new_file(tempfile, "w");
933 if(out){
934 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
935 BIO_write(out, iobuf, len);
937 BIO_free(out);
938 if(!X509_LOOKUP_load_file(lookup, tempfile, X509_FILETYPE_PEM))
939 failed++;
941 fs_give((void **) &tempfile);
944 BIO_free(in);
947 else{
948 p = p + strlen(p);
949 q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in cacert container, missing BEGIN, certtext=%s"), certtext);
952 else{
953 p = p + strlen(p);
954 q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in cacert container, missing separator, line=%s"), line);
957 if(save_p)
958 *save_p = '\n';
962 return(!failed);
967 certlist_to_file(char *filename, CertList *certlist)
969 CertList *cl;
970 BIO *bio_out = NULL;
971 int ret = -1;
973 if(filename && (bio_out=BIO_new_file(filename, "w")) != NULL){
974 ret = 0;
975 for(cl = certlist; cl; cl = cl->next){
976 if(cl->name && cl->name[0] && cl->x509_cert){
977 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
978 && (BIO_puts(bio_out, cl->name) > 0)
979 && (BIO_puts(bio_out, "\n") > 0)))
980 ret = -1;
982 if(!PEM_write_bio_X509(bio_out, (X509 *) cl->x509_cert))
983 ret = -1;
987 BIO_free(bio_out);
990 return ret;
994 void
995 add_to_end_of_certlist(CertList **cl, char *name, X509 *cert)
997 CertList *new, *clp;
999 if(!cl)
1000 return;
1002 new = (CertList *) fs_get(sizeof(*new));
1003 memset((void *) new, 0, sizeof(*new));
1004 new->x509_cert = cert;
1005 new->name = name ? cpystr(name) : NULL;
1007 if(!*cl){
1008 *cl = new;
1010 else{
1011 for(clp = (*cl); clp->next; clp = clp->next)
1014 clp->next = new;
1019 void
1020 free_certlist(CertList **cl)
1022 if(cl && *cl){
1023 free_certlist(&(*cl)->next);
1024 if((*cl)->name)
1025 fs_give((void **) &(*cl)->name);
1027 if((*cl)->x509_cert)
1028 X509_free((X509 *) (*cl)->x509_cert);
1030 fs_give((void **) cl);
1035 void
1036 free_personal_certs(PERSONAL_CERT **pc)
1038 if(pc && *pc){
1039 free_personal_certs(&(*pc)->next);
1040 if((*pc)->name)
1041 fs_give((void **) &(*pc)->name);
1043 if((*pc)->name)
1044 fs_give((void **) &(*pc)->name);
1046 if((*pc)->cert)
1047 X509_free((*pc)->cert);
1049 if((*pc)->key)
1050 EVP_PKEY_free((*pc)->key);
1052 fs_give((void **) pc);
1056 #endif /* SMIME */