comparison cinst/nss-installer.c @ 1175:e210ecc32d69

(issue128) Rename mozilla process to trustbridge-nss-installer
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 22 Sep 2014 11:19:43 +0200
parents cinst/mozilla.c@1e429faf7c84
children 12ed0b72e9f5
comparison
equal deleted inserted replaced
1174:7175d117e69a 1175:e210ecc32d69
1 /* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
2 * Software engineering by Intevation GmbH
3 *
4 * This file is Free Software under the GNU GPL (v>=2)
5 * and comes with ABSOLUTELY NO WARRANTY!
6 * See LICENSE.txt for details.
7 */
8 /**
9 * @file
10 * @brief NSS store certificate installation process
11 *
12 * Reads from a file given on command line or stdin a list of
13 * instructions in the form:
14 *
15 * I:\<base64 DER econded certificate\> <BR>
16 * R:\<base64 DER econded certificate\>
17 * ...
18 *
19 * With one instruction per line. the maximum size of an input
20 * line is 9999 characters (including the \\r\\n) at the end of the line.
21 *
22 * Certificates marked with I: will be installed and the ones
23 * marked with R: will be searched and if available removed from
24 * the databases.
25 *
26 * This tool tries to find all NSS databases the user has
27 * access to and to execute the instructions on all of them.
28 *
29 * If the tool is executed with a UID of 0 or with admin privileges under
30 * windows it will not look into the user directories but instead try
31 * to write the system wide defaults.
32 *
33 * If there are other processes accessing the databases the caller
34 * has to ensure that those are terminated before this process is
35 * executed.
36 *
37 * If the same certificate is marked to be installed and to be removed
38 * in one call the behavior is undefined. This should be avoided and
39 * may lead to errors.
40 *
41 * Returns 0 on success (Even when no stores where found) an error value
42 * as defined in errorcodes.h otherwise.
43 *
44 * Success messages are written to stdout. Errors to stderr. For logging
45 * purposes each installation / removal of a certificate will be reported
46 * with the profile name that it modified.
47 *
48 * To get more verbose output add the --debug parameter
49 * as the last parameter on the command line.
50 *
51 */
52
53 /**
54 * @brief Needs to be defined to get strnlen()
55 */
56 #define _POSIX_C_SOURCE 200809L
57
58 /* REMOVEME: */
59 #include <unistd.h>
60
61 #include <cert.h>
62 #include <certdb.h>
63 #include <certt.h>
64 #include <dirent.h>
65 #include <nss.h>
66 #include <pk11pub.h>
67 #include <secerr.h>
68 #include <stdbool.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include <sys/types.h>
73 #include <sys/stat.h>
74
75 #define DEBUGPREFIX "MOZ-"
76 #include "logging.h"
77
78 #include "certhelp.h"
79 #include "errorcodes.h"
80 #include "portpath.h"
81 #include "strhelp.h"
82 #include "nss-secitemlist.h"
83 #include "util.h"
84
85 #ifndef _WIN32
86 #define CONFDIRS ".mozilla", ".thunderbird"
87 /* Default installation directory of ubuntu 14.4 is respected */
88 #define MOZILLA_DEFAULTS "/usr/lib/thunderbird/defaults", "/usr/lib/firefox/browser/defaults"
89 #define MOZILLA_DBNAMES "cert8.db", "key3.db", "secmod.db"
90 #define NSSSHARED ".pki/nssdb"
91 #define NSSSHARED_GLOBAL "/etc/skel/.pki/nssdb"
92 #define TARGET_LINUX 1
93 #define DIRSEP "/"
94 #else
95 #define MOZILLA_DEFAULTS "Mozilla Firefox\\browser\\defaults", "Mozilla Thunderbird\\defaults"
96 #define MOZILLA_DBNAMES NULL
97 #define CONFDIRS "Mozilla", "Thunderbird"
98 #define NSSSHARED ""
99 #define TARGET_LINUX NULL
100 #define DIRSEP "\\"
101 #endif
102
103 /**
104 * @brief Length of string buffers used
105 *
106 * The maximal length of input is defined as 9999 (+ terminating \0).
107 * We use it for other other input puffers besides the IPC input, too.
108 * (One size fits all).
109 */
110 #define LINEBUFLEN 10000
111
112 #ifdef _WIN32
113 #define STRTOK_R strtok_s
114 #else
115 #define STRTOK_R strtok_r
116 #endif
117
118 /**
119 * @brief Global Return Code
120 *
121 * This will be retuned by the programm and might be set to an
122 * error code on fatal errors and to and warning code on non-fatal
123 * errors. In case of mor than one warning the warning codes will be
124 * ORed together.
125 */
126 int exit_code = 0;
127
128 /**
129 * @brief Return configuration base directory.
130 * @returns A pointer to a string containing the path to the base
131 * directory holding the configuration directories for e.g. mozilla
132 * and thunderbird.
133 */
134 static char *
135 get_conf_basedir()
136 {
137 char *cdir, *envvar;
138
139 if (TARGET_LINUX)
140 envvar = "HOME" ;
141 else
142 envvar = "APPDATA";
143
144 if ((cdir = getenv(envvar)) != NULL)
145 return cdir;
146 else
147 {
148 ERRORPRINTF("FATAL! No %s in environment.\n", envvar);
149 exit(ERR_MOZ_HOMELESS);
150 }
151 }
152
153 /**
154 * @brief Get a list of all mozilla profile directories
155 *
156 * Parse the profiles.ini and extract all profile paths from that.
157 * The expected data is in the form:
158 *
159 * [Profile99]<BR>
160 * IsRelative=1<BR>
161 * Path=Example/foo.bar
162 *
163 * or<BR>
164 * [Profile0]<BR>
165 * IsRelative=0<BR>
166 * Path=c:\\foo\\bar\\baz
167 *
168 * Mozilla also accepts the ini file on Windows even if it is UTF-16
169 * encoded but never writes UTF-16 on its own. So currently we ignore
170 * this special case.
171 *
172 * @param[in] inifile_name path of the profile.ini to read.
173 * @return NULL terminated array of strings containing containing the
174 * absolute path of the profile directories. The array needs to
175 * be freed by the caller.
176 */
177 static char **
178 get_profile_dirs (char *inifile_name)
179 {
180 char **dirs = NULL;
181 char *inifile_dirname;
182 FILE *inifile;
183 char line[LINEBUFLEN];
184 char *key;
185 char *value;
186 char *path = NULL;
187 char *fqpath;
188 bool inprofile = false;
189 bool relative_path = false;
190 char *saveptr;
191
192 if ((inifile = fopen(inifile_name, "r")) != NULL)
193 {
194 DEBUGPRINTF("Searching for profile paths in: '%s'\n", inifile_name);
195
196 inifile_dirname = port_dirname(inifile_name);
197 while (fgets(line, LINEBUFLEN, inifile) != NULL)
198 {
199 /* Determine if we are in an profile section */
200 if (str_starts_with(line, "[Profile"))
201 {
202 relative_path = false;
203 inprofile = true;
204 }
205 else if (line[0] == '[')
206 inprofile = false;
207
208 /* If we are in a profile parse path related stuff */
209 if (inprofile)
210 {
211 saveptr = NULL;
212 key = STRTOK_R(line, "=", &saveptr);
213 value = STRTOK_R(NULL, "=", &saveptr);
214 str_trim(&value);
215 if (str_equal(key, "Path"))
216 {
217 if (relative_path)
218 xasprintf(&path, "%s/%s", inifile_dirname, value);
219 else
220 xasprintf(&path, "%s", value);
221 if ((fqpath = port_realpath(path)) != NULL)
222 {
223 DEBUGPRINTF("Found profile path: '%s'\n", fqpath);
224 strv_append(&dirs, fqpath, strlen(fqpath));
225 free (fqpath);
226 }
227 else
228 {
229 DEBUGPRINTF("WARN! Non existent profile path: '%s'\n", path);
230 exit_code |= WARN_MOZ_PROFILE_DOES_NOT_EXIST;
231 }
232 free(path);
233 }
234 else if (str_equal(key, "IsRelative") &&
235 str_starts_with(value, "1"))
236 relative_path = true;
237 }
238 }
239 fclose(inifile);
240 }
241 else
242 {
243 DEBUGPRINTF("WARN! Could not open ini file: '%s'\n", inifile_name);
244 exit_code |= WARN_MOZ_FAILED_TO_OPEN_INI;
245 }
246 return dirs;
247 }
248
249 /**
250 * @brief Search for mozilla profiles.ini files
251 *
252 * Use well known paths and heuristics to find the current users
253 * profiles.ini files on GNU/Linux and Windows systems.
254 *
255 * @return NULL terminated array of strings containing the absolute
256 * path of the profiles.ini files. The array needs to be freed by the
257 * caller.
258 */
259 static char **
260 get_profile_inis ()
261 {
262 char **inis = NULL;
263 char *mozpath, *fqpath, *subpath, *ppath;
264 DIR *mozdir;
265 struct dirent *mozdirent;
266 char *confbase = get_conf_basedir();
267 const char *confdirs[] = { CONFDIRS, NULL };
268
269 for (int i=0; confdirs[i] != NULL; i++)
270 {
271 xasprintf(&mozpath,"%s/%s", confbase, confdirs[i]);
272 if ((mozdir = opendir(mozpath)) != NULL)
273 {
274 while ((mozdirent = readdir(mozdir)) != NULL)
275 {
276 xasprintf(&subpath, "%s/%s/%s",
277 confbase,
278 confdirs[i],
279 mozdirent->d_name);
280 if (port_isdir(subpath)
281 && (strcmp(mozdirent->d_name, "..") != 0))
282 {
283 xasprintf(&ppath, "%s/%s/%s/%s",
284 confbase,
285 confdirs[i],
286 mozdirent->d_name,
287 "profiles.ini");
288 DEBUGPRINTF("checking for %s...\n", ppath);
289 if ((fqpath = port_realpath(ppath)) != NULL)
290 {
291 strv_append(&inis, fqpath, strlen(fqpath));
292 DEBUGPRINTF("Found mozilla ini file: '%s'\n", fqpath);
293 free(fqpath);
294 }
295 free(ppath);
296 }
297 free(subpath);
298 }
299 closedir(mozdir);
300 }
301 else
302 {
303 DEBUGPRINTF("Could not open %s/%s\n", confbase, confdirs[i]);
304 }
305 free(mozpath);
306 }
307 if (inis == NULL)
308 {
309 DEBUGPRINTF("No ini files found - will do nothing!\n");
310 }
311 return inis;
312 }
313
314
315 /** @brief make the default nss databases readable.
316 *
317 * This uses the static paths definied in this code to ensure
318 * that only the defaults are touched.
319 *
320 */
321 #ifndef WIN32
322 static void
323 make_defaults_readable()
324 {
325 const char *confdirs[] = { MOZILLA_DEFAULTS, NULL };
326 const char *filenames[] = { MOZILLA_DBNAMES, NULL };
327
328 mode_t access_mask = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
329
330 for (int i=0; confdirs[i] != NULL; i++)
331 {
332 for (int j=0; filenames[j] != NULL; j++)
333 {
334 char *realpath = NULL,
335 *path = NULL;
336 xasprintf (&path, "%s/profile/%s", confdirs[i], filenames[j]);
337 realpath = port_realpath(path);
338 xfree(path);
339 if (!realpath)
340 {
341 syslog_error_printf("Failed to find %s \n", realpath);
342 continue;
343 }
344 if (chmod(realpath, access_mask))
345 {
346 syslog_error_printf("Failed to set access_mask on file.\n");
347 }
348 xfree (realpath);
349 }
350 }
351 }
352 #endif
353
354 /**
355 * @brief Collect the default profile directories for mozilla software
356 *
357 * If the default directory is found but not the profiles subdirectory
358 * this will create the profiles subdirectory.
359 *
360 * @return NULL terminated array of strings containing the absolute path
361 * to the default profile directories. Needs to be freed by the caller.
362 */
363 static char**
364 get_default_profile_dirs()
365 {
366 char **retval = NULL;
367
368 const char *confdirs[] = { MOZILLA_DEFAULTS, NULL };
369
370 #ifdef _WIN32
371 char *program_files = get_program_files_folder();
372 if (!program_files)
373 {
374 ERRORPRINTF ("Failed to look up program files folder.\n");
375 return NULL;
376 }
377 #endif
378
379 for (int i=0; confdirs[i] != NULL; i++)
380 {
381 char *realpath = NULL,
382 *profile_dir = NULL;
383 #ifndef _WIN32
384 realpath = port_realpath(confdirs[i]);
385 #else
386 /* As on linux we only respect the default installation directory
387 mozilla firefox and thunderbird change their registry key with
388 each version as the key includes the version number. It would
389 be error prone to search the system for every instance. So we
390 only check the default installation directories. */
391 xasprintf(&realpath, "%s" DIRSEP "%s", program_files, confdirs[i]);
392 #endif
393 if (realpath == NULL)
394 {
395 DEBUGPRINTF ("Did not find directory: '%s'\n", confdirs[i]);
396 continue;
397 }
398 xasprintf(&profile_dir, "%s" DIRSEP "profile", realpath);
399 xfree(realpath);
400 if (port_isdir(profile_dir))
401 {
402 DEBUGPRINTF("Found default directory: '%s'\n", profile_dir);
403 /* All is well */
404 strv_append (&retval, profile_dir, strlen(profile_dir));
405 xfree(profile_dir);
406 profile_dir = NULL;
407 continue;
408 }
409 else
410 {
411 /* Create the directory */
412 if (port_fileexits(profile_dir))
413 {
414 DEBUGPRINTF ("Path: '%s' is not a directory but it exists. Skipping.\n",
415 profile_dir);
416 xfree(profile_dir);
417 profile_dir = NULL;
418 continue;
419 }
420 else
421 {
422 /* Lets create it */
423 if (!port_mkdir_p(profile_dir, true))
424 {
425 ERRORPRINTF ("Failed to create directory: '%s'\n", profile_dir);
426 xfree(profile_dir);
427 profile_dir = NULL;
428 continue;
429 }
430 strv_append (&retval, profile_dir, strlen(profile_dir));
431 xfree(profile_dir);
432 profile_dir = NULL;
433 }
434 }
435 }
436 #ifdef WIN32
437 xfree (program_files);
438 #endif
439 return retval;
440 }
441
442 /**
443 * @brief Collect all mozilla profile directories of current user.
444 * @return NULL terminated array of strings containing the absolute
445 * path of the profile directories. The array needs to be freed by the
446 * caller.
447 */
448 static char**
449 get_all_nssdb_dirs()
450 {
451 char **mozinis, **pdirs;
452 char **alldirs = NULL;
453
454 if (is_elevated())
455 {
456 #ifndef _WIN32
457 /* NSS Shared db does not exist under windows. */
458 if (!port_mkdir_p(NSSSHARED_GLOBAL, false))
459 {
460 ERRORPRINTF("Failed to create nssshared skeleton directory. \n");
461 }
462 else
463 {
464 strv_append(&alldirs, "sql:" NSSSHARED_GLOBAL, strlen("sql:" NSSSHARED_GLOBAL));
465 }
466 #endif
467 pdirs = get_default_profile_dirs();
468 if (pdirs != NULL)
469 {
470 for (int i=0; pdirs[i] != NULL; i++)
471 {
472 strv_append(&alldirs, pdirs[i], strlen(pdirs[i]));
473 }
474 strv_free(pdirs);
475 }
476 return alldirs;
477 }
478 /* Search Mozilla/Firefox/Thunderbird profiles */
479 if ((mozinis = get_profile_inis()) != NULL)
480 {
481 for (int i=0; mozinis[i] != NULL; i++)
482 {
483 pdirs =
484 get_profile_dirs(mozinis[i]);
485 if (pdirs != NULL)
486 {
487 for (int i=0; pdirs[i] != NULL; i++)
488 {
489 strv_append(&alldirs, pdirs[i], strlen(pdirs[i]));
490 }
491 strv_free(pdirs);
492 }
493 }
494 strv_free(mozinis);
495 }
496 /* Search for NSS shared DB (used by Chrome/Chromium on GNU/Linux) */
497 if (TARGET_LINUX)
498 {
499 char *path, *fqpath, *sqlpath;
500 xasprintf(&path, "%s/%s", get_conf_basedir(), NSSSHARED);
501 if ((fqpath = port_realpath(path)) != NULL)
502 {
503 xasprintf(&sqlpath, "sql:%s", fqpath);
504 strv_append(&alldirs, sqlpath, strlen(sqlpath));
505 free(sqlpath);
506 free(fqpath);
507 }
508 free(path);
509 }
510 return alldirs;
511 }
512
513 #ifdef DEBUGOUTPUT
514 /**
515 * @brief list certificates from nss certificate store
516 * @param[in] confdir the directory with the certificate store
517 */
518 static void
519 DEBUG_nss_list_certs (char *confdir)
520 {
521 CERTCertList *list;
522 CERTCertListNode *node;
523 char *name;
524
525 if (NSS_Initialize(confdir, "", "", "secmod.db", NSS_INIT_READONLY)
526 == SECSuccess)
527 {
528 DEBUGPRINTF("Listing certs in \"%s\"\n", confdir);
529 list = PK11_ListCerts(PK11CertListAll, NULL);
530 for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list);
531 node = CERT_LIST_NEXT(node))
532 {
533 name = node->appData;
534
535 DEBUGPRINTF("Found certificate \"%s\"\n", name);
536 }
537 /* According to valgrind this leaks memory in the list.
538 We could not find API documentation to better free this
539 so we accept the leakage here in case of debug. */
540 CERT_DestroyCertList(list);
541 NSS_Shutdown();
542 }
543 else
544 {
545 DEBUGPRINTF("Could not open nss certificate store in %s!\n", confdir);
546 }
547 }
548 #endif
549
550 /**
551 * @brief Create a string with the name for cert in SECItem.
552 *
553 * Should be freed by caller.
554 * @param[in] secitemp ponts to an SECItem holding the DER certificate.
555 * @returns a string of the from "CN of Subject - O of Subject"
556 */
557 static char *
558 nss_cert_name(SECItem *secitemp)
559 {
560 char *cn_str, *o_str, *name;
561 size_t name_len;
562 cn_str = x509_parse_subject(secitemp->data, secitemp->len, CERT_OID_CN);
563 o_str = x509_parse_subject(secitemp->data, secitemp->len, CERT_OID_O);
564 if (!cn_str || !o_str)
565 {
566 ERRORPRINTF("FATAL: Could not parse certificate!");
567 exit(ERR_INVALID_CERT);
568 }
569 name_len = strlen(cn_str) + strlen(o_str) + 4;
570 name = (char *)xmalloc(name_len);
571 snprintf(name, name_len, "%s - %s", cn_str, o_str);
572 free(cn_str);
573 free(o_str);
574 return name;
575 }
576
577 /**
578 * @brief Convert a base64 encoded DER certificate to SECItem
579 * @param[in] b64 pointer to the base64 encoded certificate
580 * @param[in] b64len length of the base64 encoded certificate
581 * @param[out] secitem pointer to the SECItem in which to store the
582 * raw DER certifiacte.
583 * @returns true on success and false on failure
584 */
585 static bool
586 base64_to_secitem(char *b64, size_t b64len, SECItem *secitem)
587 {
588 unsigned char *dercert = NULL;
589 size_t dercertlen;
590
591 if ((str_base64_decode((char **)(&dercert), &dercertlen,
592 b64, b64len) == 0) &&
593 (dercertlen > 0))
594 {
595 secitem->data = dercert;
596 secitem->len = (unsigned int) dercertlen;
597 return true;
598 }
599 else
600 {
601 DEBUGPRINTF("Base64 decode failed for: %s\n", b64);
602 }
603 return false;
604 }
605
606 /**
607 * @brief Store DER certificate in mozilla store.
608 * @param[in] pdir the mozilla profile directory with the certificate
609 * store to manipulate.
610 * @param[in] dercert pointer to a SECItem holding the DER certificate
611 * to install
612 * @returns true on success and false on failure
613 */
614 static bool
615 import_cert(char *pdir, SECItem *dercert)
616 {
617 PK11SlotInfo *pk11slot = NULL;
618 CERTCertTrust *trust = NULL;
619 CERTCertificate *cert = NULL;
620 bool success = false;
621 char *cert_name = nss_cert_name(dercert);
622
623 DEBUGPRINTF("INSTALLING cert: '%s' to: %s\n", cert_name, pdir);
624 pk11slot = PK11_GetInternalKeySlot();
625 cert = CERT_DecodeCertFromPackage((char *)dercert->data,
626 (int)dercert->len);
627 trust = (CERTCertTrust *)xmalloc(sizeof(CERTCertTrust));
628 CERT_DecodeTrustString(trust, "C,C,C");
629 if (PK11_ImportCert(pk11slot, cert, CK_INVALID_HANDLE,
630 cert_name, PR_FALSE) == SECSuccess)
631 {
632 if(CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, trust) == SECSuccess)
633 {
634 log_certificate_der (pdir, dercert->data, dercert->len, true);
635 success = true;
636 }
637 }
638 /* This could have happened on either the import cert or
639 the cert change trust. If Import Cert fails with that
640 error the certificate has in fact been added but with
641 random trist bits. See NSS Bug 595861.
642 Reference code can be found in gnome evolution under
643 smime/lib/e-cert-db.c */
644 if(PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN)
645 {
646 if (PK11_NeedUserInit (pk11slot))
647 {
648 PK11_InitPin (pk11slot, "", "");
649 }
650 if (PK11_Authenticate (pk11slot, PR_TRUE, NULL) != SECSuccess)
651 {
652 DEBUGPRINTF("Failed to authenticate.\n");
653 }
654 else if(CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, trust) == SECSuccess)
655 {
656 log_certificate_der (pdir, dercert->data, dercert->len, true);
657 success = true;
658 }
659 }
660
661 if (!success)
662 {
663 DEBUGPRINTF("Failed to install certificate '%s' to '%s'!\n", cert_name, pdir);
664 ERRORPRINTF("Error installing certificate err: %i\n", PORT_GetError());
665 }
666 CERT_DestroyCertificate (cert);
667 free(trust);
668 PK11_FreeSlot(pk11slot);
669
670 free(cert_name);
671 return success;
672 }
673
674 /**
675 * @brief Remove DER certificate from mozilla store.
676 * @param[in] pdir the mozilla profile directory with the certificate
677 * store to manipulate.
678 * @param[in] dercert pointer to a SECItem holding the DER certificate
679 * to remove
680 * @returns true on success and false on failure
681 */
682 static bool
683 remove_cert(char *pdir, SECItem *dercert)
684 {
685 PK11SlotInfo *pk11slot = NULL;
686 bool success = false;
687 char *cert_name = nss_cert_name(dercert);
688 CERTCertificate *cert = NULL;
689
690 DEBUGPRINTF("REMOVING cert: '%s' from: %s\n", cert_name, pdir);
691 if (NSS_Initialize(pdir, "", "", "secmod.db", 0) == SECSuccess)
692 {
693 pk11slot = PK11_GetInternalKeySlot();
694 cert = PK11_FindCertFromDERCertItem(pk11slot,
695 dercert, NULL);
696 if (cert != NULL)
697 {
698 if (SEC_DeletePermCertificate(cert) == SECSuccess)
699 {
700 success = true;
701 log_certificate_der (pdir, dercert->data, dercert->len, false);
702 }
703 else
704 {
705 DEBUGPRINTF("Failed to remove certificate '%s' from '%s'!\n", cert_name, pdir);
706 }
707 CERT_DestroyCertificate(cert);
708 }
709 else
710 {
711 DEBUGPRINTF("Could not find Certificate '%s' in store '%s'.\n", cert_name, pdir);
712 }
713 PK11_FreeSlot(pk11slot);
714 NSS_Shutdown();
715 }
716 else
717 {
718 DEBUGPRINTF("Could not open nss certificate store in %s!\n", pdir);
719 }
720 free(cert_name);
721 return success;
722 }
723
724 /**
725 * @brief Apply a function to a list of certificates and profiles
726 *
727 * The function must have the signature:
728 *
729 * bool function(char *pdir, SECItem der_cert)
730 *
731 * where pdir is the path of an profile and der_cert is an raw DER
732 * formatted certificate. The function must return true on success
733 * and false on failure.
734 *
735 * This function is intended for use with the import_cert and
736 * remove_cert functions.
737 *
738 * @param[in] fn the function to apply
739 * @param[inout] certs a secitem list holding the certificates
740 * the list will be change (emptied)!
741 * @param[in] pdirs the NULL terminated list of profile directories
742 * @returns true on success and false on failure
743 */
744 bool
745 apply_to_certs_and_profiles(bool fn(char *, SECItem *),
746 seciteml_t **certs, char **pdirs)
747 {
748 bool success = true;
749
750 for (int i=0; pdirs[i] != NULL; i++)
751 {
752 seciteml_t *iter = *certs;
753 if (NSS_Initialize(pdirs[i], "", "", "secmod.db", 0) != SECSuccess)
754 {
755 DEBUGPRINTF("Could not open nss certificate store in %s!\n", pdirs[i]);
756 continue;
757 }
758
759 while (iter != NULL && iter->item != NULL)
760 {
761 SECItem *cert = iter->item;
762 if (! (*fn)(pdirs[i], cert))
763 success = false;
764 iter = iter->next;
765 }
766 NSS_Shutdown();
767 }
768
769 seciteml_free(certs);
770
771 return success;
772 }
773
774 /**
775 * @brief Parse IPC commands from standard input.
776 *
777 * Reads command lines (R: and I:) from standard input and puts the
778 * certificates to process in two SECItem lists holding the
779 * certificates in DER format.
780 * @param[inout] stream from standard input
781 * @param[inout] install_list list of SECItems with certifiactes to install
782 * @param[inout] remove_list list of SECItems with certifiactes to remove
783 */
784 static void
785 parse_commands (FILE *stream,
786 seciteml_t **install_list, seciteml_t **remove_list)
787 {
788 char inpl[LINEBUFLEN];
789 size_t inpllen;
790 bool parserr = true;
791 SECItem secitem;
792
793 while ( fgets(inpl, LINEBUFLEN, stream) != NULL )
794 {
795 inpllen = strnlen(inpl, LINEBUFLEN);
796 /* Validate input line:
797 * - must be (much) longer than 3 characters
798 * - must start with "*:"
799 */
800 if ((inpllen > 3) && (inpl[1] == ':'))
801 /* Now parse Input */
802 switch(inpl[0])
803 {
804 case 'R':
805 parserr = true;
806 DEBUGPRINTF("Request to remove certificate: %s\n", &inpl[2]);
807 if (base64_to_secitem(&inpl[2], inpllen - 2, &secitem))
808 {
809 seciteml_push(remove_list, &secitem);
810 parserr = false;
811 }
812 break;
813 case 'I':
814 parserr = true;
815 DEBUGPRINTF("Request to install certificate: %s\n", &inpl[2]);
816 if (base64_to_secitem(&inpl[2], inpllen - 2, &secitem))
817 {
818 seciteml_push(install_list, &secitem);
819 parserr = false;
820 }
821 break;
822 default:
823 parserr = true;
824 }
825 else
826 {
827 parserr = true;
828 }
829
830 if (parserr)
831 {
832 ERRORPRINTF("FATAL: Invalid input: %s\n", inpl);
833 exit(ERR_MOZ_INVALID_INPUT);
834 }
835 }
836 }
837
838 #ifdef DO_RELEASE_BUILD
839 bool g_debug = false;
840 #else
841 bool g_debug = true;
842 #endif
843
844 int
845 main (int argc, char **argv)
846 {
847 char **dbdirs;
848 seciteml_t *certs_to_remove = NULL;
849 seciteml_t *certs_to_add = NULL;
850 FILE *input_stream;
851
852 switch (argc)
853 {
854 case 1:
855 DEBUGPRINTF("Opening STDIN for input...\n");
856 input_stream = stdin;
857 break;
858 case 2:
859 if (strcmp(argv[1], "--debug") == 0)
860 {
861 g_debug = true;
862 DEBUGPRINTF("Opening STDIN for input...\n");
863 input_stream = stdin;
864 break;
865 }
866 case 3:
867 DEBUGPRINTF("Opening %s for input...\n", argv[1]);
868 if ((input_stream = fopen(argv[1], "r")) == NULL)
869 {
870 ERRORPRINTF ("FATAL: Could not open %s for reading!\n",
871 argv[1]);
872 exit_code = ERR_MOZ_FAILED_TO_OPEN_INPUT;
873 goto exit;
874 }
875 if (argc == 3 && strcmp(argv[2], "--debug") == 0)
876 {
877 g_debug = true;
878 }
879 break;
880 default:
881 ERRORPRINTF("FATAL: Wrong number of arguments!\n");
882 exit_code = ERR_MOZ_WRONG_ARGC;
883 goto exit;
884 }
885
886 dbdirs =
887 get_all_nssdb_dirs();
888
889 if (dbdirs != NULL)
890 {
891 parse_commands(input_stream, &certs_to_add, &certs_to_remove);
892
893 #ifdef DEBUGOUTPUT
894 DEBUGPRINTF("OLD List of installed certs:\n");
895 for (int i=0; dbdirs[i] != NULL; i++)
896 DEBUG_nss_list_certs(dbdirs[i]);
897 #endif
898
899 if (! apply_to_certs_and_profiles(remove_cert, &certs_to_remove, dbdirs))
900 exit_code |= WARN_MOZ_COULD_NOT_REMOVE_CERT;
901
902 if (! apply_to_certs_and_profiles(import_cert, &certs_to_add, dbdirs))
903 exit_code |= WARN_MOZ_COULD_NOT_ADD_CERT;
904
905 #ifdef DEBUGOUTPUT
906 DEBUGPRINTF("NEW List of installed certs:\n");
907 for (int i=0; dbdirs[i] != NULL; i++)
908 DEBUG_nss_list_certs(dbdirs[i]);
909 #endif
910
911 #ifndef WIN32
912 if (is_elevated())
913 {
914 make_defaults_readable();
915 }
916 #endif
917
918 strv_free(dbdirs);
919 }
920
921 fclose(input_stream);
922
923 exit:
924 exit(exit_code);
925 }

http://wald.intevation.org/projects/trustbridge/