* New alpha version 2.24.1
[alpine.git] / pith / smkeys.c
blob495f0a5316bf6119ca48854980368cb7539a6c31
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-2021 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 "../pith/options.h"
38 #include "smkeys.h"
40 #ifdef APPLEKEYCHAIN
41 #include <Security/SecKeychain.h>
42 #include <Security/SecKeychainItem.h>
43 #include <Security/SecKeychainSearch.h>
44 #include <Security/SecCertificate.h>
45 #endif /* APPLEKEYCHAIN */
48 /* internal prototypes */
49 static char *emailstrclean(char *string);
50 static int mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup);
51 int compare_certs_by_name(const void *data1, const void *data2);
52 int password_policy_check(char *);
54 /* test if password passes a predetermined policy.
55 * return value: 0 - does not pass; 1 - it passes
57 int
58 password_policy_check(char *password)
60 int rv = 1;
61 char *error;
62 char tmp[1024];
64 if(password == NULL || password[0] == '\0'){
65 error = _("Password cannot be blank");
66 rv = 0;
67 } else if(strlen(password) < 8){
68 error = _("Password is too short");
69 rv = 0;
71 if(rv == 0){
72 snprintf(tmp, sizeof(tmp), "%s%s", error, _(". Enter password again"));
73 tmp[sizeof(tmp) - 1] = '\0';
74 q_status_message(SM_ORDER, 3, 3, tmp);
76 return rv;
80 int
81 create_master_password(char *pass, size_t passlen, int first_time)
83 #define MAXTRIAL 3
84 int rv, trial;
85 char prompt[MAILTMPLEN];
86 char passbackup[MAILTMPLEN];
88 if(first_time)
89 q_status_message(SM_ORDER, 3, 3,
90 _(" Creating a Master Password for your Password file "));
91 else
92 q_status_message(SM_ORDER, 3, 3,
93 _(" Retrying to create a Master Password for your Password file "));
95 for(trial = 0; trial < MAXTRIAL; trial++){
96 snprintf(prompt, sizeof(prompt),
97 _("Create master password (attempt %d of %d): "), trial+1, MAXTRIAL);
98 prompt[sizeof(prompt)- 1] = '\0';
99 pass[0] = '\0';
100 do {
101 /* rv == 1 means cancel */
102 rv = (pith_smime_enter_password)(prompt, pass, passlen);
103 if(rv == 1 || password_policy_check(pass) == 0)
104 pass[0] = '\0';
105 if(rv == 1) return 0;
106 } while ((rv != 0 && rv != 1) || (rv == 0 && pass[0] == '\0'));
108 snprintf(prompt, sizeof(prompt),
109 _("Confirm master password (attempt %d of %d): "), trial+1, MAXTRIAL);
110 prompt[sizeof(prompt)- 1] = '\0';
111 passbackup[0] = '\0';
112 do {
113 rv = (pith_smime_enter_password)(prompt, passbackup, sizeof(passbackup));
114 } while ((rv !=0 && rv !=1 && rv > 0) || passbackup[0] == '\0');
115 if(!strcmp(pass, passbackup))
116 break;
117 if(trial + 1 < MAXTRIAL)
118 q_status_message(SM_ORDER, 2, 2, _("Passwords do not match, try again."));
119 else{
120 q_status_message(SM_ORDER, 2, 2, _("Passwords do not match, too many failures."));
121 pass[0] = '\0';
124 return (trial < MAXTRIAL) ? 1 : 0;
128 * Create a self signed certificate with root name _fname_, in directory
129 * _pathdir_. If _version_ is 3, we use the _template_ file as configuration
130 * file for openssl. At this moment, we only call this function with template = NULL
131 * and version = 0, but a sensible call is
132 * ALPINE_self_signed_certificate("/etc/ssl/openssl.cnf", 2, pathdir, fname, first_time);
133 * or so.
134 * _pathdir_ is the directory to save the file,
135 * _fname_ is the root of the name to use. Append ".key" and ".crt" to this name
136 * _first_time_ is an indicator to tell us if this is the first time we call this function
138 PERSONAL_CERT *
139 ALPINE_self_signed_certificate(char *template, int version, char *pathdir, char *fname)
141 BIGNUM *b = NULL;
142 X509_NAME *name = NULL;
143 X509_REQ *req = NULL;
144 EVP_PKEY_CTX *pkctx;
145 BIO *out = NULL;
146 char tmp[MAXPATH+1], password[1024];
147 char *keyfile = NULL, *certfile = NULL;
148 char *extensions = NULL;
149 FILE *fp;
150 long errline = -1L;
151 PERSONAL_CERT *pc = NULL;
152 EVP_PKEY *pkey = NULL;
153 X509 *pcert = NULL;
154 CONF *req_conf = NULL;
155 static int first_time = 1;
157 if(pathdir == NULL)
158 return NULL;
160 if(template){
161 if((out = BIO_new_file(template, "r")) == NULL){
162 q_status_message(SM_ORDER, 2, 2, _("Problem reading configuration file"));
163 return pc;
166 if((req_conf = NCONF_new(NULL)) != NULL
167 && NCONF_load_bio(req_conf, out, &errline) > 0){
168 if((extensions = NCONF_get_string(req_conf, "req", "x509_extensions")) != NULL){
169 X509V3_CTX ctx;
170 X509V3_set_ctx_test(&ctx);
171 X509V3_set_nconf(&ctx, req_conf);
172 if (!X509V3_EXT_add_nconf(req_conf, &ctx, extensions, NULL)) {
173 q_status_message(SM_ORDER, 2, 2, _("Problem loading openssl configuration"));
174 NCONF_free(req_conf);
175 return pc;
179 BIO_free(out);
180 out = NULL;
183 if(create_master_password(password, sizeof(password), first_time)
184 && (pkctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL)) != NULL
185 && EVP_PKEY_keygen_init(pkctx) > 0
186 && EVP_PKEY_CTX_set_rsa_keygen_bits(pkctx, 2048) > 0 /* RSA:2048 */
187 && EVP_PKEY_keygen(pkctx, &pkey) > 0){
188 snprintf(tmp, sizeof(tmp), "%s.key", fname);
189 tmp[sizeof(tmp)-1] = '\0';
190 keyfile = cpystr(tmp);
191 build_path(tmp, pathdir, keyfile, sizeof(tmp));
192 keyfile[strlen(keyfile)-4] = '\0'; /* keyfile does not have .key extension */
193 if((fp = fopen(tmp, "w")) != NULL
194 && (out = BIO_new_fp(fp, BIO_CLOSE | BIO_FP_TEXT)) != NULL
195 && PEM_write_bio_PrivateKey(out, pkey, EVP_des_ede3_cbc(),
196 NULL, 0, NULL, password)){
197 BIO_free(out);
198 out = NULL;
200 memset((void *)password, 0, sizeof(password));
201 if((req = X509_REQ_new()) != NULL
202 && X509_REQ_set_version(req, 0L)){
203 name = X509_REQ_get_subject_name(req);
204 X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *) "Password File Certificate and Key Pair", -1, -1, 0);
205 if(X509_REQ_set_pubkey(req, pkey)
206 && (pcert = X509_new()) != NULL){
207 if(X509_set_version(pcert, version)
208 && (b = BN_new()) != NULL
209 && BN_set_word(b, 65537)
210 && BN_pseudo_rand(b, 64, 0, 0)
211 && X509_get_serialNumber(pcert)
212 && BN_to_ASN1_INTEGER(b, X509_get_serialNumber(pcert)) /* set serial */
213 && X509_set_issuer_name(pcert, X509_REQ_get_subject_name(req))
214 && X509_set_subject_name(pcert, X509_REQ_get_subject_name(req))){
215 X509V3_CTX ext_ctx;
216 EVP_PKEY *tmppkey;
218 X509_gmtime_adj(X509_getm_notBefore(pcert), 0);
219 X509_time_adj_ex(X509_getm_notAfter(pcert), 1095, 0, NULL);
221 if((tmppkey = X509_REQ_get0_pubkey(req)) != NULL
222 && X509_set_pubkey(pcert, tmppkey)){
223 if(extensions != NULL && version == 2){
224 X509V3_set_ctx(&ext_ctx, pcert, pcert, NULL, NULL, 0);
225 if(req_conf){ /* only if template is not null */
226 X509V3_set_nconf(&ext_ctx, req_conf);
227 X509V3_EXT_add_nconf(req_conf, &ext_ctx, extensions, pcert);
230 EVP_PKEY_free(tmppkey);
231 X509_sign(pcert, pkey, NULL);
233 BN_free(b);
238 snprintf(tmp, sizeof(tmp), "%s.crt", fname);
239 tmp[sizeof(tmp)-1] = '\0';
240 certfile = cpystr(tmp);
241 build_path(tmp, pathdir, certfile, sizeof(tmp));
242 if((fp = fopen(tmp, "w")) != NULL
243 &&(out = BIO_new_fp(fp, BIO_FP_TEXT)) != NULL){
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, *fname;
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 #ifndef _WINDOWS
304 DIR *dirp;
305 struct dirent *df; /* file in the directory */
306 #else /* _WINDOWS */
307 struct _finddata_t dbuf;
308 char bufn[_MAX_PATH + 4];
309 long findrv;
310 #endif /* !_WINDOWS */
311 CertList *cert, *cl2;
312 X509 *x;
313 BIO *in;
315 return rv; /* remove when this function is complete */
317 if(SMHOLDERTYPE(ctype) == Directory){
318 d = PATHCERTDIR(ctype);
319 if(d != NULL){
320 len = strlen(d) + strlen(S_FILESEP) + strlen(SMIME_BACKUP_DIR) + 1;
321 snprintf(p, MAXPATH, "%s%s%s", d, S_FILESEP, SMIME_BACKUP_DIR);
322 p[MAXPATH] = '\0';
323 if(our_stat(p, &sbuf) < 0){
324 if(our_mkpath(p, 0700) != 0)
325 return -1;
326 } else if((sbuf.st_mode & S_IFMT) != S_IFDIR){
327 for(i = 0, done = 0; done == 0 && i < MAX_TRY_BACKUP; i++){
328 snprintf(buf2, len+2, "%s%d", p, i);
329 if(our_stat(buf2, &sbuf) < 0){
330 if(our_mkpath(buf2, 0700) == 0)
331 done++;
333 else if((sbuf.st_mode & S_IFMT) == S_IFDIR)
334 done++;
335 if(done){
336 strncpy(p, buf2, MAXPATH);
337 p[MAXPATH] = '\0';
340 if(done == 0)
341 return -1;
343 /* if we are here, we have a backup directory where to
344 * backup certificates/keys, so now we will go
345 * through the list of certificates and back them up
346 * if we need to.
348 data = BACKUPDATACERT(ctype);
349 for(cl = DATACERT(ctype); cl; cl = cl->next){
350 char clname[MAXPATH+1];
352 snprintf(clname, MAXPATH, "%s%s", cl->name, ctype == Private ? ".key" : "");
353 clname[MAXPATH] = '\0';
354 len = strlen(d) + strlen(clname) + 2;
355 if(len < MAXPATH){
356 snprintf(buf, len, "%s%s%s", d, S_FILESEP, clname);
357 buf[sizeof(buf)-1] = '\0';
358 len = strlen(p) + strlen(clname) + strlen(cl->data.md5) + 3;
359 if(len < MAXPATH){
360 snprintf(buf2, len, "%s%s%s.%s", p, S_FILESEP, clname, cl->data.md5);
361 buf2[sizeof(buf2)-1] = '\0';
362 done = 0; /* recycle done: it means we have a file that may be a certificate*/
363 if(stat(buf2, &sbuf) < 0){
364 if (our_copy(buf2, buf) == 0)
365 done++;
366 } else if((sbuf.st_mode & S_IFMT) == S_IFREG)
367 done++;
369 if(done){
370 switch(ctype){
371 case Public:
372 case CACert:
373 if((in = BIO_new_file(buf2, "r"))!=0){
374 cert = fs_get(sizeof(CertList));
375 memset((void *)cert, 0, sizeof(CertList));
376 cert->x509_cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
377 if(cl->data.date_from != NULL)
378 cert->data.date_from = cpystr(cl->data.date_from);
379 if(cl->data.date_to != NULL)
380 cert->data.date_to = cpystr(cl->data.date_to);
381 if(cl->data.md5 != NULL)
382 cert->data.md5 = cpystr(cl->data.md5);
383 if(cl->cn != NULL)
384 cert->cn = cpystr(cl->cn);
385 snprintf(buf2, len, "%s.%s", cl->name, cl->data.md5);
386 buf2[sizeof(buf2)-1] = '\0';
387 cert->name = cpystr(buf2);
388 if(data == NULL)
389 data = cert;
390 else{
391 for (cl2 = data; cl2 && cl2->next; cl2 = cl2->next);
392 cl2->next = cert;
394 BIO_free(in);
396 break;
398 case Private: break;
399 default: alpine_panic("Bad ctype (0)");
405 /* if we are here, it means we just loaded the backup variable with
406 * a copy of the data that comes from the certlist not coming from
407 * backup. Now we are going to load the contents of the .backup
408 * directory.
411 /* Here is the plan: read the backup directory (in the variable "p")
412 * and attempt to add it. If already there, skip it; otherwise continue
414 #ifndef _WINDOWS
415 if ((dirp = opendir(p)) != NULL) {
416 while ((df = readdir(dirp)) != NULL) {
417 fname = df->d_name;
419 if (fname && *fname == '.') /* no hidden files here */
420 continue;
421 #else
422 snprintf(bufn, sizeof(bufn), "%s%s*.*", p, (p[strlen(p) - 1] == '\\') ? "" : "\\");
423 bufn[sizeof(bufn) - 1] = '\0';
424 if ((findrv = _findfirst(bufn, &dbuf)) >= 0) {
425 do {
426 fname = fname_to_utf8(dbuf.name);
427 #endif /* ! _WINDOWS */
428 /* make sure that we have a file */
429 snprintf(buf2, sizeof(buf2), "%s%s%s", p, S_FILESEP, fname);
430 buf2[sizeof(buf2) - 1] = '\0';
431 if (our_stat(buf2, &sbuf) == 0
432 && (sbuf.st_mode & S_IFMT) != S_IFREG)
433 continue;
435 /* make sure it is not already in the list */
436 for (cl = data; cl; cl = cl->next)
437 if (strcmp(cl->name, fname) == 0)
438 break;
439 if (cl != NULL)
440 continue;
442 /* ok, if it is not in the list, and it is a certificate. Add it */
443 switch (ctype) {
444 case Public:
445 case CACert:
446 if ((in = BIO_new_file(buf2, "r")) != 0) {
447 x = PEM_read_bio_X509(in, NULL, NULL, NULL);
448 if (x) { /* for now copy this information */
449 cert = smime_X509_to_cert_info(x, fname);
450 /* we will use the cert->data.md5 variable to find a backup
451 certificate, not the name */
452 cert->next = data;
453 data = cert;
455 BIO_free(in);
457 break;
459 case Private:
460 /* here we must check it is a key of some cert....*/
461 break;
463 default: alpine_panic("Bad ctype (1)");
464 } /* end switch */
465 #ifndef _WINDOWS
467 closedir(dirp);
468 #else /* _WINDOWS */
469 } while (_findnext(findrv, &dbuf) == 0);
470 _findclose(findrv);
471 #endif /* ! _WINDOWS */
473 /* Now that we are here, we have all the information in the backup
474 * directory
477 switch (ctype) {
478 case Public: ps_global->smime->backuppubliccertlist = data; break;
479 case Private: ps_global->smime->backupprivatecertlist = data; break;
480 case CACert: ps_global->smime->backupcacertlist = data; break;
481 default: alpine_panic("Bad ctype (n)");
484 } else if(SMHOLDERTYPE(ctype) == Container){
486 } /* else APPLEKEYCHAIN */
487 return rv;
490 char *
491 smime_get_cn(X509 *x)
493 X509_NAME_ENTRY *e;
494 X509_NAME *subject;
495 char buf[256];
496 char *rv = NULL;
498 subject = X509_get_subject_name(x);
499 if((e = X509_NAME_get_entry(subject, X509_NAME_entry_count(subject)-1)) != NULL){
500 X509_NAME_get_text_by_OBJ(subject, X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
501 rv = cpystr(buf);
504 return rv;
508 compare_certs_by_name(const void *data1, const void *data2)
510 int rv, i, j;
511 char *s;
513 CertList *cl1 = *(CertList **) data1;
514 CertList *cl2 = *(CertList **) data2;
516 i = j = -1;
517 if((s = strchr(cl1->name, '@')) != NULL){
518 i = s - cl1->name;
519 *s = '\0';
522 if((s = strchr(cl2->name, '@')) != NULL){
523 j = s - cl2->name;
524 *s = '\0';
527 if((rv = strucmp(cl1->name, cl2->name)) == 0)
528 rv = strucmp(cl1->name + i + 1, cl2->name + j + 1);
529 if(i >= 0) cl1->name[i] = '@';
530 if(j >= 0) cl2->name[j] = '@';
531 return rv;
534 void
535 resort_certificates(CertList **data, WhichCerts ctype)
537 int i, j;
538 CertList *cl = *data;
539 CertList **cll;
540 char *s, *t;
542 if(cl == NULL)
543 return;
545 for(i = 0; cl; cl = cl->next, i++)
546 if(SMHOLDERTYPE(ctype) == Directory && ctype != Private){
547 for(t = s = cl->name; (t = strstr(s, ".crt")) != NULL; s = t+1);
548 if (s) *(s-1) = '\0';
550 j = i;
551 cll = fs_get(i*sizeof(CertList *));
552 for(cl = *data, i = 0; cl; cl = cl->next, i++)
553 cll[i] = cl;
554 qsort((void *)cll, j, sizeof(CertList *), compare_certs_by_name);
555 for(i = 0; i < j - 1; i++){
556 cll[i]->next = cll[i+1];
557 if(SMHOLDERTYPE(ctype) == Directory && ctype != Private)
558 cll[i]->name[strlen(cll[i]->name)]= '.'; /* restore ".crt" part */
560 if(SMHOLDERTYPE(ctype) == Directory && ctype != Private)
561 cll[j-1]->name[strlen(cll[j-1]->name)]= '.'; /* restore ".crt" part */
562 cll[j-1]->next = NULL;
563 *data = cll[0];
567 void
568 get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen, char *s)
570 unsigned char md[128];
571 char *b;
572 unsigned int len, i;
574 len = sizeof(md);
576 X509_digest(cert, type, md, &len);
578 b = buf;
579 *b = 0;
580 for(i=0; i<len; i++){
581 if(b-buf+3>=maxLen)
582 break;
584 if(i != 0 && s && *s)
585 *b++ = *s;
587 snprintf(b, maxLen - (b-buf), "%02x", md[i]);
588 b+=2;
594 * Remove leading whitespace, trailing whitespace and convert
595 * to lowercase. Also remove slash characters
597 * Args: s, -- The string to clean
599 * Result: the cleaned string
601 static char *
602 emailstrclean(char *string)
604 char *s = string, *sc = NULL, *p = NULL;
606 for(; *s; s++){ /* single pass */
607 if(!isspace((unsigned char) (*s))){
608 p = NULL; /* not start of blanks */
609 if(!sc) /* first non-blank? */
610 sc = string; /* start copying */
612 else if(!p) /* it's OK if sc == NULL */
613 p = sc; /* start of blanks? */
615 if(sc && *s!='/' && *s!='\\') /* if copying, copy */
616 *sc++ = isupper((unsigned char) (*s))
617 ? (unsigned char) tolower((unsigned char) (*s))
618 : (unsigned char) (*s);
621 if(p) /* if ending blanks */
622 *p = '\0'; /* tie off beginning */
623 else if(!sc) /* never saw a non-blank */
624 *string = '\0'; /* so tie whole thing off */
626 return(string);
630 char *
631 smime_get_date(const ASN1_TIME *tm)
633 BIO *mb = BIO_new(BIO_s_mem());
634 char iobuf[4096];
635 char date[MAILTMPLEN];
636 char buf[MAILTMPLEN];
637 char *m, *d, *t, *y, *z;
638 int len;
639 struct date smd;
640 struct tm smtm;
642 (void) BIO_reset(mb);
643 if(ASN1_TIME_print(mb, tm) == 0)
644 return cpystr(_("Invalid"));
646 (void) BIO_flush(mb);
647 len = BIO_read(mb, iobuf, sizeof(iobuf));
648 iobuf[len-1] = '\0';
650 /* openssl returns the date in the format:
651 * "MONTH (as name) DAY (as number) TIME(hh:mm:ss) YEAR GMT"
653 m = iobuf;
654 d = strchr(iobuf, ' ');
655 *d++ = '\0';
656 while(*d == ' ') d++;
657 t = strchr(d+1, ' ');
658 *t++ = '\0';
659 while(*t == ' ') t++;
660 y = strchr(t+1, ' ');
661 *y++ = '\0';
662 while(*y == ' ') y++;
663 z = strchr(y+1, ' ');
664 *z++ = '\0';
665 while(*z == ' ') z++;
667 snprintf(date, sizeof(date), "%s %s %s %s (%s)", d, m, y, t, z);
668 date[sizeof(date)-1] = '\0';
669 if(F_ON(F_DATES_TO_LOCAL,ps_global)){
670 parse_date(convert_date_to_local(date), &smd);
671 memset(&smtm, 0, sizeof(smtm));
672 smtm.tm_year = smd.year - 1900;
673 smtm.tm_mon = MIN(MAX(smd.month-1, 0), 11);
674 smtm.tm_mday = MIN(MAX(smd.day, 1), 31);
675 our_strftime(buf, sizeof(buf), "%x", &smtm);
677 else
678 snprintf(buf, sizeof(buf), "%s/%s/%s", m, d, y + strlen(y) - 2);
679 buf[sizeof(buf)-1] = '\0';
681 BIO_free(mb);
682 return cpystr(buf);
686 * Add a lookup for each "*.crt*" file in the given directory.
689 add_certs_in_dir(X509_LOOKUP *lookup, char *path, char *ext, CertList **cdata)
691 char buf[MAXPATH], *fname;
692 #ifndef _WINDOWS
693 struct direct *d;
694 DIR *dirp;
695 #else /* _WINDOWS */
696 struct _finddata_t dbuf;
697 char bufn[_MAX_PATH + 4];
698 long findrv;
699 #endif /* !_WINDOWS */
700 CertList *cert, *cl;
701 int ret = 0, nfiles = 0, nerr = 0;
703 #ifndef _WINDOWS
704 if((dirp = opendir(path)) != NULL){
705 while(!ret && (d=readdir(dirp)) != NULL){
706 fname = d->d_name;
707 #else /* _WINDOWS */
708 snprintf(bufn, sizeof(bufn), "%s%s*.*", path, (path[strlen(path)-1] == '\\') ? "" : "\\");
709 bufn[sizeof(bufn)-1] = '\0';
710 if((findrv = _findfirst(bufn, &dbuf)) >= 0){
712 fname = fname_to_utf8(dbuf.name);
713 #endif /* ! _WINDOWS */
714 if(srchrstr(fname, ext)){
715 nfiles++;
716 build_path(buf, path, fname, sizeof(buf));
718 if(!X509_LOOKUP_load_file(lookup, buf, X509_FILETYPE_PEM)){
719 q_status_message1(SM_ORDER, 3, 3, _("Error loading file %s"), buf);
720 nerr++;
721 } else {
722 if(cdata){
723 BIO *in;
724 X509 *x;
726 cert = fs_get(sizeof(CertList));
727 memset((void *)cert, 0, sizeof(CertList));
728 cert->name = cpystr(fname);
729 /* read buf into a bio and fill the CertData structure */
730 if((in = BIO_new_file(buf, "r"))!=0){
731 if((x = PEM_read_bio_X509(in, NULL, NULL, NULL)) != NULL){
732 cert->data.date_from = smime_get_date(X509_get0_notBefore(x));
733 cert->data.date_to = smime_get_date(X509_get0_notAfter(x));
734 get_fingerprint(x, EVP_md5(), buf, sizeof(buf), NULL);
735 cert->data.md5 = cpystr(buf);
736 cert->cn = smime_get_cn(x);
737 X509_free(x);
739 BIO_free(in);
741 if(*cdata == NULL)
742 *cdata = cert;
743 else{
744 for (cl = *cdata; cl && cl->next; cl = cl->next);
745 cl->next = cert;
752 #ifndef _WINDOWS
754 closedir(dirp);
755 #else /* _WINDOWS */
756 } while(_findnext(findrv, &dbuf) == 0);
757 _findclose(findrv);
758 #endif /* ! _WINDOWS */
761 /* if all certificates fail to load */
762 if(nerr > 0 && nerr == nfiles) ret = -1;
763 return ret;
768 * Get an X509_STORE. This consists of the system
769 * certs directory and any certificates in the user's
770 * ~/.alpine-smime/ca directory.
772 X509_STORE *
773 get_ca_store(void)
775 X509_LOOKUP *lookup;
776 X509_STORE *store = NULL;
778 dprint((9, "get_ca_store()"));
780 if(!(store=X509_STORE_new())){
781 dprint((9, "X509_STORE_new() failed"));
782 return store;
785 if(!(lookup=X509_STORE_add_lookup(store, X509_LOOKUP_file()))){
786 dprint((9, "X509_STORE_add_lookup() failed"));
787 free_x509_store(&store);
788 return NULL;
791 if(ps_global->smime && ps_global->smime->catype == Container
792 && ps_global->smime->cacontent){
794 if(!mem_add_extra_cacerts(ps_global->smime->cacontent, lookup)){
795 free_x509_store(&store);
796 return NULL;
799 else if(ps_global->smime && ps_global->smime->catype == Directory
800 && ps_global->smime->capath){
801 if(add_certs_in_dir(lookup, ps_global->smime->capath, ".crt", &ps_global->smime->cacertlist) < 0){
802 free_x509_store(&store);
803 return NULL;
805 resort_certificates(&ps_global->smime->cacertlist, CACert);
808 if(!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()))){
809 free_x509_store(&store);
810 return NULL;
813 #ifdef SMIME_SSLCERTS
814 dprint((9, "get_ca_store(): adding cacerts from %s", SMIME_SSLCERTS));
815 X509_LOOKUP_add_dir(lookup, SMIME_SSLCERTS, X509_FILETYPE_PEM);
816 #endif
818 return store;
821 void
822 free_x509_store(X509_STORE **xstore)
824 if(xstore == NULL || *xstore == NULL)
825 return;
826 X509_STORE_free(*xstore);
827 *xstore = NULL;
830 EVP_PKEY *
831 load_key(PERSONAL_CERT *pc, char *pass, int flag)
833 BIO *in;
834 EVP_PKEY *key = NULL;
835 char buf[MAXPATH], file[MAXPATH];
837 if(!(ps_global->smime && pc && pc->name))
838 return key;
840 if(ps_global->smime->privatetype == Container){
841 char *q;
843 if(pc->keytext && (q = strstr(pc->keytext, "-----END")) != NULL){
844 while(*q && *q != '\n')
845 q++;
847 if(*q == '\n')
848 q++;
850 if((in = BIO_new_mem_buf(pc->keytext, q-pc->keytext)) != NULL){
851 key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass);
852 BIO_free(in);
856 else if(ps_global->smime->privatetype == Directory){
857 /* filename is path/name.key */
858 strncpy(buf, pc->name, sizeof(buf)-5);
859 buf[sizeof(buf)-5] = '\0';
860 strncat(buf, ".key", 5);
861 build_path(file, ps_global->smime->privatepath, buf, sizeof(file));
863 if(!(in = BIO_new_file(file, "r")))
864 return NULL;
866 key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass);
867 BIO_free(in);
870 return key;
874 #include <openssl/x509v3.h>
876 * This newer version is from Adrian Vogel. It looks for the email
877 * address not only in the email address field, but also in an
878 * X509v3 extension field, Subject Alternative Name.
880 char **
881 get_x509_subject_email(X509 *x)
883 char **result = NULL;
884 int i, n;
885 STACK_OF(OPENSSL_STRING) *emails = X509_get1_email(x);
886 if ((n = sk_OPENSSL_STRING_num(emails)) > 0) {
887 result = fs_get((n+1)*sizeof(char *));
888 for(i = 0; i < n; i++)
889 result[i] = cpystr(sk_OPENSSL_STRING_value(emails, i));
890 result[i] = NULL;
892 X509_email_free(emails);
893 return result;
898 * Save the certificate for the given email address in
899 * ~/.alpine-smime/public.
901 * Should consider the security hazards in making a file with
902 * the email address that has come from the certificate.
904 * The argument email is destroyed.
906 * args: ctype says where the user wants to save the certificate
908 void
909 save_cert_for(char *email, X509 *cert, WhichCerts ctype)
911 if(!ps_global->smime || ctype == Private)
912 return;
914 dprint((9, "save_cert_for(%s, %s)", email ? email : "?", ctype == Public ? _("Public") : ctype == Private ? _("Private") : "CACert"));
915 emailstrclean(email);
917 if(ps_global->smime->publictype == Keychain){
918 #ifdef APPLEKEYCHAIN
920 OSStatus rc;
921 SecCertificateRef secCertificateRef;
922 CSSM_DATA certData;
924 memset((void *) &certData, 0, sizeof(certData));
925 memset((void *) &secCertificateRef, 0, sizeof(secCertificateRef));
927 /* convert OpenSSL X509 cert data to MacOS certData */
928 if((certData.Length = i2d_X509(cert, &(certData.Data))) > 0){
931 * Put that certData into a SecCertificateRef.
932 * Version 3 should work for versions 1-3.
934 if(!(rc=SecCertificateCreateFromData(&certData,
935 CSSM_CERT_X_509v3,
936 CSSM_CERT_ENCODING_DER,
937 &secCertificateRef))){
939 /* add it to the default keychain */
940 if(!(rc=SecCertificateAddToKeychain(secCertificateRef, NULL))){
941 /* ok */
943 else if(rc == errSecDuplicateItem){
944 dprint((9, "save_cert_for: certificate for %s already in keychain", email));
946 else{
947 dprint((9, "SecCertificateAddToKeychain failed"));
950 else{
951 dprint((9, "SecCertificateCreateFromData failed"));
954 else{
955 dprint((9, "i2d_X509 failed"));
958 #endif /* APPLEKEYCHAIN */
960 else if(SMHOLDERTYPE(ctype) == Container){
961 REMDATA_S *rd = NULL;
962 char *ret_dir = NULL;
963 char path[MAXPATH];
964 char fpath[MAXPATH];
965 char *upath = PATHCERTDIR(ctype);
966 char *tempfile = NULL;
967 int err = 0;
968 CertList *clist = DATACERT(ctype);
970 add_to_end_of_certlist(&clist, email, X509_dup(cert));
972 switch(ctype){
973 case Private: ps_global->smime->privatecertlist = clist; break;
974 case Public : ps_global->smime->publiccertlist = clist; break;
975 case CACert : ps_global->smime->cacertlist = clist; break;
976 default: break;
979 if(!upath)
980 return;
982 if(IS_REMOTE(upath)){
983 rd = rd_create_remote(RemImap, upath, REMOTE_SMIME_SUBTYPE,
984 NULL, "Error: ",
985 _("Can't access remote smime configuration."));
986 if(!rd){
987 return;
990 (void) rd_read_metadata(rd);
992 if(rd->access == MaybeRorW){
993 if(rd->read_status == 'R')
994 rd->access = ReadOnly;
995 else
996 rd->access = ReadWrite;
999 if(rd->access != NoExists){
1001 rd_check_remvalid(rd, 1L);
1004 * If the cached info says it is readonly but
1005 * it looks like it's been fixed now, change it to readwrite.
1007 if(rd->read_status == 'R'){
1008 rd_check_readonly_access(rd);
1009 if(rd->read_status == 'W'){
1010 rd->access = ReadWrite;
1011 rd->flags |= REM_OUTOFDATE;
1013 else
1014 rd->access = ReadOnly;
1018 if(rd->flags & REM_OUTOFDATE){
1019 if(rd_update_local(rd) != 0){
1021 dprint((1, "save_cert_for: rd_update_local failed\n"));
1022 rd_close_remdata(&rd);
1023 return;
1026 else
1027 rd_open_remote(rd);
1029 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1030 rd_close_remdata(&rd);
1031 return;
1034 rd->flags |= DO_REMTRIM;
1036 strncpy(path, rd->lf, sizeof(path)-1);
1037 path[sizeof(path)-1] = '\0';
1039 else{
1040 strncpy(path, upath, sizeof(path)-1);
1041 path[sizeof(path)-1] = '\0';
1044 tempfile = tempfile_in_same_dir(path, "az", &ret_dir);
1045 if(tempfile){
1046 if(certlist_to_file(tempfile, DATACERT(ctype)))
1047 err++;
1049 if(!err && ret_dir){
1050 if(IS_REMOTE(upath)){
1051 strncpy(fpath, rd->lf, sizeof(fpath));
1052 fpath[sizeof(fpath)-1] = '\0';
1054 else{
1055 if(strlen(path) + strlen(tempfile) - strlen(ret_dir) + 1 < sizeof(path))
1056 snprintf(fpath, sizeof(fpath), "%s%c%s",
1057 path, tempfile[strlen(ret_dir)], tempfile + strlen(ret_dir) + 1);
1058 else
1059 err++;
1062 else err++;
1064 fs_give((void **)&ret_dir);
1066 if(!err){
1067 if(rename_file(tempfile, fpath) < 0){
1068 q_status_message2(SM_ORDER, 3, 3,
1069 _("Can't rename %s to %s"), tempfile, fpath);
1070 err++;
1074 if(!err && IS_REMOTE(upath)){
1075 int e, we_cancel;
1076 char datebuf[200];
1078 datebuf[0] = '\0';
1080 we_cancel = busy_cue(_("Copying to remote smime container"), NULL, 1);
1081 if((e = rd_update_remote(rd, datebuf)) != 0){
1082 if(e == -1){
1083 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1084 _("Error opening temporary smime file %s: %s"),
1085 rd->lf, error_description(errno));
1086 dprint((1,
1087 "write_remote_smime: error opening temp file %s\n",
1088 rd->lf ? rd->lf : "?"));
1090 else{
1091 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1092 _("Error copying to %s: %s"),
1093 rd->rn, error_description(errno));
1094 dprint((1,
1095 "write_remote_smime: error copying from %s to %s\n",
1096 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1099 q_status_message(SM_ORDER | SM_DING, 5, 5,
1100 _("Copy of smime cert to remote folder failed, changes NOT saved remotely"));
1102 else{
1103 rd_update_metadata(rd, datebuf);
1104 rd->read_status = 'W';
1107 rd_close_remdata(&rd);
1109 if(we_cancel)
1110 cancel_busy_cue(-1);
1113 fs_give((void **) &tempfile);
1116 else if(SMHOLDERTYPE(ctype) == Directory){
1117 char *path = PATHCERTDIR(ctype);
1118 char certfilename[MAXPATH];
1119 BIO *bio_out;
1121 build_path(certfilename, path, email, sizeof(certfilename));
1122 strncat(certfilename, ".crt", sizeof(certfilename)-1-strlen(certfilename));
1123 certfilename[sizeof(certfilename)-1] = 0;
1125 bio_out = BIO_new_file(certfilename, "w");
1126 if(bio_out){
1127 PEM_write_bio_X509(bio_out, cert);
1128 BIO_free(bio_out);
1129 q_status_message1(SM_ORDER, 1, 1, _("Saved certificate for <%s>"), email);
1131 else{
1132 q_status_message1(SM_ORDER, 1, 1, _("Couldn't save certificate for <%s>"), email);
1139 * Try to retrieve the certificate for the given email address.
1140 * The caller should free the cert.
1142 X509 *
1143 get_cert_for(char *email, WhichCerts ctype, int tolower)
1145 char certfilename[MAXPATH];
1146 char emailaddr[MAXPATH];
1147 X509 *cert = NULL;
1148 BIO *in;
1150 if(ctype == Password){
1151 build_path(certfilename, PATHCERTDIR(ctype), email, sizeof(certfilename));
1152 strncat(certfilename, EXTCERT(Public), sizeof(certfilename)-1-strlen(certfilename));
1153 certfilename[sizeof(certfilename)-1] = 0;
1155 if((in = BIO_new_file(certfilename, "r"))!=0){
1157 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
1159 if(cert){
1160 /* could check email addr in cert matches */
1163 BIO_free(in);
1166 return cert;
1169 if(!ps_global->smime)
1170 return cert;
1172 dprint((9, "get_cert_for(%s, %s)", email ? email : "?", "none yet"));
1174 if(ctype == Private) /* there is no private certificate info */
1175 ctype = Public; /* return public information instead */
1176 strncpy(emailaddr, email, sizeof(emailaddr)-1);
1177 emailaddr[sizeof(emailaddr)-1] = 0;
1179 /* clean it up (lowercase, space removal) */
1180 if(tolower)
1181 emailstrclean(emailaddr);
1183 if(ps_global->smime->publictype == Keychain){
1184 #ifdef APPLEKEYCHAIN
1186 OSStatus rc;
1187 SecKeychainItemRef itemRef = nil;
1188 SecKeychainAttributeList attrList;
1189 SecKeychainAttribute attrib;
1190 SecKeychainSearchRef searchRef = nil;
1191 CSSM_DATA certData;
1193 /* low-level form of MacOS data */
1194 memset((void *) &certData, 0, sizeof(certData));
1196 attrList.count = 1;
1197 attrList.attr = &attrib;
1199 /* kSecAlias means email address for a certificate */
1200 attrib.tag = kSecAlias;
1201 attrib.data = emailaddr;
1202 attrib.length = strlen(attrib.data);
1204 /* Find the certificate in the default keychain */
1205 if(!(rc=SecKeychainSearchCreateFromAttributes(NULL,
1206 kSecCertificateItemClass,
1207 &attrList,
1208 &searchRef))){
1210 if(!(rc=SecKeychainSearchCopyNext(searchRef, &itemRef))){
1212 /* extract the data portion of the certificate */
1213 if(!(rc=SecCertificateGetData((SecCertificateRef) itemRef, &certData))){
1216 * Convert it from MacOS form to OpenSSL form.
1217 * The input is certData from above and the output
1218 * is the X509 *cert.
1220 if(!d2i_X509(&cert, &(certData.Data), certData.Length)){
1221 dprint((9, "d2i_X509 failed"));
1224 else{
1225 dprint((9, "SecCertificateGetData failed"));
1228 else if(rc == errSecItemNotFound){
1229 dprint((9, "get_cert_for: Public cert for %s not found", emailaddr));
1231 else{
1232 dprint((9, "SecKeychainSearchCopyNext failed"));
1235 else{
1236 dprint((9, "SecKeychainSearchCreateFromAttributes failed"));
1239 if(searchRef)
1240 CFRelease(searchRef);
1242 #endif /* APPLEKEYCHAIN */
1244 else if(SMHOLDERTYPE(ctype) == Container){
1245 CertList *cl;
1247 for(cl = DATACERT(ctype); cl; cl = cl->next){
1248 if(cl->name && !strucmp(emailaddr, cl->name))
1249 break;
1252 if(cl)
1253 cert = X509_dup((X509 *) cl->x509_cert);
1255 else if(SMHOLDERTYPE(ctype) == Directory){
1256 build_path(certfilename, PATHCERTDIR(ctype), emailaddr, sizeof(certfilename));
1257 strncat(certfilename, EXTCERT(ctype), sizeof(certfilename)-1-strlen(certfilename));
1258 certfilename[sizeof(certfilename)-1] = 0;
1260 if((in = BIO_new_file(certfilename, "r"))!=0){
1262 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
1264 if(cert){
1265 /* could check email addr in cert matches */
1268 BIO_free(in);
1273 return cert;
1277 * load_cert_for_key finds a certificate in pathdir that matches a private key
1278 * pkey. It returns its name in certfile, and the certificate in *pcert.
1279 * return value: success: different from zero, failure 0. If both certfile
1280 * and pcert are NULL, this function returns if there is certificate that
1281 * matches the given key.
1284 load_cert_for_key(char *pathdir, EVP_PKEY *pkey, char **certfile, X509 **pcert)
1286 #ifndef _WINDOWS
1287 DIR *dirp;
1288 struct dirent *d;
1289 #else /* _WINDOWS */
1290 struct _finddata_t dbuf;
1291 char bufn[_MAX_PATH + 4];
1292 long findrv;
1293 #endif /* ! _WINDOWS */
1294 size_t ll;
1295 int rv = 0;
1296 BIO *in;
1297 X509 *x;
1298 char buf[MAXPATH+1], pathcert[MAXPATH+1], *fname;
1300 if(pathdir == NULL || pkey == NULL)
1301 return 0;
1303 if(certfile) *certfile = NULL;
1304 if(pcert) *pcert = NULL;
1306 #ifndef _WINDOWS
1307 if((dirp = opendir(pathdir)) != NULL){
1308 while(rv == 0 && (d=readdir(dirp)) != NULL){
1309 fname = d->d_name;
1310 #else
1311 snprintf(bufn, sizeof(bufn), "%s%s*.*", pathdir, (pathdir[strlen(pathdir)-1] == '\\') ? "" : "\\");
1312 bufn[sizeof(bufn)-1] = '\0';
1313 if((findrv = _findfirst(bufn, &dbuf)) >= 0){
1315 fname = fname_to_utf8(dbuf.name);
1316 #endif /* ! _WINDOWS */
1317 if((ll=strlen(fname)) && ll > 4){
1318 if(!strcmp(fname+ll-4, ".crt")){
1319 strncpy(buf, fname, sizeof(buf));
1320 buf[sizeof(buf)-1] = '\0';
1321 build_path(pathcert, pathdir, buf, sizeof(pathcert));
1322 if((in = BIO_new_file(pathcert, "r")) != NULL){
1323 if((x = PEM_read_bio_X509(in, NULL, NULL, NULL)) != NULL){
1324 if(X509_check_private_key(x, pkey) > 0){
1325 rv = 1;
1326 if(certfile) *certfile = cpystr(buf);
1327 if(pcert) *pcert = x;
1329 else
1330 X509_free(x);
1332 BIO_free(in);
1336 #ifndef _WINDOWS
1338 closedir(dirp);
1339 #else /* _WINDOWS */
1340 } while(_findnext(findrv, &dbuf) == 0);
1341 _findclose(findrv);
1342 #endif
1344 return rv;
1348 PERSONAL_CERT *
1349 mem_to_personal_certs(char *contents)
1351 PERSONAL_CERT *result = NULL;
1352 char *p, *q, *line, *name, *keytext, *save_p;
1353 X509 *cert = NULL;
1355 if(contents && *contents){
1356 for(p = contents; *p != '\0';){
1357 line = p;
1359 while(*p && *p != '\n')
1360 p++;
1362 save_p = NULL;
1363 if(*p == '\n'){
1364 save_p = p;
1365 *p++ = '\0';
1368 if(strncmp(EMAILADDRLEADER, line, strlen(EMAILADDRLEADER)) == 0){
1369 name = line + strlen(EMAILADDRLEADER);
1370 cert = get_cert_for(name, Public, 1);
1371 keytext = p;
1373 /* advance p past this record */
1374 if((q = strstr(keytext, "-----END")) != NULL){
1375 while(*q && *q != '\n')
1376 q++;
1378 if(*q == '\n')
1379 q++;
1381 p = q;
1383 else{
1384 p = p + strlen(p);
1385 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error in privatekey container, missing END"));
1388 if(cert){
1389 PERSONAL_CERT *pc;
1391 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1392 pc->cert = cert;
1393 pc->name = cpystr(name);
1394 pc->cname = NULL;
1395 pc->keytext = keytext; /* a pointer into contents */
1397 pc->key = load_key(pc, "", SM_NORMALCERT);
1399 pc->next = result;
1400 result = pc;
1404 if(save_p)
1405 *save_p = '\n';
1409 return result;
1413 CertList *
1414 mem_to_certlist(char *contents, WhichCerts ctype)
1416 CertList *ret = NULL;
1417 char *p, *q, *line, *name, *certtext, *save_p;
1418 X509 *cert = NULL;
1419 BIO *in;
1420 char *sep = (ctype == Public || ctype == Private)
1421 ? EMAILADDRLEADER : CACERTSTORELEADER;
1423 if(contents && *contents){
1424 for(p = contents; *p != '\0';){
1425 line = p;
1427 while(*p && *p != '\n')
1428 p++;
1430 save_p = NULL;
1431 if(*p == '\n'){
1432 save_p = p;
1433 *p++ = '\0';
1436 if(strncmp(sep, line, strlen(sep)) == 0){
1437 name = line + strlen(sep);
1438 cert = NULL;
1439 certtext = strstr(p, "-----BEGIN");
1440 if(certtext != NULL){
1441 if((q = strstr(certtext, sep)) != NULL)
1442 p = q;
1443 else
1444 p = q = certtext+strlen(certtext);
1446 if((in = BIO_new_mem_buf(certtext, q-certtext)) != 0){
1447 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
1448 BIO_free(in);
1451 else{
1452 q_status_message2(SM_ORDER | SM_DING, 3, 3, _("Error in %scert container, missing BEGIN, certtext=%s"), ctype == Public ? _("public") : _("ca"), p);
1453 p = p + strlen(p);
1456 if(name && cert)
1457 add_to_end_of_certlist(&ret, name, cert);
1460 if(save_p)
1461 *save_p = '\n';
1464 if(ret != NULL)
1465 resort_certificates(&ret, ctype);
1467 return ret;
1472 * Add the CACert Container contents into the CACert store.
1474 * Returns > 0 for success, 0 for failure
1477 mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup)
1479 char *p, *q, *line, *certtext, *save_p;
1480 BIO *in, *out;
1481 int len, failed = 0;
1482 char *tempfile;
1483 char iobuf[4096];
1486 * The most straight-forward way to do this is to write
1487 * the container contents to a temp file and then load the
1488 * contents of the file with X509_LOOKUP_load_file(), like
1489 * is done in add_certs_in_dir(). What we don't know is if
1490 * each file should consist of one cacert or if they can all
1491 * just be jammed together into one file. To be safe, we'll use
1492 * one file per and do each in a separate operation.
1495 if(contents && *contents){
1496 for(p = contents; *p != '\0';){
1497 line = p;
1499 while(*p && *p != '\n')
1500 p++;
1502 save_p = NULL;
1503 if(*p == '\n'){
1504 save_p = p;
1505 *p++ = '\0';
1508 /* look for separator line */
1509 if(strncmp(CACERTSTORELEADER, line, strlen(CACERTSTORELEADER)) == 0){
1510 /* certtext is the content that should go in a file */
1511 certtext = strstr(p, "-----BEGIN");
1512 if(certtext != NULL){
1513 if((q = strstr(certtext, CACERTSTORELEADER)) != NULL){
1514 p = q;
1516 else{ /* end of file */
1517 q = certtext + strlen(certtext);
1518 p = q;
1521 in = BIO_new_mem_buf(certtext, q-certtext);
1522 if(in){
1523 tempfile = temp_nam(NULL, "az");
1524 out = tempfile != NULL ? BIO_new_file(tempfile, "w") : NULL;
1525 if(out != NULL){
1526 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1527 BIO_write(out, iobuf, len);
1529 BIO_free(out);
1530 if(!X509_LOOKUP_load_file(lookup, tempfile, X509_FILETYPE_PEM))
1531 failed++;
1534 if(tempfile != NULL){
1535 unlink(tempfile);
1536 fs_give((void **) &tempfile);
1539 BIO_free(in);
1542 else{
1543 p = p + strlen(p);
1544 q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in cacert container, missing BEGIN, certtext=%s"), certtext);
1547 else{
1548 p = p + strlen(p);
1549 q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in cacert container, missing separator, line=%s"), line);
1552 if(save_p)
1553 *save_p = '\n';
1557 return(!failed);
1562 certlist_to_file(char *filename, CertList *certlist)
1564 CertList *cl;
1565 BIO *bio_out = NULL;
1566 int ret = -1;
1568 if(filename && (bio_out=BIO_new_file(filename, "w")) != NULL){
1569 ret = 0;
1570 for(cl = certlist; cl; cl = cl->next){
1571 if(cl->name && cl->name[0] && cl->x509_cert){
1572 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1573 && (BIO_puts(bio_out, cl->name) > 0)
1574 && (BIO_puts(bio_out, "\n") > 0)))
1575 ret = -1;
1577 if(!PEM_write_bio_X509(bio_out, (X509 *) cl->x509_cert))
1578 ret = -1;
1582 BIO_free(bio_out);
1585 return ret;
1589 void
1590 add_to_end_of_certlist(CertList **cl, char *name, X509 *cert)
1592 CertList *new;
1594 if(!cl)
1595 return;
1597 new = smime_X509_to_cert_info(cert, name);
1598 new->next = *cl;
1599 *cl = new;
1603 void
1604 free_certlist(CertList **cl)
1606 if(cl && *cl){
1607 if((*cl)->data.date_from)
1608 fs_give((void **) &(*cl)->data.date_from);
1610 if((*cl)->data.date_to)
1611 fs_give((void **) &(*cl)->data.date_to);
1613 if((*cl)->data.md5)
1614 fs_give((void **) &(*cl)->data.md5);
1616 if((*cl)->name)
1617 fs_give((void **) &(*cl)->name);
1619 if((*cl)->cn)
1620 fs_give((void **) &(*cl)->cn);
1622 if((*cl)->x509_cert)
1623 X509_free((X509 *) (*cl)->x509_cert);
1625 free_certlist(&(*cl)->next);
1627 fs_give((void **) cl);
1632 void
1633 free_personal_certs(PERSONAL_CERT **pc)
1635 if(pc && *pc){
1636 if((*pc)->name)
1637 fs_give((void **) &(*pc)->name);
1639 if((*pc)->cname)
1640 fs_give((void **) &(*pc)->cname);
1642 if((*pc)->cert)
1643 X509_free((*pc)->cert);
1645 if((*pc)->key)
1646 EVP_PKEY_free((*pc)->key);
1648 free_personal_certs(&(*pc)->next);
1650 fs_give((void **) pc);
1654 #endif /* SMIME */