* For mailing lists, Alpine adds a description of the type of link
[alpine.git] / pith / smkeys.c
blob64b10565f4dfb234f34b71805ac1dbad2caeda3f
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
16 * This is based on a contribution from Jonathan Paisley, see smime.c
20 #include "../pith/headers.h"
22 #ifdef SMIME
24 #include "../pith/status.h"
25 #include "../pith/conf.h"
26 #include "../pith/remote.h"
27 #include "../pith/tempfile.h"
28 #include "../pith/busy.h"
29 #include "../pith/osdep/lstcmpnt.h"
30 #include "../pith/util.h"
31 #include "../pith/mailindx.h"
32 #include "../pith/readfile.h"
33 #include "../pith/options.h"
34 #include "smkeys.h"
36 #ifdef APPLEKEYCHAIN
37 #include <Security/SecKeychain.h>
38 #include <Security/SecKeychainItem.h>
39 #include <Security/SecKeychainSearch.h>
40 #include <Security/SecCertificate.h>
41 #endif /* APPLEKEYCHAIN */
44 /* internal prototypes */
45 static char *emailstrclean(char *string);
46 static int mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup);
47 int compare_certs_by_name(const void *data1, const void *data2);
48 int password_policy_check(char *);
50 /* test if password passes a predetermined policy.
51 * return value: 0 - does not pass; 1 - it passes
53 int
54 password_policy_check(char *password)
56 int rv = 1;
57 char *error;
58 char tmp[1024];
60 if(password == NULL || password[0] == '\0'){
61 error = _("Password cannot be blank");
62 rv = 0;
63 } else if(strlen(password) < 8){
64 error = _("Password is too short");
65 rv = 0;
67 if(rv == 0){
68 snprintf(tmp, sizeof(tmp), "%s%s", error, _(". Enter password again"));
69 tmp[sizeof(tmp) - 1] = '\0';
70 q_status_message(SM_ORDER, 3, 3, tmp);
72 return rv;
76 int
77 create_master_password(char *pass, size_t passlen, int first_time)
79 #define MAXTRIAL 3
80 int rv, trial;
81 char prompt[MAILTMPLEN];
82 char passbackup[MAILTMPLEN];
84 if(first_time)
85 q_status_message(SM_ORDER, 3, 3,
86 _(" Creating a Master Password for your Password file "));
87 else
88 q_status_message(SM_ORDER, 3, 3,
89 _(" Retrying to create a Master Password for your Password file "));
91 for(trial = 0; trial < MAXTRIAL; trial++){
92 snprintf(prompt, sizeof(prompt),
93 _("Create master password (attempt %d of %d): "), trial+1, MAXTRIAL);
94 prompt[sizeof(prompt)- 1] = '\0';
95 pass[0] = '\0';
96 do {
97 /* rv == 1 means cancel */
98 rv = (pith_smime_enter_password)(prompt, pass, passlen);
99 if(rv == 1 || password_policy_check(pass) == 0)
100 pass[0] = '\0';
101 if(rv == 1) return 0;
102 } while ((rv != 0 && rv != 1) || (rv == 0 && pass[0] == '\0'));
104 snprintf(prompt, sizeof(prompt),
105 _("Confirm master password (attempt %d of %d): "), trial+1, MAXTRIAL);
106 prompt[sizeof(prompt)- 1] = '\0';
107 passbackup[0] = '\0';
108 do {
109 rv = (pith_smime_enter_password)(prompt, passbackup, sizeof(passbackup));
110 } while ((rv !=0 && rv !=1 && rv > 0) || passbackup[0] == '\0');
111 if(!strcmp(pass, passbackup))
112 break;
113 if(trial + 1 < MAXTRIAL)
114 q_status_message(SM_ORDER, 2, 2, _("Passwords do not match, try again."));
115 else{
116 q_status_message(SM_ORDER, 2, 2, _("Passwords do not match, too many failures."));
117 pass[0] = '\0';
120 return (trial < MAXTRIAL) ? 1 : 0;
124 * Create a self signed certificate with root name _fname_, in directory
125 * _pathdir_. If _version_ is 3, we use the _template_ file as configuration
126 * file for openssl. At this moment, we only call this function with template = NULL
127 * and version = 0, but a sensible call is
128 * ALPINE_self_signed_certificate("/etc/ssl/openssl.cnf", 2, pathdir, fname, first_time);
129 * or so.
130 * _pathdir_ is the directory to save the file,
131 * _fname_ is the root of the name to use. Append ".key" and ".crt" to this name
132 * _first_time_ is an indicator to tell us if this is the first time we call this function
134 PERSONAL_CERT *
135 ALPINE_self_signed_certificate(char *template, int version, char *pathdir, char *fname)
137 BIGNUM *b = NULL;
138 X509_NAME *name = NULL;
139 X509_REQ *req = NULL;
140 EVP_PKEY_CTX *pkctx;
141 BIO *out = NULL;
142 char tmp[MAXPATH+1], password[1024];
143 char *keyfile = NULL, *certfile = NULL;
144 char *extensions = NULL;
145 FILE *fp;
146 long errline = -1L;
147 PERSONAL_CERT *pc = NULL;
148 EVP_PKEY *pkey = NULL;
149 X509 *pcert = NULL;
150 CONF *req_conf = NULL;
151 static int first_time = 1;
153 if(pathdir == NULL)
154 return NULL;
156 if(template){
157 if((out = BIO_new_file(template, "r")) == NULL){
158 q_status_message(SM_ORDER, 2, 2, _("Problem reading configuration file"));
159 return pc;
162 if((req_conf = NCONF_new(NULL)) != NULL
163 && NCONF_load_bio(req_conf, out, &errline) > 0){
164 if((extensions = NCONF_get_string(req_conf, "req", "x509_extensions")) != NULL){
165 X509V3_CTX ctx;
166 X509V3_set_ctx_test(&ctx);
167 X509V3_set_nconf(&ctx, req_conf);
168 if (!X509V3_EXT_add_nconf(req_conf, &ctx, extensions, NULL)) {
169 q_status_message(SM_ORDER, 2, 2, _("Problem loading openssl configuration"));
170 NCONF_free(req_conf);
171 return pc;
175 BIO_free(out);
176 out = NULL;
179 if(create_master_password(password, sizeof(password), first_time)
180 && (pkctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL)) != NULL
181 && EVP_PKEY_keygen_init(pkctx) > 0
182 && EVP_PKEY_CTX_set_rsa_keygen_bits(pkctx, 2048) > 0 /* RSA:2048 */
183 && EVP_PKEY_keygen(pkctx, &pkey) > 0){
184 snprintf(tmp, sizeof(tmp), "%s.key", fname);
185 tmp[sizeof(tmp)-1] = '\0';
186 keyfile = cpystr(tmp);
187 build_path(tmp, pathdir, keyfile, sizeof(tmp));
188 keyfile[strlen(keyfile)-4] = '\0'; /* keyfile does not have .key extension */
189 if((fp = fopen(tmp, "w")) != NULL
190 && (out = BIO_new_fp(fp, BIO_CLOSE | BIO_FP_TEXT)) != NULL
191 && PEM_write_bio_PrivateKey(out, pkey, EVP_des_ede3_cbc(),
192 NULL, 0, NULL, password)){
193 BIO_free(out);
194 out = NULL;
196 memset((void *)password, 0, sizeof(password));
197 if((req = X509_REQ_new()) != NULL
198 && X509_REQ_set_version(req, 0L)){
199 name = X509_REQ_get_subject_name(req);
200 X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *) "Password File Certificate and Key Pair", -1, -1, 0);
201 if(X509_REQ_set_pubkey(req, pkey)
202 && (pcert = X509_new()) != NULL){
203 if(X509_set_version(pcert, version)
204 && (b = BN_new()) != NULL
205 && BN_set_word(b, 65537)
206 && BN_pseudo_rand(b, 64, 0, 0)
207 && X509_get_serialNumber(pcert)
208 && BN_to_ASN1_INTEGER(b, X509_get_serialNumber(pcert)) /* set serial */
209 && X509_set_issuer_name(pcert, X509_REQ_get_subject_name(req))
210 && X509_set_subject_name(pcert, X509_REQ_get_subject_name(req))){
211 X509V3_CTX ext_ctx;
212 EVP_PKEY *tmppkey;
214 X509_gmtime_adj(X509_getm_notBefore(pcert), 0);
215 X509_time_adj_ex(X509_getm_notAfter(pcert), 1095, 0, NULL);
217 if((tmppkey = X509_REQ_get0_pubkey(req)) != NULL
218 && X509_set_pubkey(pcert, tmppkey)){
219 if(extensions != NULL && version == 2){
220 X509V3_set_ctx(&ext_ctx, pcert, pcert, NULL, NULL, 0);
221 if(req_conf){ /* only if template is not null */
222 X509V3_set_nconf(&ext_ctx, req_conf);
223 X509V3_EXT_add_nconf(req_conf, &ext_ctx, extensions, pcert);
226 EVP_PKEY_free(tmppkey);
227 X509_sign(pcert, pkey, NULL);
229 BN_free(b);
234 snprintf(tmp, sizeof(tmp), "%s.crt", fname);
235 tmp[sizeof(tmp)-1] = '\0';
236 certfile = cpystr(tmp);
237 build_path(tmp, pathdir, certfile, sizeof(tmp));
238 if((fp = fopen(tmp, "w")) != NULL
239 &&(out = BIO_new_fp(fp, BIO_FP_TEXT)) != NULL){
240 PEM_write_bio_X509(out, pcert);
241 BIO_flush(out);
242 BIO_free(out);
243 out = NULL;
245 if(req_conf)
246 NCONF_free(req_conf);
248 if(keyfile && certfile && pkey && pcert){
249 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
250 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
251 pc->name = keyfile;
252 pc->key = pkey;
253 pc->cert = pcert;
254 pc->cname = certfile;
256 first_time = 0;
257 return pc;
260 CertList *
261 smime_X509_to_cert_info(X509 *x, char *name)
263 CertList *cert;
264 char buf[MAXPATH+1];
266 if(x == NULL) return NULL;
268 cert = fs_get(sizeof(CertList));
269 memset((void *)cert, 0, sizeof(CertList));
270 cert->x509_cert = x;
271 cert->name = name ? cpystr(name) : NULL;
272 cert->data.date_from = smime_get_date(X509_get0_notBefore(x));
273 cert->data.date_to = smime_get_date(X509_get0_notAfter(x));
274 cert->cn = smime_get_cn(x);
275 get_fingerprint(x, EVP_md5(), buf, sizeof(buf), NULL);
276 cert->data.md5 = cpystr(buf);
278 return cert;
281 #define SMIME_BACKUP_DIR ".backup"
282 #define MAX_TRY_BACKUP 100
284 /* return value: 0 - success, -1 error
285 * Call this function after setting up paths in ps_global->smime
286 * and reading certificates names in certlist.
289 setup_certs_backup_by_type(WhichCerts ctype)
291 int rv = 0; /* assume success */
292 int len;
293 int i, done;
294 char *d, *fname;
295 char p[MAXPATH+1]; /* path to where the backup is */
296 char buf[MAXPATH+1], buf2[MAXPATH+1];
297 struct stat sbuf;
298 CertList *data, *cl;
299 #ifndef _WINDOWS
300 DIR *dirp;
301 struct dirent *df; /* file in the directory */
302 #else /* _WINDOWS */
303 struct _finddata_t dbuf;
304 char bufn[_MAX_PATH + 4];
305 long findrv;
306 #endif /* !_WINDOWS */
307 CertList *cert, *cl2;
308 X509 *x;
309 BIO *in;
311 return rv; /* remove when this function is complete */
313 if(SMHOLDERTYPE(ctype) == Directory){
314 d = PATHCERTDIR(ctype);
315 if(d != NULL){
316 len = strlen(d) + strlen(S_FILESEP) + strlen(SMIME_BACKUP_DIR) + 1;
317 snprintf(p, MAXPATH, "%s%s%s", d, S_FILESEP, SMIME_BACKUP_DIR);
318 p[MAXPATH] = '\0';
319 if(our_stat(p, &sbuf) < 0){
320 if(our_mkpath(p, 0700) != 0)
321 return -1;
322 } else if((sbuf.st_mode & S_IFMT) != S_IFDIR){
323 for(i = 0, done = 0; done == 0 && i < MAX_TRY_BACKUP; i++){
324 snprintf(buf2, len+2, "%s%d", p, i);
325 if(our_stat(buf2, &sbuf) < 0){
326 if(our_mkpath(buf2, 0700) == 0)
327 done++;
329 else if((sbuf.st_mode & S_IFMT) == S_IFDIR)
330 done++;
331 if(done){
332 strncpy(p, buf2, MAXPATH);
333 p[MAXPATH] = '\0';
336 if(done == 0)
337 return -1;
339 /* if we are here, we have a backup directory where to
340 * backup certificates/keys, so now we will go
341 * through the list of certificates and back them up
342 * if we need to.
344 data = BACKUPDATACERT(ctype);
345 for(cl = DATACERT(ctype); cl; cl = cl->next){
346 char clname[MAXPATH+1];
348 snprintf(clname, MAXPATH, "%s%s", cl->name, ctype == Private ? ".key" : "");
349 clname[MAXPATH] = '\0';
350 len = strlen(d) + strlen(clname) + 2;
351 if(len < MAXPATH){
352 snprintf(buf, len, "%s%s%s", d, S_FILESEP, clname);
353 buf[sizeof(buf)-1] = '\0';
354 len = strlen(p) + strlen(clname) + strlen(cl->data.md5) + 3;
355 if(len < MAXPATH){
356 snprintf(buf2, len, "%s%s%s.%s", p, S_FILESEP, clname, cl->data.md5);
357 buf2[sizeof(buf2)-1] = '\0';
358 done = 0; /* recycle done: it means we have a file that may be a certificate*/
359 if(stat(buf2, &sbuf) < 0){
360 if (our_copy(buf2, buf) == 0)
361 done++;
362 } else if((sbuf.st_mode & S_IFMT) == S_IFREG)
363 done++;
365 if(done){
366 switch(ctype){
367 case Public:
368 case CACert:
369 if((in = BIO_new_file(buf2, "r"))!=0){
370 cert = fs_get(sizeof(CertList));
371 memset((void *)cert, 0, sizeof(CertList));
372 cert->x509_cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
373 if(cl->data.date_from != NULL)
374 cert->data.date_from = cpystr(cl->data.date_from);
375 if(cl->data.date_to != NULL)
376 cert->data.date_to = cpystr(cl->data.date_to);
377 if(cl->data.md5 != NULL)
378 cert->data.md5 = cpystr(cl->data.md5);
379 if(cl->cn != NULL)
380 cert->cn = cpystr(cl->cn);
381 snprintf(buf2, len, "%s.%s", cl->name, cl->data.md5);
382 buf2[sizeof(buf2)-1] = '\0';
383 cert->name = cpystr(buf2);
384 if(data == NULL)
385 data = cert;
386 else{
387 for (cl2 = data; cl2 && cl2->next; cl2 = cl2->next);
388 cl2->next = cert;
390 BIO_free(in);
392 break;
394 case Private: break;
395 default: alpine_panic("Bad ctype (0)");
401 /* if we are here, it means we just loaded the backup variable with
402 * a copy of the data that comes from the certlist not coming from
403 * backup. Now we are going to load the contents of the .backup
404 * directory.
407 /* Here is the plan: read the backup directory (in the variable "p")
408 * and attempt to add it. If already there, skip it; otherwise continue
410 #ifndef _WINDOWS
411 if ((dirp = opendir(p)) != NULL) {
412 while ((df = readdir(dirp)) != NULL) {
413 fname = df->d_name;
415 if (fname && *fname == '.') /* no hidden files here */
416 continue;
417 #else
418 snprintf(bufn, sizeof(bufn), "%s%s*.*", p, (p[strlen(p) - 1] == '\\') ? "" : "\\");
419 bufn[sizeof(bufn) - 1] = '\0';
420 if ((findrv = _findfirst(bufn, &dbuf)) >= 0) {
421 do {
422 fname = fname_to_utf8(dbuf.name);
423 #endif /* ! _WINDOWS */
424 /* make sure that we have a file */
425 snprintf(buf2, sizeof(buf2), "%s%s%s", p, S_FILESEP, fname);
426 buf2[sizeof(buf2) - 1] = '\0';
427 if (our_stat(buf2, &sbuf) == 0
428 && (sbuf.st_mode & S_IFMT) != S_IFREG)
429 continue;
431 /* make sure it is not already in the list */
432 for (cl = data; cl; cl = cl->next)
433 if (strcmp(cl->name, fname) == 0)
434 break;
435 if (cl != NULL)
436 continue;
438 /* ok, if it is not in the list, and it is a certificate. Add it */
439 switch (ctype) {
440 case Public:
441 case CACert:
442 if ((in = BIO_new_file(buf2, "r")) != 0) {
443 x = PEM_read_bio_X509(in, NULL, NULL, NULL);
444 if (x) { /* for now copy this information */
445 cert = smime_X509_to_cert_info(x, fname);
446 /* we will use the cert->data.md5 variable to find a backup
447 certificate, not the name */
448 cert->next = data;
449 data = cert;
451 BIO_free(in);
453 break;
455 case Private:
456 /* here we must check it is a key of some cert....*/
457 break;
459 default: alpine_panic("Bad ctype (1)");
460 } /* end switch */
461 #ifndef _WINDOWS
463 closedir(dirp);
464 #else /* _WINDOWS */
465 } while (_findnext(findrv, &dbuf) == 0);
466 _findclose(findrv);
467 #endif /* ! _WINDOWS */
469 /* Now that we are here, we have all the information in the backup
470 * directory
473 switch (ctype) {
474 case Public: ps_global->smime->backuppubliccertlist = data; break;
475 case Private: ps_global->smime->backupprivatecertlist = data; break;
476 case CACert: ps_global->smime->backupcacertlist = data; break;
477 default: alpine_panic("Bad ctype (n)");
480 } else if(SMHOLDERTYPE(ctype) == Container){
482 } /* else APPLEKEYCHAIN */
483 return rv;
486 char *
487 smime_get_cn(X509 *x)
489 X509_NAME_ENTRY *e;
490 X509_NAME *subject;
491 char buf[256];
492 char *rv = NULL;
494 subject = X509_get_subject_name(x);
495 if((e = X509_NAME_get_entry(subject, X509_NAME_entry_count(subject)-1)) != NULL){
496 X509_NAME_get_text_by_OBJ(subject, X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
497 rv = cpystr(buf);
500 return rv;
504 compare_certs_by_name(const void *data1, const void *data2)
506 int rv, i, j;
507 char *s;
509 CertList *cl1 = *(CertList **) data1;
510 CertList *cl2 = *(CertList **) data2;
512 i = j = -1;
513 if((s = strchr(cl1->name, '@')) != NULL){
514 i = s - cl1->name;
515 *s = '\0';
518 if((s = strchr(cl2->name, '@')) != NULL){
519 j = s - cl2->name;
520 *s = '\0';
523 if((rv = strucmp(cl1->name, cl2->name)) == 0)
524 rv = strucmp(cl1->name + i + 1, cl2->name + j + 1);
525 if(i >= 0) cl1->name[i] = '@';
526 if(j >= 0) cl2->name[j] = '@';
527 return rv;
530 void
531 resort_certificates(CertList **data, WhichCerts ctype)
533 int i, j;
534 CertList *cl = *data;
535 CertList **cll;
536 char *s, *t;
538 if(cl == NULL)
539 return;
541 for(i = 0; cl; cl = cl->next, i++)
542 if(SMHOLDERTYPE(ctype) == Directory && ctype != Private){
543 for(t = s = cl->name; (t = strstr(s, ".crt")) != NULL; s = t+1);
544 if (s) *(s-1) = '\0';
546 j = i;
547 cll = fs_get(i*sizeof(CertList *));
548 for(cl = *data, i = 0; cl; cl = cl->next, i++)
549 cll[i] = cl;
550 qsort((void *)cll, j, sizeof(CertList *), compare_certs_by_name);
551 for(i = 0; i < j - 1; i++){
552 cll[i]->next = cll[i+1];
553 if(SMHOLDERTYPE(ctype) == Directory && ctype != Private)
554 cll[i]->name[strlen(cll[i]->name)]= '.'; /* restore ".crt" part */
556 if(SMHOLDERTYPE(ctype) == Directory && ctype != Private)
557 cll[j-1]->name[strlen(cll[j-1]->name)]= '.'; /* restore ".crt" part */
558 cll[j-1]->next = NULL;
559 *data = cll[0];
563 void
564 get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen, char *s)
566 unsigned char md[128];
567 char *b;
568 unsigned int len, i;
570 len = sizeof(md);
572 X509_digest(cert, type, md, &len);
574 b = buf;
575 *b = 0;
576 for(i=0; i<len; i++){
577 if(b-buf+3>=maxLen)
578 break;
580 if(i != 0 && s && *s)
581 *b++ = *s;
583 snprintf(b, maxLen - (b-buf), "%02x", md[i]);
584 b+=2;
590 * Remove leading whitespace, trailing whitespace and convert
591 * to lowercase. Also remove slash characters
593 * Args: s, -- The string to clean
595 * Result: the cleaned string
597 static char *
598 emailstrclean(char *string)
600 char *s = string, *sc = NULL, *p = NULL;
602 for(; *s; s++){ /* single pass */
603 if(!isspace((unsigned char) (*s))){
604 p = NULL; /* not start of blanks */
605 if(!sc) /* first non-blank? */
606 sc = string; /* start copying */
608 else if(!p) /* it's OK if sc == NULL */
609 p = sc; /* start of blanks? */
611 if(sc && *s!='/' && *s!='\\') /* if copying, copy */
612 *sc++ = isupper((unsigned char) (*s))
613 ? (unsigned char) tolower((unsigned char) (*s))
614 : (unsigned char) (*s);
617 if(p) /* if ending blanks */
618 *p = '\0'; /* tie off beginning */
619 else if(!sc) /* never saw a non-blank */
620 *string = '\0'; /* so tie whole thing off */
622 return(string);
626 char *
627 smime_get_date(const ASN1_TIME *tm)
629 BIO *mb = BIO_new(BIO_s_mem());
630 char iobuf[4096];
631 char date[MAILTMPLEN];
632 char buf[MAILTMPLEN];
633 char *m, *d, *t, *y, *z;
634 int len;
635 struct date smd;
636 struct tm smtm;
638 (void) BIO_reset(mb);
639 if(ASN1_TIME_print(mb, tm) == 0)
640 return cpystr(_("Invalid"));
642 (void) BIO_flush(mb);
643 len = BIO_read(mb, iobuf, sizeof(iobuf));
644 iobuf[len-1] = '\0';
646 /* openssl returns the date in the format:
647 * "MONTH (as name) DAY (as number) TIME(hh:mm:ss) YEAR GMT"
649 m = iobuf;
650 d = strchr(iobuf, ' ');
651 *d++ = '\0';
652 while(*d == ' ') d++;
653 t = strchr(d+1, ' ');
654 *t++ = '\0';
655 while(*t == ' ') t++;
656 y = strchr(t+1, ' ');
657 *y++ = '\0';
658 while(*y == ' ') y++;
659 z = strchr(y+1, ' ');
660 *z++ = '\0';
661 while(*z == ' ') z++;
663 snprintf(date, sizeof(date), "%s %s %s %s (%s)", d, m, y, t, z);
664 date[sizeof(date)-1] = '\0';
665 if(F_ON(F_DATES_TO_LOCAL,ps_global)){
666 parse_date(convert_date_to_local(date), &smd);
667 memset(&smtm, 0, sizeof(smtm));
668 smtm.tm_year = smd.year - 1900;
669 smtm.tm_mon = MIN(MAX(smd.month-1, 0), 11);
670 smtm.tm_mday = MIN(MAX(smd.day, 1), 31);
671 our_strftime(buf, sizeof(buf), "%x", &smtm);
673 else
674 snprintf(buf, sizeof(buf), "%s/%s/%s", m, d, y + strlen(y) - 2);
675 buf[sizeof(buf)-1] = '\0';
677 BIO_free(mb);
678 return cpystr(buf);
682 * Add a lookup for each "*.crt*" file in the given directory.
685 add_certs_in_dir(X509_LOOKUP *lookup, char *path, char *ext, CertList **cdata)
687 char buf[MAXPATH], *fname;
688 #ifndef _WINDOWS
689 struct direct *d;
690 DIR *dirp;
691 #else /* _WINDOWS */
692 struct _finddata_t dbuf;
693 char bufn[_MAX_PATH + 4];
694 long findrv;
695 #endif /* !_WINDOWS */
696 CertList *cert, *cl;
697 int ret = 0, nfiles = 0, nerr = 0;
699 #ifndef _WINDOWS
700 if((dirp = opendir(path)) != NULL){
701 while(!ret && (d=readdir(dirp)) != NULL){
702 fname = d->d_name;
703 #else /* _WINDOWS */
704 snprintf(bufn, sizeof(bufn), "%s%s*.*", path, (path[strlen(path)-1] == '\\') ? "" : "\\");
705 bufn[sizeof(bufn)-1] = '\0';
706 if((findrv = _findfirst(bufn, &dbuf)) >= 0){
708 fname = fname_to_utf8(dbuf.name);
709 #endif /* ! _WINDOWS */
710 if(srchrstr(fname, ext)){
711 nfiles++;
712 build_path(buf, path, fname, sizeof(buf));
714 if(!X509_LOOKUP_load_file(lookup, buf, X509_FILETYPE_PEM)){
715 q_status_message1(SM_ORDER, 3, 3, _("Error loading file %s"), buf);
716 nerr++;
717 } else {
718 if(cdata){
719 BIO *in;
720 X509 *x;
722 cert = fs_get(sizeof(CertList));
723 memset((void *)cert, 0, sizeof(CertList));
724 cert->name = cpystr(fname);
725 /* read buf into a bio and fill the CertData structure */
726 if((in = BIO_new_file(buf, "r"))!=0){
727 if((x = PEM_read_bio_X509(in, NULL, NULL, NULL)) != NULL){
728 cert->data.date_from = smime_get_date(X509_get0_notBefore(x));
729 cert->data.date_to = smime_get_date(X509_get0_notAfter(x));
730 get_fingerprint(x, EVP_md5(), buf, sizeof(buf), NULL);
731 cert->data.md5 = cpystr(buf);
732 cert->cn = smime_get_cn(x);
733 X509_free(x);
735 BIO_free(in);
737 if(*cdata == NULL)
738 *cdata = cert;
739 else{
740 for (cl = *cdata; cl && cl->next; cl = cl->next);
741 cl->next = cert;
748 #ifndef _WINDOWS
750 closedir(dirp);
751 #else /* _WINDOWS */
752 } while(_findnext(findrv, &dbuf) == 0);
753 _findclose(findrv);
754 #endif /* ! _WINDOWS */
757 /* if all certificates fail to load */
758 if(nerr > 0 && nerr == nfiles) ret = -1;
759 return ret;
764 * Get an X509_STORE. This consists of the system
765 * certs directory and any certificates in the user's
766 * ~/.alpine-smime/ca directory.
768 X509_STORE *
769 get_ca_store(void)
771 X509_LOOKUP *lookup;
772 X509_STORE *store = NULL;
774 dprint((9, "get_ca_store()"));
776 if(!(store=X509_STORE_new())){
777 dprint((9, "X509_STORE_new() failed"));
778 return store;
781 if(!(lookup=X509_STORE_add_lookup(store, X509_LOOKUP_file()))){
782 dprint((9, "X509_STORE_add_lookup() failed"));
783 free_x509_store(&store);
784 return NULL;
787 if(ps_global->smime && ps_global->smime->catype == Container
788 && ps_global->smime->cacontent){
790 if(!mem_add_extra_cacerts(ps_global->smime->cacontent, lookup)){
791 free_x509_store(&store);
792 return NULL;
795 else if(ps_global->smime && ps_global->smime->catype == Directory
796 && ps_global->smime->capath){
797 if(add_certs_in_dir(lookup, ps_global->smime->capath, ".crt", &ps_global->smime->cacertlist) < 0){
798 free_x509_store(&store);
799 return NULL;
801 resort_certificates(&ps_global->smime->cacertlist, CACert);
804 if(!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()))){
805 free_x509_store(&store);
806 return NULL;
809 #ifdef SMIME_SSLCERTS
810 dprint((9, "get_ca_store(): adding cacerts from %s", SMIME_SSLCERTS));
811 X509_LOOKUP_add_dir(lookup, SMIME_SSLCERTS, X509_FILETYPE_PEM);
812 #endif
814 return store;
817 void
818 free_x509_store(X509_STORE **xstore)
820 if(xstore == NULL || *xstore == NULL)
821 return;
822 X509_STORE_free(*xstore);
823 *xstore = NULL;
826 EVP_PKEY *
827 load_key(PERSONAL_CERT *pc, char *pass, int flag)
829 BIO *in;
830 EVP_PKEY *key = NULL;
831 char buf[MAXPATH], file[MAXPATH];
833 if(!(ps_global->smime && pc && pc->name))
834 return key;
836 if(ps_global->smime->privatetype == Container){
837 char *q;
839 if(pc->keytext && (q = strstr(pc->keytext, "-----END")) != NULL){
840 while(*q && *q != '\n')
841 q++;
843 if(*q == '\n')
844 q++;
846 if((in = BIO_new_mem_buf(pc->keytext, q-pc->keytext)) != NULL){
847 key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass);
848 BIO_free(in);
852 else if(ps_global->smime->privatetype == Directory){
853 /* filename is path/name.key */
854 strncpy(buf, pc->name, sizeof(buf)-5);
855 buf[sizeof(buf)-5] = '\0';
856 strncat(buf, ".key", 5);
857 build_path(file, ps_global->smime->privatepath, buf, sizeof(file));
859 if(!(in = BIO_new_file(file, "r")))
860 return NULL;
862 key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass);
863 BIO_free(in);
866 return key;
870 #include <openssl/x509v3.h>
872 * This newer version is from Adrian Vogel. It looks for the email
873 * address not only in the email address field, but also in an
874 * X509v3 extension field, Subject Alternative Name.
876 char **
877 get_x509_subject_email(X509 *x)
879 char **result = NULL;
880 int i, n;
881 STACK_OF(OPENSSL_STRING) *emails = X509_get1_email(x);
882 if ((n = sk_OPENSSL_STRING_num(emails)) > 0) {
883 result = fs_get((n+1)*sizeof(char *));
884 for(i = 0; i < n; i++)
885 result[i] = cpystr(sk_OPENSSL_STRING_value(emails, i));
886 result[i] = NULL;
888 X509_email_free(emails);
889 return result;
894 * Save the certificate for the given email address in
895 * ~/.alpine-smime/public.
897 * Should consider the security hazards in making a file with
898 * the email address that has come from the certificate.
900 * The argument email is destroyed.
902 * args: ctype says where the user wants to save the certificate
904 void
905 save_cert_for(char *email, X509 *cert, WhichCerts ctype)
907 if(!ps_global->smime || ctype == Private)
908 return;
910 dprint((9, "save_cert_for(%s, %s)", email ? email : "?", ctype == Public ? _("Public") : ctype == Private ? _("Private") : "CACert"));
911 emailstrclean(email);
913 if(ps_global->smime->publictype == Keychain){
914 #ifdef APPLEKEYCHAIN
916 OSStatus rc;
917 SecCertificateRef secCertificateRef;
918 CSSM_DATA certData;
920 memset((void *) &certData, 0, sizeof(certData));
921 memset((void *) &secCertificateRef, 0, sizeof(secCertificateRef));
923 /* convert OpenSSL X509 cert data to MacOS certData */
924 if((certData.Length = i2d_X509(cert, &(certData.Data))) > 0){
927 * Put that certData into a SecCertificateRef.
928 * Version 3 should work for versions 1-3.
930 if(!(rc=SecCertificateCreateFromData(&certData,
931 CSSM_CERT_X_509v3,
932 CSSM_CERT_ENCODING_DER,
933 &secCertificateRef))){
935 /* add it to the default keychain */
936 if(!(rc=SecCertificateAddToKeychain(secCertificateRef, NULL))){
937 /* ok */
939 else if(rc == errSecDuplicateItem){
940 dprint((9, "save_cert_for: certificate for %s already in keychain", email));
942 else{
943 dprint((9, "SecCertificateAddToKeychain failed"));
946 else{
947 dprint((9, "SecCertificateCreateFromData failed"));
950 else{
951 dprint((9, "i2d_X509 failed"));
954 #endif /* APPLEKEYCHAIN */
956 else if(SMHOLDERTYPE(ctype) == Container){
957 REMDATA_S *rd = NULL;
958 char *ret_dir = NULL;
959 char path[MAXPATH];
960 char fpath[MAXPATH];
961 char *upath = PATHCERTDIR(ctype);
962 char *tempfile = NULL;
963 int err = 0;
964 CertList *clist = DATACERT(ctype);
966 add_to_end_of_certlist(&clist, email, X509_dup(cert));
968 switch(ctype){
969 case Private: ps_global->smime->privatecertlist = clist; break;
970 case Public : ps_global->smime->publiccertlist = clist; break;
971 case CACert : ps_global->smime->cacertlist = clist; break;
972 default: break;
975 if(!upath)
976 return;
978 if(IS_REMOTE(upath)){
979 rd = rd_create_remote(RemImap, upath, REMOTE_SMIME_SUBTYPE,
980 NULL, "Error: ",
981 _("Can't access remote smime configuration."));
982 if(!rd){
983 return;
986 (void) rd_read_metadata(rd);
988 if(rd->access == MaybeRorW){
989 if(rd->read_status == 'R')
990 rd->access = ReadOnly;
991 else
992 rd->access = ReadWrite;
995 if(rd->access != NoExists){
997 rd_check_remvalid(rd, 1L);
1000 * If the cached info says it is readonly but
1001 * it looks like it's been fixed now, change it to readwrite.
1003 if(rd->read_status == 'R'){
1004 rd_check_readonly_access(rd);
1005 if(rd->read_status == 'W'){
1006 rd->access = ReadWrite;
1007 rd->flags |= REM_OUTOFDATE;
1009 else
1010 rd->access = ReadOnly;
1014 if(rd->flags & REM_OUTOFDATE){
1015 if(rd_update_local(rd) != 0){
1017 dprint((1, "save_cert_for: rd_update_local failed\n"));
1018 rd_close_remdata(&rd);
1019 return;
1022 else
1023 rd_open_remote(rd);
1025 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1026 rd_close_remdata(&rd);
1027 return;
1030 rd->flags |= DO_REMTRIM;
1032 strncpy(path, rd->lf, sizeof(path)-1);
1033 path[sizeof(path)-1] = '\0';
1035 else{
1036 strncpy(path, upath, sizeof(path)-1);
1037 path[sizeof(path)-1] = '\0';
1040 tempfile = tempfile_in_same_dir(path, "az", &ret_dir);
1041 if(tempfile){
1042 if(certlist_to_file(tempfile, DATACERT(ctype)))
1043 err++;
1045 if(!err && ret_dir){
1046 if(IS_REMOTE(upath)){
1047 strncpy(fpath, rd->lf, sizeof(fpath));
1048 fpath[sizeof(fpath)-1] = '\0';
1050 else{
1051 if(strlen(path) + strlen(tempfile) - strlen(ret_dir) + 1 < sizeof(path))
1052 snprintf(fpath, sizeof(fpath), "%.*s%c%.*s",
1053 (int) strlen(path), path,
1054 tempfile[strlen(ret_dir)],
1055 (int) (sizeof(fpath) - strlen(fpath) - 1),
1056 tempfile + strlen(ret_dir) + 1);
1057 else
1058 err++;
1061 else err++;
1063 fs_give((void **)&ret_dir);
1065 if(!err){
1066 if(rename_file(tempfile, fpath) < 0){
1067 q_status_message2(SM_ORDER, 3, 3,
1068 _("Can't rename %s to %s"), tempfile, fpath);
1069 err++;
1073 if(!err && IS_REMOTE(upath)){
1074 int e, we_cancel;
1075 char datebuf[200];
1077 datebuf[0] = '\0';
1079 we_cancel = busy_cue(_("Copying to remote smime container"), NULL, 1);
1080 if((e = rd_update_remote(rd, datebuf)) != 0){
1081 if(e == -1){
1082 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1083 _("Error opening temporary smime file %s: %s"),
1084 rd->lf, error_description(errno));
1085 dprint((1,
1086 "write_remote_smime: error opening temp file %s\n",
1087 rd->lf ? rd->lf : "?"));
1089 else{
1090 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1091 _("Error copying to %s: %s"),
1092 rd->rn, error_description(errno));
1093 dprint((1,
1094 "write_remote_smime: error copying from %s to %s\n",
1095 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1098 q_status_message(SM_ORDER | SM_DING, 5, 5,
1099 _("Copy of smime cert to remote folder failed, changes NOT saved remotely"));
1101 else{
1102 rd_update_metadata(rd, datebuf);
1103 rd->read_status = 'W';
1106 rd_close_remdata(&rd);
1108 if(we_cancel)
1109 cancel_busy_cue(-1);
1112 fs_give((void **) &tempfile);
1115 else if(SMHOLDERTYPE(ctype) == Directory){
1116 char *path = PATHCERTDIR(ctype);
1117 char certfilename[MAXPATH];
1118 BIO *bio_out;
1120 build_path(certfilename, path, email, sizeof(certfilename));
1121 strncat(certfilename, ".crt", sizeof(certfilename)-1-strlen(certfilename));
1122 certfilename[sizeof(certfilename)-1] = 0;
1124 bio_out = BIO_new_file(certfilename, "w");
1125 if(bio_out){
1126 PEM_write_bio_X509(bio_out, cert);
1127 BIO_free(bio_out);
1128 q_status_message1(SM_ORDER, 1, 1, _("Saved certificate for <%s>"), email);
1130 else{
1131 q_status_message1(SM_ORDER, 1, 1, _("Couldn't save certificate for <%s>"), email);
1138 * Try to retrieve the certificate for the given email address.
1139 * The caller should free the cert.
1141 X509 *
1142 get_cert_for(char *email, WhichCerts ctype, int tolower)
1144 char certfilename[MAXPATH];
1145 char emailaddr[MAXPATH];
1146 X509 *cert = NULL;
1147 BIO *in;
1149 if(ctype == Password){
1150 build_path(certfilename, PATHCERTDIR(ctype), email, sizeof(certfilename));
1151 strncat(certfilename, EXTCERT(Public), sizeof(certfilename)-1-strlen(certfilename));
1152 certfilename[sizeof(certfilename)-1] = 0;
1154 if((in = BIO_new_file(certfilename, "r"))!=0){
1156 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
1158 if(cert){
1159 /* could check email addr in cert matches */
1162 BIO_free(in);
1165 return cert;
1168 if(!ps_global->smime)
1169 return cert;
1171 dprint((9, "get_cert_for(%s, %s)", email ? email : "?", "none yet"));
1173 if(ctype == Private) /* there is no private certificate info */
1174 ctype = Public; /* return public information instead */
1175 strncpy(emailaddr, email, sizeof(emailaddr)-1);
1176 emailaddr[sizeof(emailaddr)-1] = 0;
1178 /* clean it up (lowercase, space removal) */
1179 if(tolower)
1180 emailstrclean(emailaddr);
1182 if(ps_global->smime->publictype == Keychain){
1183 #ifdef APPLEKEYCHAIN
1185 OSStatus rc;
1186 SecKeychainItemRef itemRef = nil;
1187 SecKeychainAttributeList attrList;
1188 SecKeychainAttribute attrib;
1189 SecKeychainSearchRef searchRef = nil;
1190 CSSM_DATA certData;
1192 /* low-level form of MacOS data */
1193 memset((void *) &certData, 0, sizeof(certData));
1195 attrList.count = 1;
1196 attrList.attr = &attrib;
1198 /* kSecAlias means email address for a certificate */
1199 attrib.tag = kSecAlias;
1200 attrib.data = emailaddr;
1201 attrib.length = strlen(attrib.data);
1203 /* Find the certificate in the default keychain */
1204 if(!(rc=SecKeychainSearchCreateFromAttributes(NULL,
1205 kSecCertificateItemClass,
1206 &attrList,
1207 &searchRef))){
1209 if(!(rc=SecKeychainSearchCopyNext(searchRef, &itemRef))){
1211 /* extract the data portion of the certificate */
1212 if(!(rc=SecCertificateGetData((SecCertificateRef) itemRef, &certData))){
1215 * Convert it from MacOS form to OpenSSL form.
1216 * The input is certData from above and the output
1217 * is the X509 *cert.
1219 if(!d2i_X509(&cert, &(certData.Data), certData.Length)){
1220 dprint((9, "d2i_X509 failed"));
1223 else{
1224 dprint((9, "SecCertificateGetData failed"));
1227 else if(rc == errSecItemNotFound){
1228 dprint((9, "get_cert_for: Public cert for %s not found", emailaddr));
1230 else{
1231 dprint((9, "SecKeychainSearchCopyNext failed"));
1234 else{
1235 dprint((9, "SecKeychainSearchCreateFromAttributes failed"));
1238 if(searchRef)
1239 CFRelease(searchRef);
1241 #endif /* APPLEKEYCHAIN */
1243 else if(SMHOLDERTYPE(ctype) == Container){
1244 CertList *cl;
1246 for(cl = DATACERT(ctype); cl; cl = cl->next){
1247 if(cl->name && !strucmp(emailaddr, cl->name))
1248 break;
1251 if(cl)
1252 cert = X509_dup((X509 *) cl->x509_cert);
1254 else if(SMHOLDERTYPE(ctype) == Directory){
1255 build_path(certfilename, PATHCERTDIR(ctype), emailaddr, sizeof(certfilename));
1256 strncat(certfilename, EXTCERT(ctype), sizeof(certfilename)-1-strlen(certfilename));
1257 certfilename[sizeof(certfilename)-1] = 0;
1259 if((in = BIO_new_file(certfilename, "r"))!=0){
1261 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
1263 if(cert){
1264 /* could check email addr in cert matches */
1267 BIO_free(in);
1272 return cert;
1276 * load_cert_for_key finds a certificate in pathdir that matches a private key
1277 * pkey. It returns its name in certfile, and the certificate in *pcert.
1278 * return value: success: different from zero, failure 0. If both certfile
1279 * and pcert are NULL, this function returns if there is certificate that
1280 * matches the given key.
1283 load_cert_for_key(char *pathdir, EVP_PKEY *pkey, char **certfile, X509 **pcert)
1285 #ifndef _WINDOWS
1286 DIR *dirp;
1287 struct dirent *d;
1288 #else /* _WINDOWS */
1289 struct _finddata_t dbuf;
1290 char bufn[_MAX_PATH + 4];
1291 long findrv;
1292 #endif /* ! _WINDOWS */
1293 size_t ll;
1294 int rv = 0;
1295 BIO *in;
1296 X509 *x;
1297 char buf[MAXPATH+1], pathcert[MAXPATH+1], *fname;
1299 if(pathdir == NULL || pkey == NULL)
1300 return 0;
1302 if(certfile) *certfile = NULL;
1303 if(pcert) *pcert = NULL;
1305 #ifndef _WINDOWS
1306 if((dirp = opendir(pathdir)) != NULL){
1307 while(rv == 0 && (d=readdir(dirp)) != NULL){
1308 fname = d->d_name;
1309 #else
1310 snprintf(bufn, sizeof(bufn), "%s%s*.*", pathdir, (pathdir[strlen(pathdir)-1] == '\\') ? "" : "\\");
1311 bufn[sizeof(bufn)-1] = '\0';
1312 if((findrv = _findfirst(bufn, &dbuf)) >= 0){
1314 fname = fname_to_utf8(dbuf.name);
1315 #endif /* ! _WINDOWS */
1316 if((ll=strlen(fname)) && ll > 4){
1317 if(!strcmp(fname+ll-4, ".crt")){
1318 strncpy(buf, fname, sizeof(buf));
1319 buf[sizeof(buf)-1] = '\0';
1320 build_path(pathcert, pathdir, buf, sizeof(pathcert));
1321 if((in = BIO_new_file(pathcert, "r")) != NULL){
1322 if((x = PEM_read_bio_X509(in, NULL, NULL, NULL)) != NULL){
1323 if(X509_check_private_key(x, pkey) > 0){
1324 rv = 1;
1325 if(certfile) *certfile = cpystr(buf);
1326 if(pcert) *pcert = x;
1328 else
1329 X509_free(x);
1331 BIO_free(in);
1335 #ifndef _WINDOWS
1337 closedir(dirp);
1338 #else /* _WINDOWS */
1339 } while(_findnext(findrv, &dbuf) == 0);
1340 _findclose(findrv);
1341 #endif
1343 return rv;
1347 PERSONAL_CERT *
1348 mem_to_personal_certs(char *contents)
1350 PERSONAL_CERT *result = NULL;
1351 char *p, *q, *line, *name, *keytext, *save_p;
1352 X509 *cert = NULL;
1354 if(contents && *contents){
1355 for(p = contents; *p != '\0';){
1356 line = p;
1358 while(*p && *p != '\n')
1359 p++;
1361 save_p = NULL;
1362 if(*p == '\n'){
1363 save_p = p;
1364 *p++ = '\0';
1367 if(strncmp(EMAILADDRLEADER, line, strlen(EMAILADDRLEADER)) == 0){
1368 name = line + strlen(EMAILADDRLEADER);
1369 cert = get_cert_for(name, Public, 1);
1370 keytext = p;
1372 /* advance p past this record */
1373 if((q = strstr(keytext, "-----END")) != NULL){
1374 while(*q && *q != '\n')
1375 q++;
1377 if(*q == '\n')
1378 q++;
1380 p = q;
1382 else{
1383 p = p + strlen(p);
1384 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error in privatekey container, missing END"));
1387 if(cert){
1388 PERSONAL_CERT *pc;
1390 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1391 pc->cert = cert;
1392 pc->name = cpystr(name);
1393 pc->cname = NULL;
1394 pc->keytext = keytext; /* a pointer into contents */
1396 pc->key = load_key(pc, "", SM_NORMALCERT);
1398 pc->next = result;
1399 result = pc;
1403 if(save_p)
1404 *save_p = '\n';
1408 return result;
1412 CertList *
1413 mem_to_certlist(char *contents, WhichCerts ctype)
1415 CertList *ret = NULL;
1416 char *p, *q, *line, *name, *certtext, *save_p;
1417 X509 *cert = NULL;
1418 BIO *in;
1419 char *sep = (ctype == Public || ctype == Private)
1420 ? EMAILADDRLEADER : CACERTSTORELEADER;
1422 if(contents && *contents){
1423 for(p = contents; *p != '\0';){
1424 line = p;
1426 while(*p && *p != '\n')
1427 p++;
1429 save_p = NULL;
1430 if(*p == '\n'){
1431 save_p = p;
1432 *p++ = '\0';
1435 if(strncmp(sep, line, strlen(sep)) == 0){
1436 name = line + strlen(sep);
1437 cert = NULL;
1438 certtext = strstr(p, "-----BEGIN");
1439 if(certtext != NULL){
1440 if((q = strstr(certtext, sep)) != NULL)
1441 p = q;
1442 else
1443 p = q = certtext+strlen(certtext);
1445 if((in = BIO_new_mem_buf(certtext, q-certtext)) != 0){
1446 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
1447 BIO_free(in);
1450 else{
1451 q_status_message2(SM_ORDER | SM_DING, 3, 3, _("Error in %scert container, missing BEGIN, certtext=%s"), ctype == Public ? _("public") : _("ca"), p);
1452 p = p + strlen(p);
1455 if(name && cert)
1456 add_to_end_of_certlist(&ret, name, cert);
1459 if(save_p)
1460 *save_p = '\n';
1463 if(ret != NULL)
1464 resort_certificates(&ret, ctype);
1466 return ret;
1471 * Add the CACert Container contents into the CACert store.
1473 * Returns > 0 for success, 0 for failure
1476 mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup)
1478 char *p, *q, *line, *certtext, *save_p;
1479 BIO *in, *out;
1480 int len, failed = 0;
1481 char *tempfile;
1482 char iobuf[4096];
1485 * The most straight-forward way to do this is to write
1486 * the container contents to a temp file and then load the
1487 * contents of the file with X509_LOOKUP_load_file(), like
1488 * is done in add_certs_in_dir(). What we don't know is if
1489 * each file should consist of one cacert or if they can all
1490 * just be jammed together into one file. To be safe, we'll use
1491 * one file per and do each in a separate operation.
1494 if(contents && *contents){
1495 for(p = contents; *p != '\0';){
1496 line = p;
1498 while(*p && *p != '\n')
1499 p++;
1501 save_p = NULL;
1502 if(*p == '\n'){
1503 save_p = p;
1504 *p++ = '\0';
1507 /* look for separator line */
1508 if(strncmp(CACERTSTORELEADER, line, strlen(CACERTSTORELEADER)) == 0){
1509 /* certtext is the content that should go in a file */
1510 certtext = strstr(p, "-----BEGIN");
1511 if(certtext != NULL){
1512 if((q = strstr(certtext, CACERTSTORELEADER)) != NULL){
1513 p = q;
1515 else{ /* end of file */
1516 q = certtext + strlen(certtext);
1517 p = q;
1520 in = BIO_new_mem_buf(certtext, q-certtext);
1521 if(in){
1522 tempfile = temp_nam(NULL, "az");
1523 out = tempfile != NULL ? BIO_new_file(tempfile, "w") : NULL;
1524 if(out != NULL){
1525 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1526 BIO_write(out, iobuf, len);
1528 BIO_free(out);
1529 if(!X509_LOOKUP_load_file(lookup, tempfile, X509_FILETYPE_PEM))
1530 failed++;
1533 if(tempfile != NULL){
1534 unlink(tempfile);
1535 fs_give((void **) &tempfile);
1538 BIO_free(in);
1541 else{
1542 p = p + strlen(p);
1543 q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in cacert container, missing BEGIN, certtext=%s"), certtext);
1546 else{
1547 p = p + strlen(p);
1548 q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in cacert container, missing separator, line=%s"), line);
1551 if(save_p)
1552 *save_p = '\n';
1556 return(!failed);
1561 certlist_to_file(char *filename, CertList *certlist)
1563 CertList *cl;
1564 BIO *bio_out = NULL;
1565 int ret = -1;
1567 if(filename && (bio_out=BIO_new_file(filename, "w")) != NULL){
1568 ret = 0;
1569 for(cl = certlist; cl; cl = cl->next){
1570 if(cl->name && cl->name[0] && cl->x509_cert){
1571 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1572 && (BIO_puts(bio_out, cl->name) > 0)
1573 && (BIO_puts(bio_out, "\n") > 0)))
1574 ret = -1;
1576 if(!PEM_write_bio_X509(bio_out, (X509 *) cl->x509_cert))
1577 ret = -1;
1581 BIO_free(bio_out);
1584 return ret;
1588 void
1589 add_to_end_of_certlist(CertList **cl, char *name, X509 *cert)
1591 CertList *new;
1593 if(!cl)
1594 return;
1596 new = smime_X509_to_cert_info(cert, name);
1597 new->next = *cl;
1598 *cl = new;
1602 void
1603 free_certlist(CertList **cl)
1605 if(cl && *cl){
1606 if((*cl)->data.date_from)
1607 fs_give((void **) &(*cl)->data.date_from);
1609 if((*cl)->data.date_to)
1610 fs_give((void **) &(*cl)->data.date_to);
1612 if((*cl)->data.md5)
1613 fs_give((void **) &(*cl)->data.md5);
1615 if((*cl)->name)
1616 fs_give((void **) &(*cl)->name);
1618 if((*cl)->cn)
1619 fs_give((void **) &(*cl)->cn);
1621 if((*cl)->x509_cert)
1622 X509_free((X509 *) (*cl)->x509_cert);
1624 free_certlist(&(*cl)->next);
1626 fs_give((void **) cl);
1631 void
1632 free_personal_certs(PERSONAL_CERT **pc)
1634 if(pc && *pc){
1635 if((*pc)->name)
1636 fs_give((void **) &(*pc)->name);
1638 if((*pc)->cname)
1639 fs_give((void **) &(*pc)->cname);
1641 if((*pc)->cert)
1642 X509_free((*pc)->cert);
1644 if((*pc)->key)
1645 EVP_PKEY_free((*pc)->key);
1647 free_personal_certs(&(*pc)->next);
1649 fs_give((void **) pc);
1653 #endif /* SMIME */