* clear out some warnings by gcc 9.3.1.
[alpine.git] / pith / smkeys.c
blob760c4b1d011de902031512e74e5a6734aee8f3eb
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-2020 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, "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 EVP_PKEY *tpubkey = X509_REQ_get0_pubkey(req);
245 PEM_write_bio_X509(out, pcert);
246 BIO_flush(out);
247 BIO_free(out);
248 out = NULL;
250 if(req_conf)
251 NCONF_free(req_conf);
253 if(keyfile && certfile && pkey && pcert){
254 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
255 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
256 pc->name = keyfile;
257 pc->key = pkey;
258 pc->cert = pcert;
259 pc->cname = certfile;
261 first_time = 0;
262 return pc;
265 CertList *
266 smime_X509_to_cert_info(X509 *x, char *name)
268 CertList *cert;
269 char buf[MAXPATH+1];
271 if(x == NULL) return NULL;
273 cert = fs_get(sizeof(CertList));
274 memset((void *)cert, 0, sizeof(CertList));
275 cert->x509_cert = x;
276 cert->name = name ? cpystr(name) : NULL;
277 cert->data.date_from = smime_get_date(X509_get0_notBefore(x));
278 cert->data.date_to = smime_get_date(X509_get0_notAfter(x));
279 cert->cn = smime_get_cn(x);
280 get_fingerprint(x, EVP_md5(), buf, sizeof(buf), NULL);
281 cert->data.md5 = cpystr(buf);
283 return cert;
286 #define SMIME_BACKUP_DIR ".backup"
287 #define MAX_TRY_BACKUP 100
289 /* return value: 0 - success, -1 error
290 * Call this function after setting up paths in ps_global->smime
291 * and reading certificates names in certlist.
294 setup_certs_backup_by_type(WhichCerts ctype)
296 int rv = 0; /* assume success */
297 int len;
298 int i, done;
299 char *d, *fname;
300 char p[MAXPATH+1]; /* path to where the backup is */
301 char buf[MAXPATH+1], buf2[MAXPATH+1];
302 struct stat sbuf;
303 CertList *data, *cl;
304 #ifndef _WINDOWS
305 DIR *dirp;
306 struct dirent *df; /* file in the directory */
307 #else /* _WINDOWS */
308 struct _finddata_t dbuf;
309 char bufn[_MAX_PATH + 4];
310 long findrv;
311 #endif /* !_WINDOWS */
312 CertList *cert, *cl2;
313 X509 *x;
314 BIO *in;
316 return rv; /* remove when this function is complete */
318 if(SMHOLDERTYPE(ctype) == Directory){
319 d = PATHCERTDIR(ctype);
320 if(d != NULL){
321 len = strlen(d) + strlen(S_FILESEP) + strlen(SMIME_BACKUP_DIR) + 1;
322 snprintf(p, MAXPATH, "%s%s%s", d, S_FILESEP, SMIME_BACKUP_DIR);
323 p[MAXPATH] = '\0';
324 if(our_stat(p, &sbuf) < 0){
325 if(our_mkpath(p, 0700) != 0)
326 return -1;
327 } else if((sbuf.st_mode & S_IFMT) != S_IFDIR){
328 for(i = 0, done = 0; done == 0 && i < MAX_TRY_BACKUP; i++){
329 snprintf(buf2, len+2, "%s%d", p, i);
330 if(our_stat(buf2, &sbuf) < 0){
331 if(our_mkpath(buf2, 0700) == 0)
332 done++;
334 else if((sbuf.st_mode & S_IFMT) == S_IFDIR)
335 done++;
336 if(done){
337 strncpy(p, buf2, MAXPATH);
338 p[MAXPATH] = '\0';
341 if(done == 0)
342 return -1;
344 /* if we are here, we have a backup directory where to
345 * backup certificates/keys, so now we will go
346 * through the list of certificates and back them up
347 * if we need to.
349 data = BACKUPDATACERT(ctype);
350 for(cl = DATACERT(ctype); cl; cl = cl->next){
351 char clname[MAXPATH+1];
353 snprintf(clname, MAXPATH, "%s%s", cl->name, ctype == Private ? ".key" : "");
354 clname[MAXPATH] = '\0';
355 len = strlen(d) + strlen(clname) + 2;
356 if(len < MAXPATH){
357 snprintf(buf, len, "%s%s%s", d, S_FILESEP, clname);
358 buf[sizeof(buf)-1] = '\0';
359 len = strlen(p) + strlen(clname) + strlen(cl->data.md5) + 3;
360 if(len < MAXPATH){
361 snprintf(buf2, len, "%s%s%s.%s", p, S_FILESEP, clname, cl->data.md5);
362 buf2[sizeof(buf2)-1] = '\0';
363 done = 0; /* recycle done: it means we have a file that may be a certificate*/
364 if(stat(buf2, &sbuf) < 0){
365 if (our_copy(buf2, buf) == 0)
366 done++;
367 } else if((sbuf.st_mode & S_IFMT) == S_IFREG)
368 done++;
370 if(done){
371 switch(ctype){
372 case Public:
373 case CACert:
374 if((in = BIO_new_file(buf2, "r"))!=0){
375 cert = fs_get(sizeof(CertList));
376 memset((void *)cert, 0, sizeof(CertList));
377 cert->x509_cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
378 if(cl->data.date_from != NULL)
379 cert->data.date_from = cpystr(cl->data.date_from);
380 if(cl->data.date_to != NULL)
381 cert->data.date_to = cpystr(cl->data.date_to);
382 if(cl->data.md5 != NULL)
383 cert->data.md5 = cpystr(cl->data.md5);
384 if(cl->cn != NULL)
385 cert->cn = cpystr(cl->cn);
386 snprintf(buf2, len, "%s.%s", cl->name, cl->data.md5);
387 buf2[sizeof(buf2)-1] = '\0';
388 cert->name = cpystr(buf2);
389 if(data == NULL)
390 data = cert;
391 else{
392 for (cl2 = data; cl2 && cl2->next; cl2 = cl2->next);
393 cl2->next = cert;
395 BIO_free(in);
397 break;
399 case Private: break;
400 default: alpine_panic("Bad ctype (0)");
406 /* if we are here, it means we just loaded the backup variable with
407 * a copy of the data that comes from the certlist not coming from
408 * backup. Now we are going to load the contents of the .backup
409 * directory.
412 /* Here is the plan: read the backup directory (in the variable "p")
413 * and attempt to add it. If already there, skip it; otherwise continue
415 #ifndef _WINDOWS
416 if ((dirp = opendir(p)) != NULL) {
417 while ((df = readdir(dirp)) != NULL) {
418 fname = df->d_name;
420 if (fname && *fname == '.') /* no hidden files here */
421 continue;
422 #else
423 snprintf(bufn, sizeof(bufn), "%s%s*.*", p, (p[strlen(p) - 1] == '\\') ? "" : "\\");
424 bufn[sizeof(bufn) - 1] = '\0';
425 if ((findrv = _findfirst(bufn, &dbuf)) >= 0) {
426 do {
427 fname = fname_to_utf8(dbuf.name);
428 #endif /* ! _WINDOWS */
429 /* make sure that we have a file */
430 snprintf(buf2, sizeof(buf2), "%s%s%s", p, S_FILESEP, fname);
431 buf2[sizeof(buf2) - 1] = '\0';
432 if (our_stat(buf2, &sbuf) == 0
433 && (sbuf.st_mode & S_IFMT) != S_IFREG)
434 continue;
436 /* make sure it is not already in the list */
437 for (cl = data; cl; cl = cl->next)
438 if (strcmp(cl->name, fname) == 0)
439 break;
440 if (cl != NULL)
441 continue;
443 /* ok, if it is not in the list, and it is a certificate. Add it */
444 switch (ctype) {
445 case Public:
446 case CACert:
447 if ((in = BIO_new_file(buf2, "r")) != 0) {
448 x = PEM_read_bio_X509(in, NULL, NULL, NULL);
449 if (x) { /* for now copy this information */
450 cert = smime_X509_to_cert_info(x, fname);
451 /* we will use the cert->data.md5 variable to find a backup
452 certificate, not the name */
453 cert->next = data;
454 data = cert;
456 BIO_free(in);
458 break;
460 case Private:
461 /* here we must check it is a key of some cert....*/
462 break;
464 default: alpine_panic("Bad ctype (1)");
465 } /* end switch */
466 #ifndef _WINDOWS
468 closedir(dirp);
469 #else /* _WINDOWS */
470 } while (_findnext(findrv, &dbuf) == 0);
471 _findclose(findrv);
472 #endif /* ! _WINDOWS */
474 /* Now that we are here, we have all the information in the backup
475 * directory
478 switch (ctype) {
479 case Public: ps_global->smime->backuppubliccertlist = data; break;
480 case Private: ps_global->smime->backupprivatecertlist = data; break;
481 case CACert: ps_global->smime->backupcacertlist = data; break;
482 default: alpine_panic("Bad ctype (n)");
485 } else if(SMHOLDERTYPE(ctype) == Container){
487 } /* else APPLEKEYCHAIN */
488 return rv;
491 char *
492 smime_get_cn(X509 *x)
494 X509_NAME_ENTRY *e;
495 X509_NAME *subject;
496 char buf[256];
497 char *rv = NULL;
499 subject = X509_get_subject_name(x);
500 if((e = X509_NAME_get_entry(subject, X509_NAME_entry_count(subject)-1)) != NULL){
501 X509_NAME_get_text_by_OBJ(subject, X509_NAME_ENTRY_get_object(e), buf, sizeof(buf));
502 rv = cpystr(buf);
505 return rv;
509 compare_certs_by_name(const void *data1, const void *data2)
511 int rv, i, j;
512 char *s;
514 CertList *cl1 = *(CertList **) data1;
515 CertList *cl2 = *(CertList **) data2;
517 i = j = -1;
518 if((s = strchr(cl1->name, '@')) != NULL){
519 i = s - cl1->name;
520 *s = '\0';
523 if((s = strchr(cl2->name, '@')) != NULL){
524 j = s - cl2->name;
525 *s = '\0';
528 if((rv = strucmp(cl1->name, cl2->name)) == 0)
529 rv = strucmp(cl1->name + i + 1, cl2->name + j + 1);
530 if(i >= 0) cl1->name[i] = '@';
531 if(j >= 0) cl2->name[j] = '@';
532 return rv;
535 void
536 resort_certificates(CertList **data, WhichCerts ctype)
538 int i, j;
539 CertList *cl = *data;
540 CertList **cll;
541 char *s, *t;
543 if(cl == NULL)
544 return;
546 for(i = 0; cl; cl = cl->next, i++)
547 if(SMHOLDERTYPE(ctype) == Directory && ctype != Private){
548 for(t = s = cl->name; (t = strstr(s, ".crt")) != NULL; s = t+1);
549 if (s) *(s-1) = '\0';
551 j = i;
552 cll = fs_get(i*sizeof(CertList *));
553 for(cl = *data, i = 0; cl; cl = cl->next, i++)
554 cll[i] = cl;
555 qsort((void *)cll, j, sizeof(CertList *), compare_certs_by_name);
556 for(i = 0; i < j - 1; i++){
557 cll[i]->next = cll[i+1];
558 if(SMHOLDERTYPE(ctype) == Directory && ctype != Private)
559 cll[i]->name[strlen(cll[i]->name)]= '.'; /* restore ".crt" part */
561 if(SMHOLDERTYPE(ctype) == Directory && ctype != Private)
562 cll[j-1]->name[strlen(cll[j-1]->name)]= '.'; /* restore ".crt" part */
563 cll[j-1]->next = NULL;
564 *data = cll[0];
568 void
569 get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen, char *s)
571 unsigned char md[128];
572 char *b;
573 unsigned int len, i;
575 len = sizeof(md);
577 X509_digest(cert, type, md, &len);
579 b = buf;
580 *b = 0;
581 for(i=0; i<len; i++){
582 if(b-buf+3>=maxLen)
583 break;
585 if(i != 0 && s && *s)
586 *b++ = *s;
588 snprintf(b, maxLen - (b-buf), "%02x", md[i]);
589 b+=2;
595 * Remove leading whitespace, trailing whitespace and convert
596 * to lowercase. Also remove slash characters
598 * Args: s, -- The string to clean
600 * Result: the cleaned string
602 static char *
603 emailstrclean(char *string)
605 char *s = string, *sc = NULL, *p = NULL;
607 for(; *s; s++){ /* single pass */
608 if(!isspace((unsigned char) (*s))){
609 p = NULL; /* not start of blanks */
610 if(!sc) /* first non-blank? */
611 sc = string; /* start copying */
613 else if(!p) /* it's OK if sc == NULL */
614 p = sc; /* start of blanks? */
616 if(sc && *s!='/' && *s!='\\') /* if copying, copy */
617 *sc++ = isupper((unsigned char) (*s))
618 ? (unsigned char) tolower((unsigned char) (*s))
619 : (unsigned char) (*s);
622 if(p) /* if ending blanks */
623 *p = '\0'; /* tie off beginning */
624 else if(!sc) /* never saw a non-blank */
625 *string = '\0'; /* so tie whole thing off */
627 return(string);
631 char *
632 smime_get_date(const ASN1_TIME *tm)
634 BIO *mb = BIO_new(BIO_s_mem());
635 char iobuf[4096];
636 char date[MAILTMPLEN];
637 char buf[MAILTMPLEN];
638 char *m, *d, *t, *y, *z;
639 int len;
640 struct date smd;
641 struct tm smtm;
643 (void) BIO_reset(mb);
644 if(ASN1_TIME_print(mb, tm) == 0)
645 return cpystr(_("Invalid"));
647 (void) BIO_flush(mb);
648 len = BIO_read(mb, iobuf, sizeof(iobuf));
649 iobuf[len-1] = '\0';
651 /* openssl returns the date in the format:
652 * "MONTH (as name) DAY (as number) TIME(hh:mm:ss) YEAR GMT"
654 m = iobuf;
655 d = strchr(iobuf, ' ');
656 *d++ = '\0';
657 while(*d == ' ') d++;
658 t = strchr(d+1, ' ');
659 *t++ = '\0';
660 while(*t == ' ') t++;
661 y = strchr(t+1, ' ');
662 *y++ = '\0';
663 while(*y == ' ') y++;
664 z = strchr(y+1, ' ');
665 *z++ = '\0';
666 while(*z == ' ') z++;
668 snprintf(date, sizeof(date), "%s %s %s %s (%s)", d, m, y, t, z);
669 date[sizeof(date)-1] = '\0';
670 if(F_ON(F_DATES_TO_LOCAL,ps_global)){
671 parse_date(convert_date_to_local(date), &smd);
672 memset(&smtm, 0, sizeof(smtm));
673 smtm.tm_year = smd.year - 1900;
674 smtm.tm_mon = MIN(MAX(smd.month-1, 0), 11);
675 smtm.tm_mday = MIN(MAX(smd.day, 1), 31);
676 our_strftime(buf, sizeof(buf), "%x", &smtm);
678 else
679 snprintf(buf, sizeof(buf), "%s/%s/%s", m, d, y + strlen(y) - 2);
680 buf[sizeof(buf)-1] = '\0';
682 BIO_free(mb);
683 return cpystr(buf);
687 * Add a lookup for each "*.crt*" file in the given directory.
690 add_certs_in_dir(X509_LOOKUP *lookup, char *path, char *ext, CertList **cdata)
692 char buf[MAXPATH], *fname;
693 #ifndef _WINDOWS
694 struct direct *d;
695 DIR *dirp;
696 #else /* _WINDOWS */
697 struct _finddata_t dbuf;
698 char bufn[_MAX_PATH + 4];
699 long findrv;
700 #endif /* !_WINDOWS */
701 CertList *cert, *cl;
702 int ret = 0, nfiles = 0, nerr = 0;
704 #ifndef _WINDOWS
705 if((dirp = opendir(path)) != NULL){
706 while(!ret && (d=readdir(dirp)) != NULL){
707 fname = d->d_name;
708 #else /* _WINDOWS */
709 snprintf(bufn, sizeof(bufn), "%s%s*.*", path, (path[strlen(path)-1] == '\\') ? "" : "\\");
710 bufn[sizeof(bufn)-1] = '\0';
711 if((findrv = _findfirst(bufn, &dbuf)) >= 0){
713 fname = fname_to_utf8(dbuf.name);
714 #endif /* ! _WINDOWS */
715 if(srchrstr(fname, ext)){
716 nfiles++;
717 build_path(buf, path, fname, sizeof(buf));
719 if(!X509_LOOKUP_load_file(lookup, buf, X509_FILETYPE_PEM)){
720 q_status_message1(SM_ORDER, 3, 3, _("Error loading file %s"), buf);
721 nerr++;
722 } else {
723 if(cdata){
724 BIO *in;
725 X509 *x;
727 cert = fs_get(sizeof(CertList));
728 memset((void *)cert, 0, sizeof(CertList));
729 cert->name = cpystr(fname);
730 /* read buf into a bio and fill the CertData structure */
731 if((in = BIO_new_file(buf, "r"))!=0){
732 if((x = PEM_read_bio_X509(in, NULL, NULL, NULL)) != NULL){
733 cert->data.date_from = smime_get_date(X509_get0_notBefore(x));
734 cert->data.date_to = smime_get_date(X509_get0_notAfter(x));
735 get_fingerprint(x, EVP_md5(), buf, sizeof(buf), NULL);
736 cert->data.md5 = cpystr(buf);
737 cert->cn = smime_get_cn(x);
738 X509_free(x);
740 BIO_free(in);
742 if(*cdata == NULL)
743 *cdata = cert;
744 else{
745 for (cl = *cdata; cl && cl->next; cl = cl->next);
746 cl->next = cert;
753 #ifndef _WINDOWS
755 closedir(dirp);
756 #else /* _WINDOWS */
757 } while(_findnext(findrv, &dbuf) == 0);
758 _findclose(findrv);
759 #endif /* ! _WINDOWS */
762 /* if all certificates fail to load */
763 if(nerr > 0 && nerr == nfiles) ret = -1;
764 return ret;
769 * Get an X509_STORE. This consists of the system
770 * certs directory and any certificates in the user's
771 * ~/.alpine-smime/ca directory.
773 X509_STORE *
774 get_ca_store(void)
776 X509_LOOKUP *lookup;
777 X509_STORE *store = NULL;
779 dprint((9, "get_ca_store()"));
781 if(!(store=X509_STORE_new())){
782 dprint((9, "X509_STORE_new() failed"));
783 return store;
786 if(!(lookup=X509_STORE_add_lookup(store, X509_LOOKUP_file()))){
787 dprint((9, "X509_STORE_add_lookup() failed"));
788 free_x509_store(&store);
789 return NULL;
792 if(ps_global->smime && ps_global->smime->catype == Container
793 && ps_global->smime->cacontent){
795 if(!mem_add_extra_cacerts(ps_global->smime->cacontent, lookup)){
796 free_x509_store(&store);
797 return NULL;
800 else if(ps_global->smime && ps_global->smime->catype == Directory
801 && ps_global->smime->capath){
802 if(add_certs_in_dir(lookup, ps_global->smime->capath, ".crt", &ps_global->smime->cacertlist) < 0){
803 free_x509_store(&store);
804 return NULL;
806 resort_certificates(&ps_global->smime->cacertlist, CACert);
809 if(!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()))){
810 free_x509_store(&store);
811 return NULL;
814 #ifdef SMIME_SSLCERTS
815 dprint((9, "get_ca_store(): adding cacerts from %s", SMIME_SSLCERTS));
816 X509_LOOKUP_add_dir(lookup, SMIME_SSLCERTS, X509_FILETYPE_PEM);
817 #endif
819 return store;
822 void
823 free_x509_store(X509_STORE **xstore)
825 if(xstore == NULL || *xstore == NULL)
826 return;
827 X509_STORE_free(*xstore);
828 *xstore = NULL;
831 EVP_PKEY *
832 load_key(PERSONAL_CERT *pc, char *pass, int flag)
834 BIO *in;
835 EVP_PKEY *key = NULL;
836 char buf[MAXPATH], file[MAXPATH];
838 if(!(ps_global->smime && pc && pc->name))
839 return key;
841 if(ps_global->smime->privatetype == Container){
842 char *q;
844 if(pc->keytext && (q = strstr(pc->keytext, "-----END")) != NULL){
845 while(*q && *q != '\n')
846 q++;
848 if(*q == '\n')
849 q++;
851 if((in = BIO_new_mem_buf(pc->keytext, q-pc->keytext)) != NULL){
852 key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass);
853 BIO_free(in);
857 else if(ps_global->smime->privatetype == Directory){
858 /* filename is path/name.key */
859 strncpy(buf, pc->name, sizeof(buf)-5);
860 buf[sizeof(buf)-5] = '\0';
861 strncat(buf, ".key", 5);
862 build_path(file, ps_global->smime->privatepath, buf, sizeof(file));
864 if(!(in = BIO_new_file(file, "r")))
865 return NULL;
867 key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass);
868 BIO_free(in);
871 return key;
875 #include <openssl/x509v3.h>
877 * This newer version is from Adrian Vogel. It looks for the email
878 * address not only in the email address field, but also in an
879 * X509v3 extension field, Subject Alternative Name.
881 char **
882 get_x509_subject_email(X509 *x)
884 char **result = NULL;
885 int i, n;
886 STACK_OF(OPENSSL_STRING) *emails = X509_get1_email(x);
887 if ((n = sk_OPENSSL_STRING_num(emails)) > 0) {
888 result = fs_get((n+1)*sizeof(char *));
889 for(i = 0; i < n; i++)
890 result[i] = cpystr(sk_OPENSSL_STRING_value(emails, i));
891 result[i] = NULL;
893 X509_email_free(emails);
894 return result;
899 * Save the certificate for the given email address in
900 * ~/.alpine-smime/public.
902 * Should consider the security hazards in making a file with
903 * the email address that has come from the certificate.
905 * The argument email is destroyed.
907 * args: ctype says where the user wants to save the certificate
909 void
910 save_cert_for(char *email, X509 *cert, WhichCerts ctype)
912 if(!ps_global->smime || ctype == Private)
913 return;
915 dprint((9, "save_cert_for(%s, %s)", email ? email : "?", ctype == Public ? _("Public") : ctype == Private ? _("Private") : "CACert"));
916 emailstrclean(email);
918 if(ps_global->smime->publictype == Keychain){
919 #ifdef APPLEKEYCHAIN
921 OSStatus rc;
922 SecCertificateRef secCertificateRef;
923 CSSM_DATA certData;
925 memset((void *) &certData, 0, sizeof(certData));
926 memset((void *) &secCertificateRef, 0, sizeof(secCertificateRef));
928 /* convert OpenSSL X509 cert data to MacOS certData */
929 if((certData.Length = i2d_X509(cert, &(certData.Data))) > 0){
932 * Put that certData into a SecCertificateRef.
933 * Version 3 should work for versions 1-3.
935 if(!(rc=SecCertificateCreateFromData(&certData,
936 CSSM_CERT_X_509v3,
937 CSSM_CERT_ENCODING_DER,
938 &secCertificateRef))){
940 /* add it to the default keychain */
941 if(!(rc=SecCertificateAddToKeychain(secCertificateRef, NULL))){
942 /* ok */
944 else if(rc == errSecDuplicateItem){
945 dprint((9, "save_cert_for: certificate for %s already in keychain", email));
947 else{
948 dprint((9, "SecCertificateAddToKeychain failed"));
951 else{
952 dprint((9, "SecCertificateCreateFromData failed"));
955 else{
956 dprint((9, "i2d_X509 failed"));
959 #endif /* APPLEKEYCHAIN */
961 else if(SMHOLDERTYPE(ctype) == Container){
962 REMDATA_S *rd = NULL;
963 char *ret_dir = NULL;
964 char path[MAXPATH];
965 char fpath[MAXPATH];
966 char *upath = PATHCERTDIR(ctype);
967 char *tempfile = NULL;
968 int err = 0;
969 CertList *clist = DATACERT(ctype);
971 add_to_end_of_certlist(&clist, email, X509_dup(cert));
973 switch(ctype){
974 case Private: ps_global->smime->privatecertlist = clist; break;
975 case Public : ps_global->smime->publiccertlist = clist; break;
976 case CACert : ps_global->smime->cacertlist = clist; break;
977 default: break;
980 if(!upath)
981 return;
983 if(IS_REMOTE(upath)){
984 rd = rd_create_remote(RemImap, upath, REMOTE_SMIME_SUBTYPE,
985 NULL, "Error: ",
986 _("Can't access remote smime configuration."));
987 if(!rd){
988 return;
991 (void) rd_read_metadata(rd);
993 if(rd->access == MaybeRorW){
994 if(rd->read_status == 'R')
995 rd->access = ReadOnly;
996 else
997 rd->access = ReadWrite;
1000 if(rd->access != NoExists){
1002 rd_check_remvalid(rd, 1L);
1005 * If the cached info says it is readonly but
1006 * it looks like it's been fixed now, change it to readwrite.
1008 if(rd->read_status == 'R'){
1009 rd_check_readonly_access(rd);
1010 if(rd->read_status == 'W'){
1011 rd->access = ReadWrite;
1012 rd->flags |= REM_OUTOFDATE;
1014 else
1015 rd->access = ReadOnly;
1019 if(rd->flags & REM_OUTOFDATE){
1020 if(rd_update_local(rd) != 0){
1022 dprint((1, "save_cert_for: rd_update_local failed\n"));
1023 rd_close_remdata(&rd);
1024 return;
1027 else
1028 rd_open_remote(rd);
1030 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1031 rd_close_remdata(&rd);
1032 return;
1035 rd->flags |= DO_REMTRIM;
1037 strncpy(path, rd->lf, sizeof(path)-1);
1038 path[sizeof(path)-1] = '\0';
1040 else{
1041 strncpy(path, upath, sizeof(path)-1);
1042 path[sizeof(path)-1] = '\0';
1045 tempfile = tempfile_in_same_dir(path, "az", &ret_dir);
1046 if(tempfile){
1047 if(certlist_to_file(tempfile, DATACERT(ctype)))
1048 err++;
1050 if(!err && ret_dir){
1051 if(IS_REMOTE(upath)){
1052 strncpy(fpath, rd->lf, sizeof(fpath));
1053 fpath[sizeof(fpath)-1] = '\0';
1055 else{
1056 if(strlen(path) + strlen(tempfile) - strlen(ret_dir) + 1 < sizeof(path))
1057 snprintf(fpath, sizeof(fpath), "%s%c%s",
1058 path, tempfile[strlen(ret_dir)], tempfile + strlen(ret_dir) + 1);
1059 else
1060 err++;
1063 else err++;
1065 fs_give((void **)&ret_dir);
1067 if(!err){
1068 if(rename_file(tempfile, fpath) < 0){
1069 q_status_message2(SM_ORDER, 3, 3,
1070 _("Can't rename %s to %s"), tempfile, fpath);
1071 err++;
1075 if(!err && IS_REMOTE(upath)){
1076 int e, we_cancel;
1077 char datebuf[200];
1079 datebuf[0] = '\0';
1081 we_cancel = busy_cue(_("Copying to remote smime container"), NULL, 1);
1082 if((e = rd_update_remote(rd, datebuf)) != 0){
1083 if(e == -1){
1084 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1085 _("Error opening temporary smime file %s: %s"),
1086 rd->lf, error_description(errno));
1087 dprint((1,
1088 "write_remote_smime: error opening temp file %s\n",
1089 rd->lf ? rd->lf : "?"));
1091 else{
1092 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1093 _("Error copying to %s: %s"),
1094 rd->rn, error_description(errno));
1095 dprint((1,
1096 "write_remote_smime: error copying from %s to %s\n",
1097 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1100 q_status_message(SM_ORDER | SM_DING, 5, 5,
1101 _("Copy of smime cert to remote folder failed, changes NOT saved remotely"));
1103 else{
1104 rd_update_metadata(rd, datebuf);
1105 rd->read_status = 'W';
1108 rd_close_remdata(&rd);
1110 if(we_cancel)
1111 cancel_busy_cue(-1);
1114 fs_give((void **) &tempfile);
1117 else if(SMHOLDERTYPE(ctype) == Directory){
1118 char *path = PATHCERTDIR(ctype);
1119 char certfilename[MAXPATH];
1120 BIO *bio_out;
1122 build_path(certfilename, path, email, sizeof(certfilename));
1123 strncat(certfilename, ".crt", sizeof(certfilename)-1-strlen(certfilename));
1124 certfilename[sizeof(certfilename)-1] = 0;
1126 bio_out = BIO_new_file(certfilename, "w");
1127 if(bio_out){
1128 PEM_write_bio_X509(bio_out, cert);
1129 BIO_free(bio_out);
1130 q_status_message1(SM_ORDER, 1, 1, _("Saved certificate for <%s>"), email);
1132 else{
1133 q_status_message1(SM_ORDER, 1, 1, _("Couldn't save certificate for <%s>"), email);
1140 * Try to retrieve the certificate for the given email address.
1141 * The caller should free the cert.
1143 X509 *
1144 get_cert_for(char *email, WhichCerts ctype, int tolower)
1146 char certfilename[MAXPATH];
1147 char emailaddr[MAXPATH];
1148 X509 *cert = NULL;
1149 BIO *in;
1151 if(ctype == Password){
1152 build_path(certfilename, PATHCERTDIR(ctype), email, sizeof(certfilename));
1153 strncat(certfilename, EXTCERT(Public), sizeof(certfilename)-1-strlen(certfilename));
1154 certfilename[sizeof(certfilename)-1] = 0;
1156 if((in = BIO_new_file(certfilename, "r"))!=0){
1158 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
1160 if(cert){
1161 /* could check email addr in cert matches */
1164 BIO_free(in);
1167 return cert;
1170 if(!ps_global->smime)
1171 return cert;
1173 dprint((9, "get_cert_for(%s, %s)", email ? email : "?", "none yet"));
1175 if(ctype == Private) /* there is no private certificate info */
1176 ctype = Public; /* return public information instead */
1177 strncpy(emailaddr, email, sizeof(emailaddr)-1);
1178 emailaddr[sizeof(emailaddr)-1] = 0;
1180 /* clean it up (lowercase, space removal) */
1181 if(tolower)
1182 emailstrclean(emailaddr);
1184 if(ps_global->smime->publictype == Keychain){
1185 #ifdef APPLEKEYCHAIN
1187 OSStatus rc;
1188 SecKeychainItemRef itemRef = nil;
1189 SecKeychainAttributeList attrList;
1190 SecKeychainAttribute attrib;
1191 SecKeychainSearchRef searchRef = nil;
1192 CSSM_DATA certData;
1194 /* low-level form of MacOS data */
1195 memset((void *) &certData, 0, sizeof(certData));
1197 attrList.count = 1;
1198 attrList.attr = &attrib;
1200 /* kSecAlias means email address for a certificate */
1201 attrib.tag = kSecAlias;
1202 attrib.data = emailaddr;
1203 attrib.length = strlen(attrib.data);
1205 /* Find the certificate in the default keychain */
1206 if(!(rc=SecKeychainSearchCreateFromAttributes(NULL,
1207 kSecCertificateItemClass,
1208 &attrList,
1209 &searchRef))){
1211 if(!(rc=SecKeychainSearchCopyNext(searchRef, &itemRef))){
1213 /* extract the data portion of the certificate */
1214 if(!(rc=SecCertificateGetData((SecCertificateRef) itemRef, &certData))){
1217 * Convert it from MacOS form to OpenSSL form.
1218 * The input is certData from above and the output
1219 * is the X509 *cert.
1221 if(!d2i_X509(&cert, &(certData.Data), certData.Length)){
1222 dprint((9, "d2i_X509 failed"));
1225 else{
1226 dprint((9, "SecCertificateGetData failed"));
1229 else if(rc == errSecItemNotFound){
1230 dprint((9, "get_cert_for: Public cert for %s not found", emailaddr));
1232 else{
1233 dprint((9, "SecKeychainSearchCopyNext failed"));
1236 else{
1237 dprint((9, "SecKeychainSearchCreateFromAttributes failed"));
1240 if(searchRef)
1241 CFRelease(searchRef);
1243 #endif /* APPLEKEYCHAIN */
1245 else if(SMHOLDERTYPE(ctype) == Container){
1246 CertList *cl;
1248 for(cl = DATACERT(ctype); cl; cl = cl->next){
1249 if(cl->name && !strucmp(emailaddr, cl->name))
1250 break;
1253 if(cl)
1254 cert = X509_dup((X509 *) cl->x509_cert);
1256 else if(SMHOLDERTYPE(ctype) == Directory){
1257 build_path(certfilename, PATHCERTDIR(ctype), emailaddr, sizeof(certfilename));
1258 strncat(certfilename, EXTCERT(ctype), sizeof(certfilename)-1-strlen(certfilename));
1259 certfilename[sizeof(certfilename)-1] = 0;
1261 if((in = BIO_new_file(certfilename, "r"))!=0){
1263 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
1265 if(cert){
1266 /* could check email addr in cert matches */
1269 BIO_free(in);
1274 return cert;
1278 * load_cert_for_key finds a certificate in pathdir that matches a private key
1279 * pkey. It returns its name in certfile, and the certificate in *pcert.
1280 * return value: success: different from zero, failure 0. If both certfile
1281 * and pcert are NULL, this function returns if there is certificate that
1282 * matches the given key.
1285 load_cert_for_key(char *pathdir, EVP_PKEY *pkey, char **certfile, X509 **pcert)
1287 #ifndef _WINDOWS
1288 DIR *dirp;
1289 struct dirent *d;
1290 #else /* _WINDOWS */
1291 struct _finddata_t dbuf;
1292 char bufn[_MAX_PATH + 4];
1293 long findrv;
1294 #endif /* ! _WINDOWS */
1295 size_t ll;
1296 int rv = 0;
1297 BIO *in;
1298 X509 *x;
1299 char buf[MAXPATH+1], pathcert[MAXPATH+1], *fname;
1301 if(pathdir == NULL || pkey == NULL)
1302 return 0;
1304 if(certfile) *certfile = NULL;
1305 if(pcert) *pcert = NULL;
1307 #ifndef _WINDOWS
1308 if((dirp = opendir(pathdir)) != NULL){
1309 while(rv == 0 && (d=readdir(dirp)) != NULL){
1310 fname = d->d_name;
1311 #else
1312 snprintf(bufn, sizeof(bufn), "%s%s*.*", pathdir, (pathdir[strlen(pathdir)-1] == '\\') ? "" : "\\");
1313 bufn[sizeof(bufn)-1] = '\0';
1314 if((findrv = _findfirst(bufn, &dbuf)) >= 0){
1316 fname = fname_to_utf8(dbuf.name);
1317 #endif /* ! _WINDOWS */
1318 if((ll=strlen(fname)) && ll > 4){
1319 if(!strcmp(fname+ll-4, ".crt")){
1320 strncpy(buf, fname, sizeof(buf));
1321 buf[sizeof(buf)-1] = '\0';
1322 build_path(pathcert, pathdir, buf, sizeof(pathcert));
1323 if((in = BIO_new_file(pathcert, "r")) != NULL){
1324 if((x = PEM_read_bio_X509(in, NULL, NULL, NULL)) != NULL){
1325 if(X509_check_private_key(x, pkey) > 0){
1326 rv = 1;
1327 if(certfile) *certfile = cpystr(buf);
1328 if(pcert) *pcert = x;
1330 else
1331 X509_free(x);
1333 BIO_free(in);
1337 #ifndef _WINDOWS
1339 closedir(dirp);
1340 #else /* _WINDOWS */
1341 } while(_findnext(findrv, &dbuf) == 0);
1342 _findclose(findrv);
1343 #endif
1345 return rv;
1349 PERSONAL_CERT *
1350 mem_to_personal_certs(char *contents)
1352 PERSONAL_CERT *result = NULL;
1353 char *p, *q, *line, *name, *keytext, *save_p;
1354 X509 *cert = NULL;
1356 if(contents && *contents){
1357 for(p = contents; *p != '\0';){
1358 line = p;
1360 while(*p && *p != '\n')
1361 p++;
1363 save_p = NULL;
1364 if(*p == '\n'){
1365 save_p = p;
1366 *p++ = '\0';
1369 if(strncmp(EMAILADDRLEADER, line, strlen(EMAILADDRLEADER)) == 0){
1370 name = line + strlen(EMAILADDRLEADER);
1371 cert = get_cert_for(name, Public, 1);
1372 keytext = p;
1374 /* advance p past this record */
1375 if((q = strstr(keytext, "-----END")) != NULL){
1376 while(*q && *q != '\n')
1377 q++;
1379 if(*q == '\n')
1380 q++;
1382 p = q;
1384 else{
1385 p = p + strlen(p);
1386 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error in privatekey container, missing END"));
1389 if(cert){
1390 PERSONAL_CERT *pc;
1392 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1393 pc->cert = cert;
1394 pc->name = cpystr(name);
1395 pc->cname = NULL;
1396 pc->keytext = keytext; /* a pointer into contents */
1398 pc->key = load_key(pc, "", SM_NORMALCERT);
1400 pc->next = result;
1401 result = pc;
1405 if(save_p)
1406 *save_p = '\n';
1410 return result;
1414 CertList *
1415 mem_to_certlist(char *contents, WhichCerts ctype)
1417 CertList *ret = NULL;
1418 char *p, *q, *line, *name, *certtext, *save_p;
1419 X509 *cert = NULL;
1420 BIO *in;
1421 char *sep = (ctype == Public || ctype == Private)
1422 ? EMAILADDRLEADER : CACERTSTORELEADER;
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 if(strncmp(sep, line, strlen(sep)) == 0){
1438 name = line + strlen(sep);
1439 cert = NULL;
1440 certtext = strstr(p, "-----BEGIN");
1441 if(certtext != NULL){
1442 if((q = strstr(certtext, sep)) != NULL)
1443 p = q;
1444 else
1445 p = q = certtext+strlen(certtext);
1447 if((in = BIO_new_mem_buf(certtext, q-certtext)) != 0){
1448 cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
1449 BIO_free(in);
1452 else{
1453 q_status_message2(SM_ORDER | SM_DING, 3, 3, _("Error in %scert container, missing BEGIN, certtext=%s"), ctype == Public ? _("public") : _("ca"), p);
1454 p = p + strlen(p);
1457 if(name && cert)
1458 add_to_end_of_certlist(&ret, name, cert);
1461 if(save_p)
1462 *save_p = '\n';
1465 if(ret != NULL)
1466 resort_certificates(&ret, ctype);
1468 return ret;
1473 * Add the CACert Container contents into the CACert store.
1475 * Returns > 0 for success, 0 for failure
1478 mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup)
1480 char *p, *q, *line, *certtext, *save_p;
1481 BIO *in, *out;
1482 int len, failed = 0;
1483 char *tempfile;
1484 char iobuf[4096];
1487 * The most straight-forward way to do this is to write
1488 * the container contents to a temp file and then load the
1489 * contents of the file with X509_LOOKUP_load_file(), like
1490 * is done in add_certs_in_dir(). What we don't know is if
1491 * each file should consist of one cacert or if they can all
1492 * just be jammed together into one file. To be safe, we'll use
1493 * one file per and do each in a separate operation.
1496 if(contents && *contents){
1497 for(p = contents; *p != '\0';){
1498 line = p;
1500 while(*p && *p != '\n')
1501 p++;
1503 save_p = NULL;
1504 if(*p == '\n'){
1505 save_p = p;
1506 *p++ = '\0';
1509 /* look for separator line */
1510 if(strncmp(CACERTSTORELEADER, line, strlen(CACERTSTORELEADER)) == 0){
1511 /* certtext is the content that should go in a file */
1512 certtext = strstr(p, "-----BEGIN");
1513 if(certtext != NULL){
1514 if((q = strstr(certtext, CACERTSTORELEADER)) != NULL){
1515 p = q;
1517 else{ /* end of file */
1518 q = certtext + strlen(certtext);
1519 p = q;
1522 in = BIO_new_mem_buf(certtext, q-certtext);
1523 if(in){
1524 tempfile = temp_nam(NULL, "az");
1525 out = tempfile != NULL ? BIO_new_file(tempfile, "w") : NULL;
1526 if(out != NULL){
1527 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1528 BIO_write(out, iobuf, len);
1530 BIO_free(out);
1531 if(!X509_LOOKUP_load_file(lookup, tempfile, X509_FILETYPE_PEM))
1532 failed++;
1535 if(tempfile != NULL){
1536 unlink(tempfile);
1537 fs_give((void **) &tempfile);
1540 BIO_free(in);
1543 else{
1544 p = p + strlen(p);
1545 q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in cacert container, missing BEGIN, certtext=%s"), certtext);
1548 else{
1549 p = p + strlen(p);
1550 q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in cacert container, missing separator, line=%s"), line);
1553 if(save_p)
1554 *save_p = '\n';
1558 return(!failed);
1563 certlist_to_file(char *filename, CertList *certlist)
1565 CertList *cl;
1566 BIO *bio_out = NULL;
1567 int ret = -1;
1569 if(filename && (bio_out=BIO_new_file(filename, "w")) != NULL){
1570 ret = 0;
1571 for(cl = certlist; cl; cl = cl->next){
1572 if(cl->name && cl->name[0] && cl->x509_cert){
1573 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1574 && (BIO_puts(bio_out, cl->name) > 0)
1575 && (BIO_puts(bio_out, "\n") > 0)))
1576 ret = -1;
1578 if(!PEM_write_bio_X509(bio_out, (X509 *) cl->x509_cert))
1579 ret = -1;
1583 BIO_free(bio_out);
1586 return ret;
1590 void
1591 add_to_end_of_certlist(CertList **cl, char *name, X509 *cert)
1593 CertList *new;
1595 if(!cl)
1596 return;
1598 new = smime_X509_to_cert_info(cert, name);
1599 new->next = *cl;
1600 *cl = new;
1604 void
1605 free_certlist(CertList **cl)
1607 if(cl && *cl){
1608 if((*cl)->data.date_from)
1609 fs_give((void **) &(*cl)->data.date_from);
1611 if((*cl)->data.date_to)
1612 fs_give((void **) &(*cl)->data.date_to);
1614 if((*cl)->data.md5)
1615 fs_give((void **) &(*cl)->data.md5);
1617 if((*cl)->name)
1618 fs_give((void **) &(*cl)->name);
1620 if((*cl)->cn)
1621 fs_give((void **) &(*cl)->cn);
1623 if((*cl)->x509_cert)
1624 X509_free((X509 *) (*cl)->x509_cert);
1626 free_certlist(&(*cl)->next);
1628 fs_give((void **) cl);
1633 void
1634 free_personal_certs(PERSONAL_CERT **pc)
1636 if(pc && *pc){
1637 if((*pc)->name)
1638 fs_give((void **) &(*pc)->name);
1640 if((*pc)->cname)
1641 fs_give((void **) &(*pc)->cname);
1643 if((*pc)->cert)
1644 X509_free((*pc)->cert);
1646 if((*pc)->key)
1647 EVP_PKEY_free((*pc)->key);
1649 free_personal_certs(&(*pc)->next);
1651 fs_give((void **) pc);
1655 #endif /* SMIME */