* Update to packages/alpine.spec to account for the new location of man
[alpine.git] / pith / smkeys.c
blobcd940868aaf1ad2cf3080f16da7530288bc0716e
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-2017 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 "../pith/mailindx.h"
36 #include "../pith/readfile.h"
37 #include "smkeys.h"
39 #ifdef APPLEKEYCHAIN
40 #include <Security/SecKeychain.h>
41 #include <Security/SecKeychainItem.h>
42 #include <Security/SecKeychainSearch.h>
43 #include <Security/SecCertificate.h>
44 #endif /* APPLEKEYCHAIN */
47 /* internal prototypes */
48 static char *emailstrclean(char *string);
49 static int mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup);
50 int compare_certs_by_name(const void *data1, const void *data2);
51 int password_policy_check(char *);
53 int (*pith_smime_enter_password)(char *, char *, size_t);
55 /* test if password passes a predetermined policy.
56 * return value: 0 - does not pass; 1 - it passes
58 int
59 password_policy_check(char *password)
61 int rv = 1;
62 char *error;
63 char tmp[1024];
65 if(password == NULL || password[0] == '\0'){
66 error = _("Password cannot be blank");
67 rv = 0;
68 } else if(strlen(password) < 8){
69 error = _("Password is too short");
70 rv = 0;
72 if(rv == 0){
73 snprintf(tmp, sizeof(tmp), "%s%s", error, _(". Enter password again"));
74 tmp[sizeof(tmp) - 1] = '\0';
75 q_status_message(SM_ORDER, 3, 3, tmp);
77 return rv;
81 int
82 create_master_password(char *pass, size_t passlen, int first_time)
84 #define MAXTRIAL 3
85 int rv, trial;
86 char prompt[MAILTMPLEN];
87 char passbackup[MAILTMPLEN];
89 if(first_time)
90 q_status_message(SM_ORDER, 3, 3,
91 _(" Creating a Master Password for your Password file "));
92 else
93 q_status_message(SM_ORDER, 3, 3,
94 _(" Retrying to create a Master Password for your Password file "));
96 for(trial = 0; trial < MAXTRIAL; trial++){
97 snprintf(prompt, sizeof(prompt),
98 _("Create master password \(attempt %d of %d): "), trial+1, MAXTRIAL);
99 prompt[sizeof(prompt)- 1] = '\0';
100 pass[0] = '\0';
101 do {
102 rv = (pith_smime_enter_password)(prompt, pass, passlen);
103 if(password_policy_check(pass) == 0)
104 pass[0] = '\0';
105 } while ((rv !=0 && rv !=1 && rv > 0) || pass[0] == '\0');
107 snprintf(prompt, sizeof(prompt),
108 _("Confirm master password \(attempt %d of %d): "), trial+1, MAXTRIAL);
109 prompt[sizeof(prompt)- 1] = '\0';
110 passbackup[0] = '\0';
111 do {
112 rv = (pith_smime_enter_password)(prompt, passbackup, sizeof(passbackup));
113 } while ((rv !=0 && rv !=1 && rv > 0) || passbackup[0] == '\0');
114 if(!strcmp(pass, passbackup))
115 break;
116 if(trial + 1 < MAXTRIAL)
117 q_status_message(SM_ORDER, 2, 2, _("Passwords do not match, try again."));
118 else{
119 q_status_message(SM_ORDER, 2, 2, _("Passwords do not match, too many failures."));
120 pass[0] = '\0';
123 return (trial < MAXTRIAL) ? 1 : 0;
127 * Create a self signed certificate with root name _fname_, in directory
128 * _pathdir_. If _version_ is 3, we use the _template_ file as configuration
129 * file for openssl. At this moment, we only call this function with template = NULL
130 * and version = 0, but a sensible call is
131 * ALPINE_self_signed_certificate("/etc/ssl/openssl.cnf", 2, pathdir, fname, first_time);
132 * or so.
133 * _pathdir_ is the directory to save the file,
134 * _fname_ is the root of the name to use. Append ".key" and ".crt" to this name
135 * _first_time_ is an indicator to tell us if this is the first time we call this function
137 PERSONAL_CERT *
138 ALPINE_self_signed_certificate(char *template, int version, char *pathdir, char *fname)
140 BIGNUM *b = NULL;
141 X509_NAME *name = NULL;
142 X509_REQ *req = NULL;
143 EVP_PKEY_CTX *pkctx;
144 BIO *out = NULL;
145 char tmp[MAXPATH+1], password[1024];
146 char *keyfile = NULL, *certfile = NULL;
147 char *extensions = NULL;
148 FILE *fp;
149 long errline = -1L;
150 PERSONAL_CERT *pc = NULL;
151 EVP_PKEY *pkey = NULL;
152 X509 *pcert = NULL;
153 CONF *req_conf = NULL;
154 static int first_time = 1;
156 if(pathdir == NULL)
157 return NULL;
159 if(template){
160 if((out = BIO_new_file(template, "r")) == NULL){
161 q_status_message(SM_ORDER, 2, 2, _("Problem reading configuration file"));
162 return pc;
165 if((req_conf = NCONF_new(NULL)) != NULL
166 && NCONF_load_bio(req_conf, out, &errline) > 0){
167 if((extensions = NCONF_get_string(req_conf, "req", "x509_extensions")) != NULL){
168 X509V3_CTX ctx;
169 X509V3_set_ctx_test(&ctx);
170 X509V3_set_nconf(&ctx, req_conf);
171 if (!X509V3_EXT_add_nconf(req_conf, &ctx, extensions, NULL)) {
172 q_status_message(SM_ORDER, 2, 2, _("Problem loading openssl configuration"));
173 NCONF_free(req_conf);
174 return pc;
178 BIO_free(out);
179 out = NULL;
182 if(create_master_password(password, sizeof(password), first_time)
183 && (pkctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL)) != NULL
184 && EVP_PKEY_keygen_init(pkctx) > 0
185 && EVP_PKEY_CTX_set_rsa_keygen_bits(pkctx, 2048) > 0 /* RSA:2048 */
186 && EVP_PKEY_keygen(pkctx, &pkey) > 0){
187 snprintf(tmp, sizeof(tmp), "%s.key", fname);
188 tmp[sizeof(tmp)-1] = '\0';
189 keyfile = cpystr(tmp);
190 build_path(tmp, pathdir, keyfile, sizeof(tmp));
191 keyfile[strlen(keyfile)-4] = '\0'; /* keyfile does not have .key extension */
192 if((fp = fopen(tmp, "w")) != NULL
193 && (out = BIO_new_fp(fp, BIO_CLOSE | BIO_FP_TEXT)) != NULL
194 && PEM_write_bio_PrivateKey(out, pkey, EVP_des_ede3_cbc(),
195 NULL, 0, NULL, password)){
196 BIO_free(out);
197 out = NULL;
199 memset((void *)password, 0, sizeof(password));
200 if((req = X509_REQ_new()) != NULL
201 && X509_REQ_set_version(req, 0L)){
202 name = X509_REQ_get_subject_name(req);
203 X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, "Password File Certificate and Key Pair", -1, -1, 0);
204 if(X509_REQ_set_pubkey(req, pkey)
205 && (pcert = X509_new()) != NULL){
206 if(X509_set_version(pcert, version)
207 && (b = BN_new()) != NULL
208 && BN_set_word(b, 65537)
209 && BN_pseudo_rand(b, 64, 0, 0)
210 && X509_get_serialNumber(pcert)
211 && BN_to_ASN1_INTEGER(b, X509_get_serialNumber(pcert)) /* set serial */
212 && X509_set_issuer_name(pcert, X509_REQ_get_subject_name(req))
213 && X509_set_subject_name(pcert, X509_REQ_get_subject_name(req))){
214 X509V3_CTX ext_ctx;
215 EVP_PKEY *tmppkey;
217 X509_gmtime_adj(X509_getm_notBefore(pcert), 0);
218 X509_time_adj_ex(X509_getm_notAfter(pcert), 1095, 0, NULL);
220 if((tmppkey = X509_REQ_get0_pubkey(req)) != NULL
221 && X509_set_pubkey(pcert, tmppkey)){
222 if(extensions != NULL && version == 2){
223 X509V3_set_ctx(&ext_ctx, pcert, pcert, NULL, NULL, 0);
224 if(req_conf){ /* only if template is not null */
225 X509V3_set_nconf(&ext_ctx, req_conf);
226 X509V3_EXT_add_nconf(req_conf, &ext_ctx, extensions, pcert);
229 EVP_PKEY_free(tmppkey);
230 X509_sign(pcert, pkey, NULL);
232 BN_free(b);
237 snprintf(tmp, sizeof(tmp), "%s.crt", fname);
238 tmp[sizeof(tmp)-1] = '\0';
239 certfile = cpystr(tmp);
240 build_path(tmp, pathdir, certfile, sizeof(tmp));
241 if((fp = fopen(tmp, "w")) != NULL
242 &&(out = BIO_new_fp(fp, BIO_FP_TEXT)) != NULL){
243 EVP_PKEY *tpubkey = X509_REQ_get0_pubkey(req);
244 PEM_write_bio_X509(out, pcert);
245 BIO_flush(out);
246 BIO_free(out);
247 out = NULL;
249 if(req_conf)
250 NCONF_free(req_conf);
252 if(keyfile && certfile && pkey && pcert){
253 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
254 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
255 pc->name = keyfile;
256 pc->key = pkey;
257 pc->cert = pcert;
258 pc->cname = certfile;
260 first_time = 0;
261 return pc;
264 CertList *
265 smime_X509_to_cert_info(X509 *x, char *name)
267 CertList *cert;
268 char buf[MAXPATH+1];
270 if(x == NULL) return NULL;
272 cert = fs_get(sizeof(CertList));
273 memset((void *)cert, 0, sizeof(CertList));
274 cert->x509_cert = x;
275 cert->name = name ? cpystr(name) : NULL;
276 cert->data.date_from = smime_get_date(X509_get0_notBefore(x));
277 cert->data.date_to = smime_get_date(X509_get0_notAfter(x));
278 cert->cn = smime_get_cn(x);
279 get_fingerprint(x, EVP_md5(), buf, sizeof(buf), NULL);
280 cert->data.md5 = cpystr(buf);
282 return cert;
285 #define SMIME_BACKUP_DIR ".backup"
286 #define MAX_TRY_BACKUP 100
288 /* return value: 0 - success, -1 error
289 * Call this function after setting up paths in ps_global->smime
290 * and reading certificates names in certlist.
293 setup_certs_backup_by_type(WhichCerts ctype)
295 int rv = 0; /* assume success */
296 int len;
297 int i, done;
298 char *d;
299 char p[MAXPATH+1]; /* path to where the backup is */
300 char buf[MAXPATH+1], buf2[MAXPATH+1];
301 struct stat sbuf;
302 CertList *data, *cl;
303 DIR *dirp;
304 struct dirent *df; /* file in the directory */
305 CertList *cert, *cl2;
306 X509 *x;
307 BIO *in;
309 return rv; /* remove when this function is complete */
311 if(SMHOLDERTYPE(ctype) == Directory){
312 d = PATHCERTDIR(ctype);
313 if(d != NULL){
314 len = strlen(d) + strlen(S_FILESEP) + strlen(SMIME_BACKUP_DIR) + 1;
315 snprintf(p, MAXPATH, "%s%s%s", d, S_FILESEP, SMIME_BACKUP_DIR);
316 p[MAXPATH] = '\0';
317 if(our_stat(p, &sbuf) < 0){
318 if(our_mkpath(p, 0700) != 0)
319 return -1;
320 } else if((sbuf.st_mode & S_IFMT) != S_IFDIR){
321 for(i = 0, done = 0; done == 0 && i < MAX_TRY_BACKUP; i++){
322 snprintf(buf2, len+2, "%s%d", p, i);
323 if(our_stat(buf2, &sbuf) < 0){
324 if(our_mkpath(buf2, 0700) == 0)
325 done++;
327 else if((sbuf.st_mode & S_IFMT) == S_IFDIR)
328 done++;
329 if(done){
330 strncpy(p, buf2, MAXPATH);
331 p[MAXPATH] = '\0';
334 if(done == 0)
335 return -1;
337 /* if we are here, we have a backup directory where to
338 * backup certificates/keys, so now we will go
339 * through the list of certificates and back them up
340 * if we need to.
342 data = BACKUPDATACERT(ctype);
343 for(cl = DATACERT(ctype); cl; cl = cl->next){
344 char clname[MAXPATH+1];
346 snprintf(clname, MAXPATH, "%s%s", cl->name, ctype == Private ? ".key" : "");
347 clname[MAXPATH] = '\0';
348 len = strlen(d) + strlen(clname) + 2;
349 if(len < MAXPATH){
350 snprintf(buf, len, "%s%s%s", d, S_FILESEP, clname);
351 buf[sizeof(buf)-1] = '\0';
352 len = strlen(p) + strlen(clname) + strlen(cl->data.md5) + 3;
353 if(len < MAXPATH){
354 snprintf(buf2, len, "%s%s%s.%s", p, S_FILESEP, clname, cl->data.md5);
355 buf2[sizeof(buf2)-1] = '\0';
356 done = 0; /* recycle done: it means we have a file that may be a certifificate*/
357 if(stat(buf2, &sbuf) < 0){
358 if (our_copy(buf2, buf) == 0)
359 done++;
360 } else if((sbuf.st_mode & S_IFMT) == S_IFREG)
361 done++;
363 if(done){
364 switch(ctype){
365 case Public:
366 case CACert:
367 if((in = BIO_new_file(buf2, "r"))!=0){
368 cert = fs_get(sizeof(CertList));
369 memset((void *)cert, 0, sizeof(CertList));
370 cert->x509_cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
371 if(cl->data.date_from != NULL)
372 cert->data.date_from = cpystr(cl->data.date_from);
373 if(cl->data.date_to != NULL)
374 cert->data.date_to = cpystr(cl->data.date_to);
375 if(cl->data.md5 != NULL)
376 cert->data.md5 = cpystr(cl->data.md5);
377 if(cl->cn != NULL)
378 cert->cn = cpystr(cl->cn);
379 snprintf(buf2, len, "%s.%s", cl->name, cl->data.md5);
380 buf2[sizeof(buf2)-1] = '\0';
381 cert->name = cpystr(buf2);
382 if(data == NULL)
383 data = cert;
384 else{
385 for (cl2 = data; cl2 && cl2->next; cl2 = cl2->next);
386 cl2->next = cert;
388 BIO_free(in);
390 break;
392 case Private: break;
393 default: alpine_panic("Bad ctype (0)");
399 /* if we are here, it means we just loaded the backup variable with
400 * a copy of the data that comes from the certlist not coming from
401 * backup. Now we are going to load the contents of the .backup
402 * directory.
405 /* Here is the plan: read the backup directory (in the variable "p")
406 * and attempt to add it. If already there, skip it; otherwise continue
409 if((dirp = opendir(p)) != NULL){
410 while((df=readdir(dirp)) != NULL){
411 if(df->d_name && *df->d_name == '.') /* no hidden files here */
412 continue;
414 /* make sure that we have a file */
415 snprintf(buf2, sizeof(buf2), "%s%s%s", p, S_FILESEP, df->d_name);
416 buf2[sizeof(buf2)-1] = '\0';
417 if(our_stat(buf2, &sbuf) == 0
418 && (sbuf.st_mode & S_IFMT) != S_IFREG)
419 continue;
421 /* make sure it is not already in the list */
422 for(cl = data; cl; cl = cl->next)
423 if(strcmp(cl->name, df->d_name) == 0)
424 break;
425 if(cl != NULL)
426 continue;
428 /* ok, if it is not in the list, and it is a certificate. Add it */
429 switch(ctype){
430 case Public:
431 case CACert:
432 if((in = BIO_new_file(buf2, "r"))!=0){
433 x = PEM_read_bio_X509(in, NULL, NULL, NULL);
434 if(x){ /* for now copy this information */
435 cert = smime_X509_to_cert_info(x, df->d_name);
436 /* we will use the cert->data.md5 variable to find a backup
437 certificate, not the name */
438 cert->next = data;
439 data = cert;
441 BIO_free(in);
443 break;
445 case Private:
446 /* here we must check it is a key of some cert....*/
447 break;
449 default: alpine_panic("Bad ctype (1)");
450 } /* end switch */
452 closedir(dirp);
455 /* Now that we are here, we have all the information in the backup
456 * directory
459 switch(ctype){
460 case Public : ps_global->smime->backuppubliccertlist = data; break;
461 case Private: ps_global->smime->backupprivatecertlist = data; break;
462 case CACert : ps_global->smime->backupcacertlist = data; break;
463 default : alpine_panic("Bad ctype (n)");
466 } else if(SMHOLDERTYPE(ctype) == Container){
468 } /* else APPLEKEYCHAIN */
469 return rv;
472 char *
473 smime_get_cn(X509 *x)
475 X509_NAME_ENTRY *e;
476 X509_NAME *subject;
477 char buf[256];
478 char *rv = NULL;
480 subject = X509_get_subject_name(x);
481 if((e = X509_NAME_get_entry(subject, X509_NAME_entry_count(subject)-1)) != NULL){
482 X509_NAME_get_text_by_OBJ(subject, X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
483 rv = cpystr(buf);
486 return rv;
490 compare_certs_by_name(const void *data1, const void *data2)
492 int rv, i, j;
493 char *s;
495 CertList *cl1 = *(CertList **) data1;
496 CertList *cl2 = *(CertList **) data2;
498 i = j = -1;
499 if((s = strchr(cl1->name, '@')) != NULL){
500 i = s - cl1->name;
501 *s = '\0';
504 if((s = strchr(cl2->name, '@')) != NULL){
505 j = s - cl2->name;
506 *s = '\0';
509 if((rv = strucmp(cl1->name, cl2->name)) == 0)
510 rv = strucmp(cl1->name + i + 1, cl2->name + j + 1);
511 if(i >= 0) cl1->name[i] = '@';
512 if(j >= 0) cl2->name[j] = '@';
513 return rv;
516 void
517 resort_certificates(CertList **data, WhichCerts ctype)
519 int i, j;
520 CertList *cl = *data;
521 CertList **cll;
522 char *s, *t;
524 if(cl == NULL)
525 return;
527 for(i = 0; cl; cl = cl->next, i++)
528 if(SMHOLDERTYPE(ctype) == Directory && ctype != Private){
529 for(t = s = cl->name; (t = strstr(s, ".crt")) != NULL; s = t+1);
530 if (s) *(s-1) = '\0';
532 j = i;
533 cll = fs_get(i*sizeof(CertList *));
534 for(cl = *data, i = 0; cl; cl = cl->next, i++)
535 cll[i] = cl;
536 qsort((void *)cll, j, sizeof(CertList *), compare_certs_by_name);
537 for(i = 0; i < j - 1; i++){
538 cll[i]->next = cll[i+1];
539 if(SMHOLDERTYPE(ctype) == Directory && ctype != Private)
540 cll[i]->name[strlen(cll[i]->name)]= '.'; /* restore ".crt" part */
542 if(SMHOLDERTYPE(ctype) == Directory && ctype != Private)
543 cll[j-1]->name[strlen(cll[j-1]->name)]= '.'; /* restore ".crt" part */
544 cll[j-1]->next = NULL;
545 *data = cll[0];
549 void
550 get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen, char *s)
552 unsigned char md[128];
553 char *b;
554 unsigned int len, i;
556 len = sizeof(md);
558 X509_digest(cert, type, md, &len);
560 b = buf;
561 *b = 0;
562 for(i=0; i<len; i++){
563 if(b-buf+3>=maxLen)
564 break;
566 if(i != 0 && s && *s)
567 *b++ = *s;
569 snprintf(b, maxLen - (b-buf), "%02x", md[i]);
570 b+=2;
576 * Remove leading whitespace, trailing whitespace and convert
577 * to lowercase. Also remove slash characters
579 * Args: s, -- The string to clean
581 * Result: the cleaned string
583 static char *
584 emailstrclean(char *string)
586 char *s = string, *sc = NULL, *p = NULL;
588 for(; *s; s++){ /* single pass */
589 if(!isspace((unsigned char) (*s))){
590 p = NULL; /* not start of blanks */
591 if(!sc) /* first non-blank? */
592 sc = string; /* start copying */
594 else if(!p) /* it's OK if sc == NULL */
595 p = sc; /* start of blanks? */
597 if(sc && *s!='/' && *s!='\\') /* if copying, copy */
598 *sc++ = isupper((unsigned char) (*s))
599 ? (unsigned char) tolower((unsigned char) (*s))
600 : (unsigned char) (*s);
603 if(p) /* if ending blanks */
604 *p = '\0'; /* tie off beginning */
605 else if(!sc) /* never saw a non-blank */
606 *string = '\0'; /* so tie whole thing off */
608 return(string);
612 char *
613 smime_get_date(const ASN1_TIME *tm)
615 BIO *mb = BIO_new(BIO_s_mem());
616 char iobuf[4096];
617 char date[MAILTMPLEN];
618 char buf[MAILTMPLEN];
619 char *m, *d, *t, *y, *z;
620 struct date smd;
621 struct tm smtm;
623 (void) BIO_reset(mb);
624 if(ASN1_TIME_print(mb, tm) == 0)
625 return cpystr(_("Invalid"));
627 (void) BIO_flush(mb);
628 BIO_read(mb, iobuf, sizeof(iobuf));
630 /* openssl returns the date in the format:
631 * "MONTH (as name) DAY (as number) TIME(hh:mm:ss) YEAR GMT"
633 m = iobuf;
634 d = strchr(iobuf, ' ');
635 *d++ = '\0';
636 while(*d == ' ') d++;
637 t = strchr(d+1, ' ');
638 *t++ = '\0';
639 while(*t == ' ') t++;
640 y = strchr(t+1, ' ');
641 *y++ = '\0';
642 while(*y == ' ') y++;
643 z = strchr(y+1, ' ');
644 *z++ = '\0';
645 while(*z == ' ') z++;
647 snprintf(date, sizeof(date), "%s %s %s %s (%s)", d, m, y, t, z);
648 date[sizeof(date)-1] = '\0';
649 if(F_ON(F_DATES_TO_LOCAL,ps_global)){
650 parse_date(convert_date_to_local(date), &smd);
651 memset(&smtm, 0, sizeof(smtm));
652 smtm.tm_year = smd.year - 1900;
653 smtm.tm_mon = MIN(MAX(smd.month-1, 0), 11);
654 smtm.tm_mday = MIN(MAX(smd.day, 1), 31);
655 our_strftime(buf, sizeof(buf), "%x", &smtm);
657 else
658 snprintf(buf, sizeof(buf), "%s/%s/%s", m, d, y + strlen(y) - 2);
659 buf[sizeof(buf)-1] = '\0';
661 return cpystr(buf);
665 * Add a lookup for each "*.crt*" file in the given directory.
668 add_certs_in_dir(X509_LOOKUP *lookup, char *path, char *ext, CertList **cdata)
670 char buf[MAXPATH];
671 struct direct *d;
672 DIR *dirp;
673 CertList *cert, *cl;
674 int ret = 0;
676 if((dirp = opendir(path)) != NULL){
677 while(!ret && (d=readdir(dirp)) != NULL){
678 if(srchrstr(d->d_name, ext)){
679 build_path(buf, path, d->d_name, sizeof(buf));
681 if(!X509_LOOKUP_load_file(lookup, buf, X509_FILETYPE_PEM)){
682 q_status_message1(SM_ORDER, 3, 3, _("Error loading file %s"), buf);
683 ret = -1;
684 } else {
685 if(cdata){
686 BIO *in;
687 X509 *x;
689 cert = fs_get(sizeof(CertList));
690 memset((void *)cert, 0, sizeof(CertList));
691 cert->name = cpystr(d->d_name);
692 /* read buf into a bio and fill the CertData structure */
693 if((in = BIO_new_file(buf, "r"))!=0){
694 if((x = PEM_read_bio_X509(in, NULL, NULL, NULL)) != NULL){
695 cert->data.date_from = smime_get_date(X509_get0_notBefore(x));
696 cert->data.date_to = smime_get_date(X509_get0_notAfter(x));
697 get_fingerprint(x, EVP_md5(), buf, sizeof(buf), NULL);
698 cert->data.md5 = cpystr(buf);
699 cert->cn = smime_get_cn(x);
700 X509_free(x);
702 BIO_free(in);
704 if(*cdata == NULL)
705 *cdata = cert;
706 else{
707 for (cl = *cdata; cl && cl->next; cl = cl->next);
708 cl->next = cert;
717 closedir(dirp);
720 return ret;
725 * Get an X509_STORE. This consists of the system
726 * certs directory and any certificates in the user's
727 * ~/.alpine-smime/ca directory.
729 X509_STORE *
730 get_ca_store(void)
732 X509_LOOKUP *lookup;
733 X509_STORE *store = NULL;
735 dprint((9, "get_ca_store()"));
737 if(!(store=X509_STORE_new())){
738 dprint((9, "X509_STORE_new() failed"));
739 return store;
742 if(!(lookup=X509_STORE_add_lookup(store, X509_LOOKUP_file()))){
743 dprint((9, "X509_STORE_add_lookup() failed"));
744 X509_STORE_free(store);
745 return NULL;
748 if(ps_global->smime && ps_global->smime->catype == Container
749 && ps_global->smime->cacontent){
751 if(!mem_add_extra_cacerts(ps_global->smime->cacontent, lookup)){
752 X509_STORE_free(store);
753 return NULL;
756 else if(ps_global->smime && ps_global->smime->catype == Directory
757 && ps_global->smime->capath){
758 if(add_certs_in_dir(lookup, ps_global->smime->capath, ".crt", &ps_global->smime->cacertlist) < 0){
759 X509_STORE_free(store);
760 return NULL;
762 resort_certificates(&ps_global->smime->cacertlist, CACert);
765 if(!(lookup=X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()))){
766 X509_STORE_free(store);
767 return NULL;
770 #ifdef SMIME_SSLCERTS
771 dprint((9, "get_ca_store(): adding cacerts from %s", SMIME_SSLCERTS));
772 X509_LOOKUP_add_dir(lookup, SMIME_SSLCERTS, X509_FILETYPE_PEM);
773 #endif
775 return store;
779 EVP_PKEY *
780 load_key(PERSONAL_CERT *pc, char *pass, int flag)
782 BIO *in;
783 EVP_PKEY *key = NULL;
784 char buf[MAXPATH], file[MAXPATH];
786 if(!(ps_global->smime && pc && pc->name))
787 return key;
789 if(ps_global->smime->privatetype == Container){
790 char *q;
792 if(pc->keytext && (q = strstr(pc->keytext, "-----END")) != NULL){
793 while(*q && *q != '\n')
794 q++;
796 if(*q == '\n')
797 q++;
799 if((in = BIO_new_mem_buf(pc->keytext, q-pc->keytext)) != NULL){
800 key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass);
801 BIO_free(in);
805 else if(ps_global->smime->privatetype == Directory){
806 /* filename is path/name.key */
807 strncpy(buf, pc->name, sizeof(buf)-5);
808 buf[sizeof(buf)-5] = '\0';
809 strncat(buf, ".key", 5);
810 build_path(file, ps_global->smime->privatepath, buf, sizeof(file));
812 if(!(in = BIO_new_file(file, "r")))
813 return NULL;
815 key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass);
816 BIO_free(in);
819 return key;
823 #include <openssl/x509v3.h>
825 * This newer version is from Adrian Vogel. It looks for the email
826 * address not only in the email address field, but also in an
827 * X509v3 extension field, Subject Altenative Name.
829 char **
830 get_x509_subject_email(X509 *x)
832 char **result = NULL;
833 int i, n;
834 STACK_OF(OPENSSL_STRING) *emails = X509_get1_email(x);
835 if ((n = sk_OPENSSL_STRING_num(emails)) > 0) {
836 result = fs_get((n+1)*sizeof(char *));
837 for(i = 0; i < n; i++)
838 result[i] = cpystr(sk_OPENSSL_STRING_value(emails, i));
839 result[i] = NULL;
841 X509_email_free(emails);
842 return result;
847 * Save the certificate for the given email address in
848 * ~/.alpine-smime/public.
850 * Should consider the security hazards in making a file with
851 * the email address that has come from the certificate.
853 * The argument email is destroyed.
855 * args: ctype says where the user wants to save the certificate
857 void
858 save_cert_for(char *email, X509 *cert, WhichCerts ctype)
860 if(!ps_global->smime || ctype == Private)
861 return;
863 dprint((9, "save_cert_for(%s, %s)", email ? email : "?", ctype == Public ? _("Public") : ctype == Private ? _("Private") : "CACert"));
864 emailstrclean(email);
866 if(ps_global->smime->publictype == Keychain){
867 #ifdef APPLEKEYCHAIN
869 OSStatus rc;
870 SecCertificateRef secCertificateRef;
871 CSSM_DATA certData;
873 memset((void *) &certData, 0, sizeof(certData));
874 memset((void *) &secCertificateRef, 0, sizeof(secCertificateRef));
876 /* convert OpenSSL X509 cert data to MacOS certData */
877 if((certData.Length = i2d_X509(cert, &(certData.Data))) > 0){
880 * Put that certData into a SecCertificateRef.
881 * Version 3 should work for versions 1-3.
883 if(!(rc=SecCertificateCreateFromData(&certData,
884 CSSM_CERT_X_509v3,
885 CSSM_CERT_ENCODING_DER,
886 &secCertificateRef))){
888 /* add it to the default keychain */
889 if(!(rc=SecCertificateAddToKeychain(secCertificateRef, NULL))){
890 /* ok */
892 else if(rc == errSecDuplicateItem){
893 dprint((9, "save_cert_for: certificate for %s already in keychain", email));
895 else{
896 dprint((9, "SecCertificateAddToKeychain failed"));
899 else{
900 dprint((9, "SecCertificateCreateFromData failed"));
903 else{
904 dprint((9, "i2d_X509 failed"));
907 #endif /* APPLEKEYCHAIN */
909 else if(SMHOLDERTYPE(ctype) == Container){
910 REMDATA_S *rd = NULL;
911 char *ret_dir = NULL;
912 char path[MAXPATH];
913 char fpath[MAXPATH];
914 char *upath = PATHCERTDIR(ctype);
915 char *tempfile = NULL;
916 int err = 0;
917 CertList *clist = DATACERT(ctype);
919 add_to_end_of_certlist(&clist, email, X509_dup(cert));
921 switch(ctype){
922 case Private: ps_global->smime->privatecertlist = clist; break;
923 case Public : ps_global->smime->publiccertlist = clist; break;
924 case CACert : ps_global->smime->cacertlist = clist; break;
925 default: break;
928 if(!upath)
929 return;
931 if(IS_REMOTE(upath)){
932 rd = rd_create_remote(RemImap, upath, REMOTE_SMIME_SUBTYPE,
933 NULL, "Error: ",
934 _("Can't access remote smime configuration."));
935 if(!rd){
936 return;
939 (void) rd_read_metadata(rd);
941 if(rd->access == MaybeRorW){
942 if(rd->read_status == 'R')
943 rd->access = ReadOnly;
944 else
945 rd->access = ReadWrite;
948 if(rd->access != NoExists){
950 rd_check_remvalid(rd, 1L);
953 * If the cached info says it is readonly but
954 * it looks like it's been fixed now, change it to readwrite.
956 if(rd->read_status == 'R'){
957 rd_check_readonly_access(rd);
958 if(rd->read_status == 'W'){
959 rd->access = ReadWrite;
960 rd->flags |= REM_OUTOFDATE;
962 else
963 rd->access = ReadOnly;
967 if(rd->flags & REM_OUTOFDATE){
968 if(rd_update_local(rd) != 0){
970 dprint((1, "save_cert_for: rd_update_local failed\n"));
971 rd_close_remdata(&rd);
972 return;
975 else
976 rd_open_remote(rd);
978 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
979 rd_close_remdata(&rd);
980 return;
983 rd->flags |= DO_REMTRIM;
985 strncpy(path, rd->lf, sizeof(path)-1);
986 path[sizeof(path)-1] = '\0';
988 else{
989 strncpy(path, upath, sizeof(path)-1);
990 path[sizeof(path)-1] = '\0';
993 tempfile = tempfile_in_same_dir(path, "az", &ret_dir);
994 if(tempfile){
995 if(certlist_to_file(tempfile, DATACERT(ctype)))
996 err++;
998 if(!err && ret_dir){
999 if(IS_REMOTE(upath)){
1000 strncpy(fpath, rd->lf, sizeof(fpath));
1001 fpath[sizeof(fpath)-1] = '\0';
1003 else{
1004 if(strlen(path) + strlen(tempfile) - strlen(ret_dir) + 1 < sizeof(path))
1005 snprintf(fpath, sizeof(fpath), "%s%c%s",
1006 path, tempfile[strlen(ret_dir)], tempfile + strlen(ret_dir) + 1);
1007 else
1008 err++;
1011 else err++;
1013 fs_give((void **)&ret_dir);
1015 if(!err){
1016 if(rename_file(tempfile, fpath) < 0){
1017 q_status_message2(SM_ORDER, 3, 3,
1018 _("Can't rename %s to %s"), tempfile, fpath);
1019 err++;
1023 if(!err && IS_REMOTE(upath)){
1024 int e, we_cancel;
1025 char datebuf[200];
1027 datebuf[0] = '\0';
1029 we_cancel = busy_cue(_("Copying to remote smime container"), NULL, 1);
1030 if((e = rd_update_remote(rd, datebuf)) != 0){
1031 if(e == -1){
1032 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1033 _("Error opening temporary smime file %s: %s"),
1034 rd->lf, error_description(errno));
1035 dprint((1,
1036 "write_remote_smime: error opening temp file %s\n",
1037 rd->lf ? rd->lf : "?"));
1039 else{
1040 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1041 _("Error copying to %s: %s"),
1042 rd->rn, error_description(errno));
1043 dprint((1,
1044 "write_remote_smime: error copying from %s to %s\n",
1045 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1048 q_status_message(SM_ORDER | SM_DING, 5, 5,
1049 _("Copy of smime cert to remote folder failed, changes NOT saved remotely"));
1051 else{
1052 rd_update_metadata(rd, datebuf);
1053 rd->read_status = 'W';
1056 rd_close_remdata(&rd);
1058 if(we_cancel)
1059 cancel_busy_cue(-1);
1062 fs_give((void **) &tempfile);
1065 else if(SMHOLDERTYPE(ctype) == Directory){
1066 char *path = PATHCERTDIR(ctype);
1067 char certfilename[MAXPATH];
1068 BIO *bio_out;
1070 build_path(certfilename, path, email, sizeof(certfilename));
1071 strncat(certfilename, ".crt", sizeof(certfilename)-1-strlen(certfilename));
1072 certfilename[sizeof(certfilename)-1] = 0;
1074 bio_out = BIO_new_file(certfilename, "w");
1075 if(bio_out){
1076 PEM_write_bio_X509(bio_out, cert);
1077 BIO_free(bio_out);
1078 q_status_message1(SM_ORDER, 1, 1, _("Saved certificate for <%s>"), email);
1080 else{
1081 q_status_message1(SM_ORDER, 1, 1, _("Couldn't save certificate for <%s>"), email);
1088 * Try to retrieve the certificate for the given email address.
1089 * The caller should free the cert.
1091 X509 *
1092 get_cert_for(char *email, WhichCerts ctype, int tolower)
1094 char certfilename[MAXPATH];
1095 char emailaddr[MAXPATH];
1096 X509 *cert = NULL;
1097 BIO *in;
1099 if(ctype == Password){
1100 build_path(certfilename, PATHCERTDIR(ctype), email, sizeof(certfilename));
1101 strncat(certfilename, EXTCERT(Public), sizeof(certfilename)-1-strlen(certfilename));
1102 certfilename[sizeof(certfilename)-1] = 0;
1104 if((in = BIO_new_file(certfilename, "r"))!=0){
1106 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
1108 if(cert){
1109 /* could check email addr in cert matches */
1112 BIO_free(in);
1115 return cert;
1118 if(!ps_global->smime)
1119 return cert;
1121 dprint((9, "get_cert_for(%s, %s)", email ? email : "?", "none yet"));
1123 if(ctype == Private) /* there is no private certificate info */
1124 ctype = Public; /* return public information instead */
1125 strncpy(emailaddr, email, sizeof(emailaddr)-1);
1126 emailaddr[sizeof(emailaddr)-1] = 0;
1128 /* clean it up (lowercase, space removal) */
1129 if(tolower)
1130 emailstrclean(emailaddr);
1132 if(ps_global->smime->publictype == Keychain){
1133 #ifdef APPLEKEYCHAIN
1135 OSStatus rc;
1136 SecKeychainItemRef itemRef = nil;
1137 SecKeychainAttributeList attrList;
1138 SecKeychainAttribute attrib;
1139 SecKeychainSearchRef searchRef = nil;
1140 CSSM_DATA certData;
1142 /* low-level form of MacOS data */
1143 memset((void *) &certData, 0, sizeof(certData));
1145 attrList.count = 1;
1146 attrList.attr = &attrib;
1148 /* kSecAlias means email address for a certificate */
1149 attrib.tag = kSecAlias;
1150 attrib.data = emailaddr;
1151 attrib.length = strlen(attrib.data);
1153 /* Find the certificate in the default keychain */
1154 if(!(rc=SecKeychainSearchCreateFromAttributes(NULL,
1155 kSecCertificateItemClass,
1156 &attrList,
1157 &searchRef))){
1159 if(!(rc=SecKeychainSearchCopyNext(searchRef, &itemRef))){
1161 /* extract the data portion of the certificate */
1162 if(!(rc=SecCertificateGetData((SecCertificateRef) itemRef, &certData))){
1165 * Convert it from MacOS form to OpenSSL form.
1166 * The input is certData from above and the output
1167 * is the X509 *cert.
1169 if(!d2i_X509(&cert, &(certData.Data), certData.Length)){
1170 dprint((9, "d2i_X509 failed"));
1173 else{
1174 dprint((9, "SecCertificateGetData failed"));
1177 else if(rc == errSecItemNotFound){
1178 dprint((9, "get_cert_for: Public cert for %s not found", emailaddr));
1180 else{
1181 dprint((9, "SecKeychainSearchCopyNext failed"));
1184 else{
1185 dprint((9, "SecKeychainSearchCreateFromAttributes failed"));
1188 if(searchRef)
1189 CFRelease(searchRef);
1191 #endif /* APPLEKEYCHAIN */
1193 else if(SMHOLDERTYPE(ctype) == Container){
1194 CertList *cl;
1196 for(cl = DATACERT(ctype); cl; cl = cl->next){
1197 if(cl->name && !strucmp(emailaddr, cl->name))
1198 break;
1201 if(cl)
1202 cert = X509_dup((X509 *) cl->x509_cert);
1204 else if(SMHOLDERTYPE(ctype) == Directory){
1205 build_path(certfilename, PATHCERTDIR(ctype), emailaddr, sizeof(certfilename));
1206 strncat(certfilename, EXTCERT(ctype), sizeof(certfilename)-1-strlen(certfilename));
1207 certfilename[sizeof(certfilename)-1] = 0;
1209 if((in = BIO_new_file(certfilename, "r"))!=0){
1211 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
1213 if(cert){
1214 /* could check email addr in cert matches */
1217 BIO_free(in);
1221 return cert;
1225 * load_cert_for_key finds a certificate in pathdir that matches a private key
1226 * pkey. It returns its name in certfile, and the certificate in *pcert.
1227 * return value: success: different from zero, failure 0. If both certfile
1228 * and pcert are NULL, this function returns if there is certificate that
1229 * matches the given key.
1232 load_cert_for_key(char *pathdir, EVP_PKEY *pkey, char **certfile, X509 **pcert)
1234 DIR *dirp;
1235 struct dirent *d;
1236 int rv = 0;
1237 BIO *in;
1238 X509 *x;
1239 char buf[MAXPATH+1], pathcert[MAXPATH+1];
1241 if(pathdir == NULL || pkey == NULL)
1242 return 0;
1244 if(certfile) *certfile = NULL;
1245 if(pcert) *pcert = NULL;
1247 if((dirp = opendir(pathdir)) != NULL){
1248 while(rv == 0 && (d=readdir(dirp)) != NULL){
1249 size_t ll;
1251 if((ll=strlen(d->d_name)) && ll > 4){
1252 if(!strcmp(d->d_name+ll-4, ".crt")){
1253 strncpy(buf, d->d_name, sizeof(buf));
1254 buf[sizeof(buf)-1] = '\0';
1255 build_path(pathcert, pathdir, buf, sizeof(pathcert));
1256 if((in = BIO_new_file(pathcert, "r")) != NULL){
1257 if((x = PEM_read_bio_X509(in, NULL, NULL, NULL)) != NULL){
1258 if(X509_check_private_key(x, pkey) > 0){
1259 rv = 1;
1260 if(certfile) *certfile = cpystr(buf);
1261 if(pcert) *pcert = x;
1263 else
1264 X509_free(x);
1266 BIO_free(in);
1271 closedir(dirp);
1273 return rv;
1277 PERSONAL_CERT *
1278 mem_to_personal_certs(char *contents)
1280 PERSONAL_CERT *result = NULL;
1281 char *p, *q, *line, *name, *keytext, *save_p;
1282 X509 *cert = NULL;
1284 if(contents && *contents){
1285 for(p = contents; *p != '\0';){
1286 line = p;
1288 while(*p && *p != '\n')
1289 p++;
1291 save_p = NULL;
1292 if(*p == '\n'){
1293 save_p = p;
1294 *p++ = '\0';
1297 if(strncmp(EMAILADDRLEADER, line, strlen(EMAILADDRLEADER)) == 0){
1298 name = line + strlen(EMAILADDRLEADER);
1299 cert = get_cert_for(name, Public, 1);
1300 keytext = p;
1302 /* advance p past this record */
1303 if((q = strstr(keytext, "-----END")) != NULL){
1304 while(*q && *q != '\n')
1305 q++;
1307 if(*q == '\n')
1308 q++;
1310 p = q;
1312 else{
1313 p = p + strlen(p);
1314 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error in privatekey container, missing END"));
1317 if(cert){
1318 PERSONAL_CERT *pc;
1320 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1321 pc->cert = cert;
1322 pc->name = cpystr(name);
1323 pc->cname = NULL;
1324 pc->keytext = keytext; /* a pointer into contents */
1326 pc->key = load_key(pc, "", SM_NORMALCERT);
1328 pc->next = result;
1329 result = pc;
1333 if(save_p)
1334 *save_p = '\n';
1338 return result;
1342 CertList *
1343 mem_to_certlist(char *contents, WhichCerts ctype)
1345 CertList *ret = NULL;
1346 char *p, *q, *line, *name, *certtext, *save_p;
1347 X509 *cert = NULL;
1348 BIO *in;
1349 char *sep = (ctype == Public || ctype == Private)
1350 ? EMAILADDRLEADER : CACERTSTORELEADER;
1352 if(contents && *contents){
1353 for(p = contents; *p != '\0';){
1354 line = p;
1356 while(*p && *p != '\n')
1357 p++;
1359 save_p = NULL;
1360 if(*p == '\n'){
1361 save_p = p;
1362 *p++ = '\0';
1365 if(strncmp(sep, line, strlen(sep)) == 0){
1366 name = line + strlen(sep);
1367 cert = NULL;
1368 certtext = strstr(p, "-----BEGIN");
1369 if(certtext != NULL){
1370 if((q = strstr(certtext, sep)) != NULL)
1371 p = q;
1372 else
1373 p = q = certtext+strlen(certtext);
1375 if((in = BIO_new_mem_buf(certtext, q-certtext)) != 0){
1376 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
1377 BIO_free(in);
1380 else{
1381 q_status_message2(SM_ORDER | SM_DING, 3, 3, _("Error in %scert container, missing BEGIN, certtext=%s"), ctype == Public ? _("public") : _("ca"), p);
1382 p = p + strlen(p);
1385 if(name && cert)
1386 add_to_end_of_certlist(&ret, name, cert);
1389 if(save_p)
1390 *save_p = '\n';
1393 if(ret != NULL)
1394 resort_certificates(&ret, ctype);
1396 return ret;
1401 * Add the CACert Container contents into the CACert store.
1403 * Returns > 0 for success, 0 for failure
1406 mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup)
1408 char *p, *q, *line, *certtext, *save_p;
1409 BIO *in, *out;
1410 int len, failed = 0;
1411 char *tempfile;
1412 char iobuf[4096];
1415 * The most straight-forward way to do this is to write
1416 * the container contents to a temp file and then load the
1417 * contents of the file with X509_LOOKUP_load_file(), like
1418 * is done in add_certs_in_dir(). What we don't know is if
1419 * each file should consist of one cacert or if they can all
1420 * just be jammed together into one file. To be safe, we'll use
1421 * one file per and do each in a separate operation.
1424 if(contents && *contents){
1425 for(p = contents; *p != '\0';){
1426 line = p;
1428 while(*p && *p != '\n')
1429 p++;
1431 save_p = NULL;
1432 if(*p == '\n'){
1433 save_p = p;
1434 *p++ = '\0';
1437 /* look for separator line */
1438 if(strncmp(CACERTSTORELEADER, line, strlen(CACERTSTORELEADER)) == 0){
1439 /* certtext is the content that should go in a file */
1440 certtext = strstr(p, "-----BEGIN");
1441 if(certtext != NULL){
1442 if((q = strstr(certtext, CACERTSTORELEADER)) != NULL){
1443 p = q;
1445 else{ /* end of file */
1446 q = certtext + strlen(certtext);
1447 p = q;
1450 in = BIO_new_mem_buf(certtext, q-certtext);
1451 if(in){
1452 tempfile = temp_nam(NULL, "az");
1453 out = tempfile != NULL ? BIO_new_file(tempfile, "w") : NULL;
1454 if(out != NULL){
1455 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1456 BIO_write(out, iobuf, len);
1458 BIO_free(out);
1459 if(!X509_LOOKUP_load_file(lookup, tempfile, X509_FILETYPE_PEM))
1460 failed++;
1463 if(tempfile != NULL){
1464 unlink(tempfile);
1465 fs_give((void **) &tempfile);
1468 BIO_free(in);
1471 else{
1472 p = p + strlen(p);
1473 q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in cacert container, missing BEGIN, certtext=%s"), certtext);
1476 else{
1477 p = p + strlen(p);
1478 q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in cacert container, missing separator, line=%s"), line);
1481 if(save_p)
1482 *save_p = '\n';
1486 return(!failed);
1491 certlist_to_file(char *filename, CertList *certlist)
1493 CertList *cl;
1494 BIO *bio_out = NULL;
1495 int ret = -1;
1497 if(filename && (bio_out=BIO_new_file(filename, "w")) != NULL){
1498 ret = 0;
1499 for(cl = certlist; cl; cl = cl->next){
1500 if(cl->name && cl->name[0] && cl->x509_cert){
1501 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1502 && (BIO_puts(bio_out, cl->name) > 0)
1503 && (BIO_puts(bio_out, "\n") > 0)))
1504 ret = -1;
1506 if(!PEM_write_bio_X509(bio_out, (X509 *) cl->x509_cert))
1507 ret = -1;
1511 BIO_free(bio_out);
1514 return ret;
1518 void
1519 add_to_end_of_certlist(CertList **cl, char *name, X509 *cert)
1521 CertList *new;
1523 if(!cl)
1524 return;
1526 new = smime_X509_to_cert_info(cert, name);
1527 new->next = *cl;
1528 *cl = new;
1532 void
1533 free_certlist(CertList **cl)
1535 if(cl && *cl){
1536 if((*cl)->data.date_from)
1537 fs_give((void **) &(*cl)->data.date_from);
1539 if((*cl)->data.date_to)
1540 fs_give((void **) &(*cl)->data.date_to);
1542 if((*cl)->data.md5)
1543 fs_give((void **) &(*cl)->data.md5);
1545 if((*cl)->name)
1546 fs_give((void **) &(*cl)->name);
1548 if((*cl)->cn)
1549 fs_give((void **) &(*cl)->cn);
1551 if((*cl)->x509_cert)
1552 X509_free((X509 *) (*cl)->x509_cert);
1554 free_certlist(&(*cl)->next);
1556 fs_give((void **) cl);
1561 void
1562 free_personal_certs(PERSONAL_CERT **pc)
1564 if(pc && *pc){
1565 if((*pc)->name)
1566 fs_give((void **) &(*pc)->name);
1568 if((*pc)->cname)
1569 fs_give((void **) &(*pc)->cname);
1571 if((*pc)->cert)
1572 X509_free((*pc)->cert);
1574 if((*pc)->key)
1575 EVP_PKEY_free((*pc)->key);
1577 free_personal_certs(&(*pc)->next);
1579 fs_give((void **) pc);
1583 #endif /* SMIME */