]> sourceware.org Git - systemtap.git/blob - nsscommon.cxx
step-prep: on debian/ubuntu machines, attempt "apt-get -y install"
[systemtap.git] / nsscommon.cxx
1 /*
2 Common functions used by the NSS-aware code in systemtap.
3
4 Copyright (C) 2009-2018 Red Hat Inc.
5
6 This file is part of systemtap, and is free software. You can
7 redistribute it and/or modify it under the terms of the GNU General Public
8 License as published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "config.h"
21
22 #include <iostream>
23 #include <fstream>
24 #include <sstream>
25 #include <cerrno>
26 #include <cstdio>
27 #include <cassert>
28
29 extern "C" {
30 #include <time.h>
31 #include <termios.h>
32 #include <unistd.h>
33 #include <glob.h>
34 #include <sys/stat.h>
35 #include <sys/utsname.h>
36 #include <nss.h>
37 #include <nspr.h>
38 #include <ssl.h>
39 #include <prerror.h>
40 #include <secerr.h>
41 #include <sslerr.h>
42 #include <cryptohi.h>
43 #include <keyhi.h>
44 #include <secder.h>
45 #include <cert.h>
46 #ifdef HAVE_HTTP_SUPPORT
47 #include <openssl/bio.h>
48 #include <openssl/x509.h>
49 #include <openssl/pem.h>
50 #include <openssl/x509v3.h>
51 #endif
52 }
53
54 #include "nsscommon.h"
55 #include "util.h"
56
57 using namespace std;
58
59 // Common constants and settings.
60 const char *
61 server_cert_nickname ()
62 {
63 return (const char *)"stap-server";
64 }
65
66 string
67 add_cert_db_prefix (const string &db_path) {
68 #if 0 && ((NSS_VMAJOR > 3) || (NSS_VMAJOR == 3 && NSS_VMINOR >= 37))
69 // https://wiki.mozilla.org/NSS_Shared_DB
70 if (db_path.find (':') == string::npos)
71 return string("sql:") + db_path;
72 #elif (NSS_VMAJOR > 3) || (NSS_VMAJOR == 3 && NSS_VMINOR >= 12)
73 // Use the dbm prefix, if a prefix is not already specified,
74 // since we're using the old database format.
75 if (db_path.find (':') == string::npos)
76 return string("dbm:") + db_path;
77 #endif
78 return db_path;
79 }
80
81 string
82 server_cert_db_path ()
83 {
84 string data_path;
85 const char* s_d = getenv ("SYSTEMTAP_DIR");
86 if (s_d != NULL)
87 data_path = s_d;
88 else
89 data_path = get_home_directory() + string("/.systemtap");
90 return data_path + "/ssl/server";
91 }
92
93 string
94 local_client_cert_db_path ()
95 {
96 string data_path;
97 const char* s_d = getenv ("SYSTEMTAP_DIR");
98 if (s_d != NULL)
99 data_path = s_d;
100 else
101 data_path = get_home_directory() + string("/.systemtap");
102 return data_path + "/ssl/client";
103 }
104
105 // Common error handling for applications using this file.
106 void
107 nsscommon_error (const string &msg, int logit)
108 {
109 // Call the extern "C" version supplied by each application.
110 nsscommon_error (msg.c_str (), logit);
111 }
112
113 // Logging. Enabled only by stap-serverd but called from some common methods.
114 static ofstream logfile;
115
116 void
117 start_log (const char *arg, bool redirect_clog)
118 {
119 if (logfile.is_open ())
120 logfile.close ();
121
122 logfile.open (arg, ios_base::app);
123 if (! logfile.good ())
124 nsscommon_error (_F("Could not open log file %s", arg));
125 else if (redirect_clog)
126 clog.rdbuf(logfile.rdbuf());
127 }
128
129 bool
130 log_ok ()
131 {
132 return logfile.good ();
133 }
134
135 void
136 log (const string &msg)
137 {
138 // What time is it?
139 time_t now;
140 time (& now);
141 string nowStr = ctime (& now);
142 // Remove the newline from the end of the time string.
143 nowStr.erase (nowStr.size () - 1, 1);
144
145 if (logfile.good ())
146 logfile << nowStr << ": " << msg << endl << flush;
147 else
148 clog << nowStr << ": " << msg << endl << flush;
149 }
150
151 void
152 end_log ()
153 {
154 if (logfile.is_open ())
155 logfile.close ();
156 }
157
158 // NSS/NSPR error reporting and cleanup.
159 // These functions are called from C code as well as C++, so make them extern "C".
160 extern "C"
161 void
162 nssError (void)
163 {
164 // See if PR_GetError can tell us what the error is.
165 PRErrorCode errorNumber = PR_GetError ();
166
167 // Try PR_ErrorToName and PR_ErrorToString; they are wise.
168 const char* errorName = PR_ErrorToName(errorNumber);
169 const char* errorText = PR_ErrorToString (errorNumber, PR_LANGUAGE_EN);
170
171 if (errorName && errorText)
172 {
173 nsscommon_error (_F("(%d %s) %s", errorNumber, errorName, errorText));
174 return;
175 }
176
177 // Fall back to our own scraped ssl error text.
178 switch (errorNumber) {
179 default: errorText = "Unknown error"; break;
180 #define NSSYERROR(code,msg) case code: errorText = msg; break
181 #include "stapsslerr.h"
182 #undef NSSYERROR
183 }
184 nsscommon_error (_F("(%d) %s", errorNumber, errorText));
185 }
186
187 extern "C"
188 SECStatus
189 nssInit (const char *db_path, int readWrite, int issueMessage)
190 {
191 SECStatus secStatus;
192 string full_db_path = add_cert_db_prefix (db_path);
193 db_path = full_db_path.c_str();
194 if (readWrite)
195 secStatus = NSS_InitReadWrite (db_path);
196 else
197 secStatus = NSS_Init (db_path);
198 if (secStatus != SECSuccess && issueMessage)
199 {
200 nsscommon_error (_F("Error initializing NSS for %s", db_path));
201 nssError ();
202 }
203 return secStatus;
204 }
205
206 extern "C"
207 void
208 nssCleanup (const char *db_path)
209 {
210 // Make sure that NSS has been initialized. Some early versions of NSS do not check this
211 // within NSS_Shutdown().
212 // When called with no certificate database path (db_path == 0), then the caller does
213 // not know whether NSS has actually been initialized. For example, the rpm finder,
214 // which calls here to shutdown NSS manually if rpmFreeCrypto() is not available
215 // (see rpm_finder.cxx:missing_rpm_enlist).
216 // However, if we're trying to close a certificate database which has not been initialized,
217 // then we have a (non fatal) internal error.
218 if (! NSS_IsInitialized ())
219 {
220 if (db_path)
221 {
222 string full_db_path = add_cert_db_prefix (db_path);
223 db_path = full_db_path.c_str();
224 nsscommon_error (_F("WARNING: Attempt to shutdown NSS for database %s, which was never initialized", db_path));
225 }
226 return;
227 }
228
229 // Shutdown NSS and ensure that it went down successfully. This is because we can not
230 // initialize NSS again if it does not.
231 if (NSS_Shutdown () != SECSuccess)
232 {
233 if (db_path)
234 {
235 string full_db_path = add_cert_db_prefix (db_path);
236 db_path = full_db_path.c_str();
237 nsscommon_error (_F("Unable to shutdown NSS for database %s", db_path));
238 }
239 else
240 nsscommon_error (_("Unable to shutdown NSS"));
241 nssError ();
242 }
243 }
244
245 // Certificate database password support functions.
246 //
247 // Disable character echoing, if the fd is a tty.
248 static void
249 echoOff(int fd)
250 {
251 if (isatty(fd)) {
252 struct termios tio;
253 tcgetattr(fd, &tio);
254 tio.c_lflag &= ~ECHO;
255 tcsetattr(fd, TCSAFLUSH, &tio);
256 }
257 }
258
259 /* Enable character echoing, if the fd is a tty. */
260 static void
261 echoOn(int fd)
262 {
263 if (isatty(fd)) {
264 struct termios tio;
265 tcgetattr(fd, &tio);
266 tio.c_lflag |= ECHO;
267 tcsetattr(fd, TCSAFLUSH, &tio);
268 }
269 }
270
271 /*
272 * This function is our custom password handler that is called by
273 * NSS when retrieving private certs and keys from the database. Returns a
274 * pointer to a string with a password for the database. Password pointer
275 * must be allocated by one of the NSPR memory allocation functions, or by PORT_Strdup,
276 * and will be freed by the caller.
277 */
278 extern "C"
279 char *
280 nssPasswordCallback (PK11SlotInfo *info __attribute ((unused)), PRBool retry, void *arg)
281 {
282 static int retries = 0;
283 #define PW_MAX 200
284 char* password = NULL;
285 char* password_ret = NULL;
286 const char *dbname ;
287 int infd;
288 int isTTY;
289
290 if (! retry)
291 {
292 /* Not a retry. */
293 retries = 0;
294 }
295 else
296 {
297 /* Maximum of 2 retries for bad password. */
298 if (++retries > 2)
299 return NULL; /* No more retries */
300 }
301
302 /* Can only prompt for a password if stdin is a tty. */
303 infd = fileno (stdin);
304 isTTY = isatty (infd);
305 if (! isTTY)
306 {
307 nsscommon_error (_("Cannot prompt for certificate database password. stdin is not a tty"));
308 return NULL;
309 }
310
311 /* Prompt for password */
312 password = (char *)PORT_Alloc (PW_MAX);
313 if (! password)
314 {
315 nssError ();
316 return NULL;
317 }
318
319 dbname = (const char *)arg;
320 cerr << _F("Password for certificate database in %s: ", dbname) << flush;
321 echoOff (infd);
322 password_ret = fgets (password, PW_MAX, stdin);
323 cerr << endl << flush;
324 echoOn(infd);
325
326 if (password_ret)
327 /* stomp on the newline */
328 *strchrnul (password, '\n') = '\0';
329 else
330 PORT_Free (password);
331
332 return password_ret;
333 }
334
335 static int
336 create_server_cert_db (const char *db_path)
337 {
338 return create_dir (db_path, 0755);
339 }
340
341 static int
342 create_client_cert_db (const char *db_path)
343 {
344 // Same properties as the server's database, at present.
345 return create_server_cert_db (db_path);
346 }
347
348 static int
349 clean_cert_db (const string &db_path)
350 {
351 // First remove all files from the directory
352 glob_t globbuf;
353 string filespec = db_path + "/*";
354 int r = glob (filespec.c_str (), 0, NULL, & globbuf);
355 if (r == GLOB_NOSPACE || r == GLOB_ABORTED)
356 nsscommon_error (_F("Could not search certificate database directory %s", db_path.c_str ()));
357 else if (r != GLOB_NOMATCH)
358 {
359 for (unsigned i = 0; i < globbuf.gl_pathc; ++i)
360 {
361 if (remove_file_or_dir (globbuf.gl_pathv[i]) != 0)
362 nsscommon_error (_F("Could not remove %s", globbuf.gl_pathv[i]));
363 }
364 globfree(& globbuf);
365 }
366
367 // Now remove the directory itself.
368 if (remove_file_or_dir (db_path.c_str ()) != 0)
369 {
370 nsscommon_error (_F("Could not remove certificate database directory %s\n%s",
371 db_path.c_str (), strerror (errno)));
372 return 1;
373 }
374
375 return 0;
376 }
377
378 static int
379 init_password (PK11SlotInfo *slot, const string &db_path, bool use_password)
380 {
381 // Prompt for the database password, if we're using one. Keep the passwords in memory for as
382 // little time as possible.
383 SECStatus secStatus;
384 if (use_password)
385 {
386 char *pw1 = 0;
387 int attempts;
388 const int max_attempts = 3;
389 for (attempts = 0; attempts < max_attempts; ++attempts)
390 {
391 pw1 = nssPasswordCallback (slot, false, (void*)db_path.c_str ());
392 if (! pw1)
393 continue;
394 cerr << "Confirm ";
395 bool match = false;
396 char *pw2 = nssPasswordCallback (slot, false, (void*)db_path.c_str ());
397 if (pw2)
398 {
399 if (strcmp (pw1, pw2) == 0)
400 match = true;
401 else
402 nsscommon_error (_("Passwords do not match"));
403 memset (pw2, 0, strlen (pw2));
404 PORT_Free (pw2);
405 }
406 if (match)
407 break;
408 memset (pw1, 0, strlen (pw1));
409 PORT_Free (pw1);
410 }
411 if (attempts >= max_attempts)
412 {
413 nsscommon_error (_("Too many password attempts"));
414 return 1;
415 }
416 secStatus = PK11_InitPin (slot, 0, pw1);
417 memset (pw1, 0, strlen (pw1));
418 PORT_Free (pw1);
419 }
420 else
421 secStatus = PK11_InitPin (slot, 0, 0);
422
423 if (secStatus != SECSuccess)
424 {
425 nsscommon_error (_F("Could not initialize pin for certificate database %s", db_path.c_str()));
426 nssError ();
427 return 1;
428 }
429
430 return 0;
431 }
432
433 static SECKEYPrivateKey *
434 generate_private_key (const string &db_path, PK11SlotInfo *slot, SECKEYPublicKey **pubkeyp)
435 {
436 if (PK11_Authenticate (slot, PR_TRUE, 0) != SECSuccess)
437 {
438 nsscommon_error (_F("Unable to authenticate the default slot for certificate database %s",
439 db_path.c_str ()));
440 nssError ();
441 return 0;
442 }
443
444 // Do some random-number initialization.
445 // TODO: We can do better.
446 srand (time (NULL));
447 char randbuf[64];
448 for (unsigned i = 0; i < sizeof (randbuf); ++i)
449 randbuf[i] = rand ();
450 PK11_RandomUpdate (randbuf, sizeof (randbuf));
451 memset (randbuf, 0, sizeof (randbuf));
452
453 // Set up for RSA.
454 PK11RSAGenParams rsaparams;
455 rsaparams.keySizeInBits = 4096; /* 1024 too small; SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED */
456 rsaparams.pe = 0x010001;
457 CK_MECHANISM_TYPE mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
458
459 // Generate the key pair.
460 SECKEYPrivateKey *privKey = PK11_GenerateKeyPair (slot, mechanism, & rsaparams, pubkeyp,
461 PR_TRUE /*isPerm*/, PR_TRUE /*isSensitive*/,
462 0/*pwdata*/);
463 if (! privKey)
464 {
465 nsscommon_error (_("Unable to generate public/private key pair"));
466 nssError ();
467 }
468 return privKey;
469 }
470
471 static CERTCertificateRequest *
472 generate_cert_request (SECKEYPublicKey *pubk, CERTName *subject)
473 {
474 CERTSubjectPublicKeyInfo *spki = SECKEY_CreateSubjectPublicKeyInfo (pubk);
475 if (! spki)
476 {
477 nsscommon_error (_("Unable to create subject public key info for certificate request"));
478 nssError ();
479 return 0;
480 }
481
482 /* Generate certificate request */
483 CERTCertificateRequest *cr = CERT_CreateCertificateRequest (subject, spki, 0);
484 SECKEY_DestroySubjectPublicKeyInfo (spki);
485 if (! cr)
486 {
487 nsscommon_error (_("Unable to create certificate request"));
488 nssError ();
489 }
490 return cr;
491 }
492
493 static CERTCertificate *
494 create_cert (CERTCertificateRequest *certReq, const string &dnsNames)
495 {
496 // What is the current date and time?
497 PRTime now = PR_Now ();
498
499 // What is the date and time 1 year from now?
500 PRExplodedTime printableTime;
501 PR_ExplodeTime (now, PR_GMTParameters, & printableTime);
502 printableTime.tm_month += 12;
503 PRTime after = PR_ImplodeTime (& printableTime);
504
505 // Note that the time is now in micro-second units.
506 CERTValidity *validity = CERT_CreateValidity (now, after);
507 if (! validity)
508 {
509 nsscommon_error (_("Unable to create certificate validity dates"));
510 nssError ();
511 return 0;
512 }
513
514 // Create a default serial number using the current time.
515 PRTime serialNumber = now >> 19; // copied from certutil.
516
517 // Create the certificate.
518 CERTCertificate *cert = CERT_CreateCertificate (serialNumber, & certReq->subject, validity,
519 certReq);
520 CERT_DestroyValidity (validity);
521 if (! cert)
522 {
523 nsscommon_error (_("Unable to create certificate"));
524 nssError ();
525 return 0;
526 }
527
528 // Predeclare these to keep C++ happy about jumps to the label 'error'.
529 SECStatus secStatus = SECSuccess;
530 unsigned char keyUsage = 0x0;
531 PRArenaPool *arena = 0;
532
533 // Add the extensions that we need.
534 void *extHandle = CERT_StartCertExtensions (cert);
535 if (! extHandle)
536 {
537 nsscommon_error (_("Unable to allocate certificate extensions"));
538 nssError ();
539 goto error;
540 }
541
542 // Cert type extension.
543 keyUsage |= (0x80 >> 1); // SSL Server
544 keyUsage |= (0x80 >> 3); // Object signer
545 keyUsage |= (0x80 >> 7); // Object signing CA
546
547 SECItem bitStringValue;
548 bitStringValue.data = & keyUsage;
549 bitStringValue.len = 1;
550
551 secStatus = CERT_EncodeAndAddBitStrExtension (extHandle,
552 SEC_OID_NS_CERT_EXT_CERT_TYPE,
553 & bitStringValue, PR_TRUE);
554 if (secStatus != SECSuccess)
555 {
556 nsscommon_error (_("Unable to encode certificate type extensions"));
557 nssError ();
558 goto error;
559 }
560
561 // Alternate dns name extension.
562 if (! dnsNames.empty ())
563 {
564 arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE);
565 if (! arena)
566 {
567 nsscommon_error (_("Unable to allocate alternate DNS name extension for certificate"));
568 goto error;
569 }
570
571 // Walk down the comma separated list of names.
572 CERTGeneralName *nameList = 0;
573 CERTGeneralName *current = 0;
574 PRCList *prev = 0;
575 vector<string>components;
576 tokenize (dnsNames, components, ",");
577 for (unsigned i = 0; i < components.size (); ++i)
578 {
579 char *tbuf = (char *)PORT_ArenaAlloc (arena, components[i].size () + 1);
580 strcpy (tbuf, components[i].c_str ());
581
582 current = (CERTGeneralName *)PORT_ZAlloc (sizeof (CERTGeneralName));
583 if (! current)
584 {
585 nsscommon_error (_("Unable to allocate alternate DNS name extension for certificate"));
586 goto error;
587 }
588 if (prev)
589 {
590 current->l.prev = prev;
591 prev->next = & current->l;
592 }
593 else
594 nameList = current;
595
596 current->type = certDNSName;
597 current->name.other.data = (unsigned char *)tbuf;
598 current->name.other.len = strlen (tbuf);
599 prev = & current->l;
600 }
601
602 // At this point nameList points to the head of a doubly linked,
603 // but not yet circular, list and current points to its tail.
604 if (nameList)
605 {
606 // Make nameList circular.
607 nameList->l.prev = prev;
608 current->l.next = & nameList->l;
609
610 // Encode and add the extension.
611 SECItem item;
612 secStatus = CERT_EncodeAltNameExtension (arena, nameList, & item);
613 if (secStatus != SECSuccess)
614 {
615 nsscommon_error (_("Unable to encode alternate DNS name extension for certificate"));
616 nssError ();
617 goto error;
618 }
619 secStatus = CERT_AddExtension(extHandle,
620 SEC_OID_X509_SUBJECT_ALT_NAME,
621 & item, PR_FALSE, PR_TRUE);
622 if (secStatus != SECSuccess)
623 {
624 nsscommon_error (_("Unable to add alternate DNS name extension for certificate"));
625 nssError ();
626 goto error;
627 }
628 }
629 } // extra dns names specified.
630
631 // We did not create any extensions on the cert request.
632 assert (certReq->attributes != NULL);
633 assert (certReq->attributes[0] == NULL);
634
635 // Finished with cert extensions.
636 secStatus = CERT_FinishExtensions (extHandle);
637 if (secStatus != SECSuccess)
638 {
639 nsscommon_error (_("Unable to complete alternate DNS name extension for certificate"));
640 nssError ();
641 goto error;
642 }
643
644 return cert;
645
646 error:
647 if (arena)
648 PORT_FreeArena (arena, PR_FALSE);
649 CERT_DestroyCertificate (cert);
650 return 0;
651 }
652
653 static SECItem *
654 sign_cert (CERTCertificate *cert, SECKEYPrivateKey *privKey)
655 {
656 SECOidTag algID = SEC_GetSignatureAlgorithmOidTag (privKey->keyType,
657 SEC_OID_UNKNOWN);
658 if (algID == SEC_OID_UNKNOWN)
659 {
660 nsscommon_error (_("Unable to determine the signature algorithm for the signing the certificate"));
661 nssError ();
662 return 0;
663 }
664
665 PRArenaPool *arena = cert->arena;
666 SECStatus rv = SECOID_SetAlgorithmID (arena, & cert->signature, algID, 0);
667 if (rv != SECSuccess)
668 {
669 nsscommon_error (_("Unable to set the signature algorithm for signing the certificate"));
670 nssError ();
671 return 0;
672 }
673
674 /* we only deal with cert v3 here */
675 *(cert->version.data) = 2;
676 cert->version.len = 1;
677
678 SECItem der;
679 der.len = 0;
680 der.data = 0;
681 void *dummy = SEC_ASN1EncodeItem (arena, & der, cert,
682 SEC_ASN1_GET (CERT_CertificateTemplate));
683 if (! dummy)
684 {
685 nsscommon_error (_("Unable to encode the certificate for signing"));
686 nssError ();
687 return 0;
688 }
689
690 SECItem *result = (SECItem *)PORT_ArenaZAlloc (arena, sizeof (SECItem));
691 if (! result)
692 {
693 nsscommon_error (_("Unable to allocate memory for signing the certificate"));
694 return 0;
695 }
696
697 rv = SEC_DerSignData (arena, result, der.data, der.len, privKey, algID);
698 if (rv != SECSuccess)
699 {
700 nsscommon_error (_("Unable to sign the certificate"));
701 nssError ();
702 return 0;
703 }
704
705 cert->derCert = *result;
706 return result;
707 }
708
709 static SECStatus
710 add_server_cert (const string &db_path, SECItem *certDER, PK11SlotInfo *slot)
711 {
712 // Decode the cert.
713 CERTCertificate *cert = CERT_DecodeCertFromPackage((char *)certDER->data, certDER->len);
714 if (! cert)
715 {
716 nsscommon_error (_("Unable to decode certificate"));
717 nssError ();
718 return SECFailure;
719 }
720
721 // Import it into the database.
722 CERTCertDBHandle *handle = 0;
723 CERTCertTrust *trust = NULL;
724 SECStatus secStatus = PK11_ImportCert (slot, cert, CK_INVALID_HANDLE,
725 server_cert_nickname (), PR_FALSE);
726 if (secStatus != SECSuccess)
727 {
728 nsscommon_error (_F("Unable to import certificate into the database at %s", db_path.c_str ()));
729 nssError ();
730 goto done;
731 }
732
733 // Make it a trusted server and signer.
734 trust = (CERTCertTrust *)PORT_ZAlloc (sizeof (CERTCertTrust));
735 if (! trust)
736 {
737 nsscommon_error (_("Unable to allocate certificate trust"));
738 secStatus = SECFailure;
739 goto done;
740 }
741
742 secStatus = CERT_DecodeTrustString (trust, "PCu,,PCu");
743 if (secStatus != SECSuccess)
744 {
745 nsscommon_error (_("Unable decode trust string 'PCu,,PCu'"));
746 nssError ();
747 goto done;
748 }
749
750 handle = CERT_GetDefaultCertDB ();
751 assert (handle);
752 secStatus = CERT_ChangeCertTrust (handle, cert, trust);
753 if (secStatus != SECSuccess)
754 {
755 nsscommon_error (_("Unable to change certificate trust"));
756 nssError ();
757 }
758
759 done:
760 CERT_DestroyCertificate (cert);
761 if (trust)
762 PORT_Free (trust);
763 return secStatus;
764 }
765
766 SECStatus
767 add_client_cert (const string &inFileName, const string &db_path, bool init_db)
768 {
769 FILE *inFile = fopen (inFileName.c_str (), "rb");
770 if (! inFile)
771 {
772 nsscommon_error (_F("Could not open certificate file %s for reading\n%s",
773 inFileName.c_str (), strerror (errno)));
774 return SECFailure;
775 }
776
777 int fd = fileno (inFile);
778 struct stat info;
779 int rc = fstat (fd, &info);
780 if (rc != 0)
781 {
782 nsscommon_error (_F("Could not obtain information about certificate file %s\n%s",
783 inFileName.c_str (), strerror (errno)));
784 fclose (inFile);
785 return SECFailure;
786 }
787
788 SECItem certDER;
789 certDER.len = info.st_size;
790 certDER.data = (unsigned char *)PORT_Alloc (certDER.len);
791 if (certDER.data == NULL)
792 {
793 nsscommon_error (_F("Could not allocate certDER\n%s",
794 strerror (errno)));
795 fclose (inFile);
796 return SECFailure;
797 }
798 size_t read = fread (certDER.data, 1, certDER.len, inFile);
799 fclose (inFile);
800 if (read != certDER.len)
801 {
802 nsscommon_error (_F("Error reading from certificate file %s\n%s",
803 inFileName.c_str (), strerror (errno)));
804 return SECFailure;
805 }
806
807 SECStatus secStatus;
808 if (init_db)
809 {
810 // The http client adds a cert by calling us via add_server_trust which
811 // first has already called nssInit; we don't want to call nssInit twice
812 // See if the database already exists and can be initialized.
813 secStatus = nssInit (db_path.c_str (), 1/*readwrite*/, 0/*issueMessage*/);
814 if (secStatus != SECSuccess)
815 {
816 // Try again with a fresh database.
817 if (clean_cert_db (db_path.c_str ()) != 0)
818 {
819 // Message already issued.
820 return SECFailure;
821 }
822
823 // Make sure the given path exists.
824 if (create_client_cert_db (db_path.c_str ()) != 0)
825 {
826 nsscommon_error (_F("Could not create certificate database directory %s",
827 db_path.c_str ()));
828 return SECFailure;
829 }
830
831 // Initialize the new database.
832 secStatus = nssInit (db_path.c_str (), 1/*readwrite*/);
833 if (secStatus != SECSuccess)
834 {
835 // Message already issued.
836 return SECFailure;
837 }
838 }
839 }
840
841 // Predeclare these to keep C++ happy about jumps to the label 'done'.
842 CERTCertificate *cert = 0;
843 CERTCertDBHandle *handle = 0;
844 CERTCertTrust *trust = 0;
845 PK11SlotInfo *slot = 0;
846
847 // Add the cert to the database
848 // Decode the cert.
849 secStatus = SECFailure;
850 cert = CERT_DecodeCertFromPackage ((char *)certDER.data, certDER.len);
851 if (! cert)
852 {
853 nsscommon_error (_("Unable to decode certificate"));
854 nssError ();
855 goto done;
856 }
857
858 // We need the internal slot for this database.
859 slot = PK11_GetInternalKeySlot ();
860 if (! slot)
861 {
862 nsscommon_error (_F("Could not obtain internal key slot for certificate database %s", db_path.c_str()));
863 nssError ();
864 goto done;
865 }
866
867 // Import it into the database.
868 secStatus = PK11_ImportCert (slot, cert, CK_INVALID_HANDLE,
869 server_cert_nickname (), PR_FALSE);
870 if (secStatus != SECSuccess)
871 {
872 nsscommon_error (_F("Could not import certificate into the database at %s", db_path.c_str()));
873 nssError ();
874 goto done;
875 }
876
877 // Make it a trusted SSL peer.
878 trust = (CERTCertTrust *)PORT_ZAlloc (sizeof (CERTCertTrust));
879 if (! trust)
880 {
881 nsscommon_error (_("Could not allocate certificate trust"));
882 goto done;
883 }
884
885 secStatus = CERT_DecodeTrustString (trust, "P,P,P");
886 if (secStatus != SECSuccess)
887 {
888 nsscommon_error (_("Unable decode trust string 'P,P,P'"));
889 nssError ();
890 goto done;
891 }
892
893 handle = CERT_GetDefaultCertDB ();
894 assert (handle);
895 secStatus = CERT_ChangeCertTrust (handle, cert, trust);
896 if (secStatus != SECSuccess)
897 {
898 nsscommon_error (_("Unable to change certificate trust"));
899 nssError ();
900 }
901
902 done:
903 // Free NSS/NSPR objects and shutdown NSS.
904 if (slot)
905 PK11_FreeSlot (slot);
906 if (trust)
907 PORT_Free (trust);
908 if (cert)
909 CERT_DestroyCertificate (cert);
910 if (certDER.data)
911 PORT_Free (certDER.data);
912 if (init_db)
913 nssCleanup (db_path.c_str ());
914
915 // Make sure that the cert database files are read/write by the owner and
916 // readable by all.
917 glob_t globbuf;
918 string filespec = db_path + "/*";
919 int r = glob (filespec.c_str (), 0, NULL, & globbuf);
920 if (r == GLOB_NOSPACE || r == GLOB_ABORTED) {
921 // Not fatal, just a warning
922 nsscommon_error (_F("Could not search certificate database directory %s", db_path.c_str ()));
923 }
924 else if (r != GLOB_NOMATCH)
925 {
926 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
927 for (unsigned i = 0; i < globbuf.gl_pathc; ++i)
928 {
929 // Not fatal, just a warning
930 if (chmod (globbuf.gl_pathv[i], mode) != 0)
931 nsscommon_error (_F("Could set file permissions for %s", globbuf.gl_pathv[i]));
932 }
933 globfree(&globbuf);
934 }
935
936 return secStatus;
937 }
938
939 int
940 gen_cert_db (const string &db_path, const string &extraDnsNames, bool use_password)
941 {
942 // Log the generation of a new database.
943 log (_F("Generating a new certificate database directory in %s",
944 db_path.c_str ()));
945
946 // Start with a clean cert database.
947 if (clean_cert_db (db_path.c_str ()) != 0)
948 {
949 // Message already issued.
950 return 1;
951 }
952
953 // Make sure the given path exists.
954 if (create_server_cert_db (db_path.c_str ()) != 0)
955 {
956 nsscommon_error (_F("Could not create certificate database directory %s",
957 db_path.c_str ()));
958 return 1;
959 }
960
961 // Initialize the new database.
962 SECStatus secStatus = nssInit (db_path.c_str (), 1/*readwrite*/);
963 if (secStatus != SECSuccess)
964 {
965 // Message already issued.
966 return 1;
967 }
968
969 // Pre declare these to keep g++ happy about jumps to the label 'error'.
970 CERTName *subject = 0;
971 SECKEYPublicKey *pubkey = 0;
972 SECKEYPrivateKey *privkey = 0;
973 CERTCertificateRequest *cr = 0;
974 CERTCertificate *cert = 0;
975 SECItem *certDER = 0;
976 string dnsNames;
977 string hostname;
978 int rc;
979 string outFileName;
980 FILE *outFile = 0;
981
982 // We need the internal slot for this database.
983 PK11SlotInfo *slot = PK11_GetInternalKeySlot ();
984 if (! slot)
985 {
986 nsscommon_error (_F("Could not obtain internal key slot for certificate database %s", db_path.c_str()));
987 nssError ();
988 goto error;
989 }
990
991 // Establish the password (if any) for the new database.
992 rc = init_password (slot, db_path, use_password);
993 if (rc != 0)
994 {
995 // Messages already issued.
996 goto error;
997 }
998
999 // Format the cert subject.
1000 subject = CERT_AsciiToName ((char *)"CN=Systemtap Compile Server, OU=Systemtap");
1001 if (! subject)
1002 {
1003 nsscommon_error (_("Unable to encode certificate common header"));
1004 nssError ();
1005 goto error;
1006 }
1007
1008 // Next, generate the private key.
1009 privkey = generate_private_key (db_path, slot, & pubkey);
1010 if (! privkey)
1011 {
1012 // Message already issued.
1013 goto error;
1014 }
1015
1016 // Next, generate a cert request.
1017 cr = generate_cert_request (pubkey, subject);
1018 if (! cr)
1019 {
1020 // Message already issued.
1021 goto error;
1022 }
1023
1024 // For the cert, we need our host name.
1025 struct utsname utsname;
1026 uname (& utsname);
1027 dnsNames = utsname.nodename;
1028
1029 // Because avahi identifies hosts using a ".local" domain, add one to the list of names.
1030 hostname = dnsNames.substr (0, dnsNames.find ('.'));
1031 dnsNames += string(",") + hostname + ".local";
1032
1033 // Add any extra names that were supplied.
1034 if (! extraDnsNames.empty ())
1035 dnsNames += "," + extraDnsNames;
1036
1037 // Now, generate the cert.
1038 cert = create_cert (cr, dnsNames);
1039 CERT_DestroyCertificateRequest (cr);
1040 if (! cert)
1041 {
1042 // NSS error already issued.
1043 nsscommon_error (_("Unable to create certificate"));
1044 goto error;
1045 }
1046
1047 // Sign the cert.
1048 certDER = sign_cert (cert, privkey);
1049 if (! certDER)
1050 {
1051 // Message already issued.
1052 goto error;
1053 }
1054
1055 // Now output it to a file.
1056 outFileName = db_path + "/stap.cert";
1057 outFile = fopen (outFileName.c_str (), "wb");
1058 if (outFile)
1059 {
1060 size_t written = fwrite (certDER->data, 1, certDER->len, outFile);
1061 if (written != certDER->len)
1062 {
1063 nsscommon_error (_F("Error writing to certificate file %s\n%s",
1064 outFileName.c_str (), strerror (errno)));
1065 }
1066 fclose (outFile);
1067 }
1068 else
1069 {
1070 nsscommon_error (_F("Could not open certificate file %s for writing\n%s",
1071 outFileName.c_str (), strerror (errno)));
1072 }
1073
1074 // Add the cert to the database
1075 secStatus = add_server_cert (db_path, certDER, slot);
1076 CERT_DestroyCertificate (cert);
1077 if (secStatus != SECSuccess)
1078 {
1079 // NSS error already issued.
1080 nsscommon_error (_F("Unable to add certificate to %s", db_path.c_str ()));
1081 goto error;
1082 }
1083
1084 // Done with the certificate database
1085 PK11_FreeSlot (slot);
1086 CERT_DestroyName (subject);
1087 SECKEY_DestroyPublicKey (pubkey);
1088 SECKEY_DestroyPrivateKey (privkey);
1089 goto done;
1090
1091 error:
1092 if (slot)
1093 PK11_FreeSlot (slot);
1094 if (subject)
1095 CERT_DestroyName (subject);
1096 if (pubkey)
1097 SECKEY_DestroyPublicKey (pubkey);
1098 if (privkey)
1099 SECKEY_DestroyPrivateKey (privkey);
1100 if (cert)
1101 CERT_DestroyCertificate (cert); // Also destroys certDER.
1102
1103 done:
1104 nssCleanup (db_path.c_str ());
1105 return secStatus != SECSuccess;
1106 }
1107
1108 CERTCertList *get_cert_list_from_db (const string &cert_nickname)
1109 {
1110 // Search the client-side database of trusted servers.
1111 CERTCertDBHandle *handle = CERT_GetDefaultCertDB ();
1112 assert (handle);
1113 CERTCertificate *db_cert = PK11_FindCertFromNickname (cert_nickname.c_str (), 0);
1114 if (! db_cert)
1115 {
1116 // No trusted servers. Not an error. Just an empty list returned.
1117 return 0;
1118 }
1119
1120 // Here, we have one cert with the desired nickname.
1121 // Now, we will attempt to get a list of ALL certs
1122 // with the same subject name as the cert we have. That list
1123 // should contain, at a minimum, the one cert we have already found.
1124 // If the list of certs is empty (0), the libraries have failed.
1125 CERTCertList *certs = CERT_CreateSubjectCertList (0, handle, & db_cert->derSubject,
1126 PR_Now (), PR_FALSE);
1127 CERT_DestroyCertificate (db_cert);
1128 if (! certs)
1129 {
1130 nsscommon_error (_("NSS library failure in CERT_CreateSubjectCertList"));
1131 nssError ();
1132 }
1133
1134 return certs;
1135 }
1136
1137 static int
1138 format_cert_validity_time (SECItem &vTime, char *timeString, size_t ts_size)
1139 {
1140 int64 time;
1141 SECStatus secStatus;
1142
1143 switch (vTime.type) {
1144 case siUTCTime:
1145 secStatus = DER_UTCTimeToTime (& time, & vTime);
1146 break;
1147 case siGeneralizedTime:
1148 secStatus = DER_GeneralizedTimeToTime (& time, & vTime);
1149 break;
1150 default:
1151 nsscommon_error (_("Could not decode certificate validity"));
1152 return 1;
1153 }
1154 if (secStatus != SECSuccess)
1155 {
1156 nsscommon_error (_("Could not decode certificate validity time"));
1157 return 1;
1158 }
1159
1160 // Convert to local time.
1161 PRExplodedTime printableTime;
1162 PR_ExplodeTime (time, PR_GMTParameters, & printableTime);
1163 if (! PR_FormatTime (timeString, ts_size, "%a %b %d %H:%M:%S %Y", & printableTime))
1164 {
1165 nsscommon_error (_("Could not format certificate validity time"));
1166 return 1;
1167 }
1168
1169 return 0;
1170 }
1171
1172 static bool
1173 cert_is_valid (CERTCertificate *cert)
1174 {
1175 // Verify the the certificate is valid as an SSL server and as an object signer and that
1176 // it is valid now.
1177 CERTCertDBHandle *handle = CERT_GetDefaultCertDB ();
1178 assert (handle);
1179 SECCertificateUsage usage = certificateUsageSSLServer | certificateUsageObjectSigner;
1180 SECStatus secStatus = CERT_VerifyCertificate (handle, cert, PR_TRUE/*checkSig*/, usage,
1181 PR_Now (), NULL, NULL/*log*/, & usage);
1182 return secStatus == SECSuccess;
1183 }
1184
1185
1186 bool
1187 get_host_name (CERTCertificate *c, string &host_name)
1188 {
1189 // Get the host name. It is in the alt-name extension of the
1190 // certificate.
1191
1192 SECStatus secStatus;
1193 PRArenaPool *tmpArena = NULL;
1194 // A memory pool to work in
1195 tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1196 if (! tmpArena)
1197 {
1198 clog << _("Out of memory:");
1199 return false;
1200 }
1201
1202 SECItem subAltName;
1203 subAltName.data = NULL;
1204 secStatus = CERT_FindCertExtension (c,
1205 SEC_OID_X509_SUBJECT_ALT_NAME,
1206 & subAltName);
1207 if (secStatus != SECSuccess || ! subAltName.data)
1208 {
1209 clog << _("Unable to find alt name extension on server certificate: ") << endl;
1210 PORT_FreeArena (tmpArena, PR_FALSE);
1211 return false;
1212 }
1213
1214 // Decode the extension.
1215 CERTGeneralName *nameList = CERT_DecodeAltNameExtension (tmpArena, & subAltName);
1216 SECITEM_FreeItem(& subAltName, PR_FALSE);
1217 if (! nameList)
1218 {
1219 clog << _("Unable to decode alt name extension on server certificate: ") << endl;
1220 return false;
1221 }
1222
1223 // We're interested in the first alternate name.
1224 assert (nameList->type == certDNSName);
1225 host_name = string ((const char *)nameList->name.other.data,
1226 nameList->name.other.len);
1227 PORT_FreeArena (tmpArena, PR_FALSE);
1228 return true;
1229 }
1230
1231 static bool
1232 cert_db_is_valid (const string &db_path, const string &nss_cert_name,
1233 CERTCertificate *this_cert)
1234 {
1235 // Make sure the given path exists.
1236 if (! file_exists (db_path))
1237 {
1238 log (_F("Certificate database %s does not exist", db_path.c_str ()));
1239 return false;
1240 }
1241
1242 // If a 'pw' file exists, then this is an old database. Treat any certs as invalid.
1243 if (file_exists (db_path + "/pw"))
1244 {
1245 log (_F("Certificate database %s is obsolete", db_path.c_str ()));
1246 return false;
1247 }
1248
1249 // Initialize the NSS libraries -- readonly
1250 SECStatus secStatus = nssInit (db_path.c_str ());
1251 if (secStatus != SECSuccess)
1252 {
1253 // Message already issued.
1254 return false;
1255 }
1256
1257 // Obtain a list of our certs from the database.
1258 bool valid_p = false;
1259 CERTCertList *certs = get_cert_list_from_db (nss_cert_name);
1260 if (! certs)
1261 {
1262 log (_F("No certificate found in database %s", db_path.c_str ()));
1263 goto done;
1264 }
1265
1266 log (_F("Certificate found in database %s", db_path.c_str ()));
1267 for (CERTCertListNode *node = CERT_LIST_HEAD (certs);
1268 ! CERT_LIST_END (node, certs);
1269 node = CERT_LIST_NEXT (node))
1270 {
1271 // The certificate we're working with.
1272 CERTCertificate *c = node->cert;
1273
1274 // Print the validity dates of the certificate.
1275 CERTValidity &v = c->validity;
1276 char timeString[256];
1277 if (format_cert_validity_time (v.notBefore, timeString, sizeof (timeString)) == 0)
1278 log (_F(" Not Valid Before: %s UTC", timeString));
1279 if (format_cert_validity_time (v.notAfter, timeString, sizeof (timeString)) == 0)
1280 log (_F(" Not Valid After: %s UTC", timeString));
1281
1282 // Now ask NSS to check the validity.
1283 if (cert_is_valid (c))
1284 {
1285 // The cert is valid. One valid cert is enough.
1286 log (_("Certificate is valid"));
1287 valid_p = true;
1288 if (this_cert)
1289 *this_cert = *c;
1290 break;
1291 }
1292
1293 // The cert is not valid. Look for another one.
1294 log (_("Certificate is not valid"));
1295 }
1296 CERT_DestroyCertList (certs);
1297
1298 done:
1299 nssCleanup (db_path.c_str ());
1300 return valid_p;
1301 }
1302
1303
1304 #ifdef HAVE_HTTP_SUPPORT
1305 /*
1306 * Similar to cert_db_is_valid; additionally it checks host_name and does no logging
1307 */
1308 static bool
1309 get_pem_cert_is_valid (const string &db_path, const string &nss_cert_name,
1310 const string &host_name, CERTCertificate *this_cert)
1311 {
1312 bool nss_already_init_p = false;
1313 bool found_match = false;
1314
1315 if (! file_exists (db_path))
1316 return false;
1317
1318 if (file_exists (db_path + "/pw"))
1319 return false;
1320
1321 if (NSS_IsInitialized ())
1322 nss_already_init_p = true;
1323
1324 if (nssInit (db_path.c_str ()) != SECSuccess)
1325 return false;
1326
1327 CERTCertList *certs = get_cert_list_from_db (nss_cert_name);
1328 if (! certs)
1329 {
1330 nssCleanup (db_path.c_str ());
1331 return false;
1332 }
1333
1334 for (CERTCertListNode *node = CERT_LIST_HEAD (certs);
1335 ! CERT_LIST_END (node, certs);
1336 node = CERT_LIST_NEXT (node))
1337 {
1338 CERTCertificate *c = node->cert;
1339 // An http client searches for the corresponding server's certificate
1340 if (host_name.length() > 0)
1341 {
1342 string cert_host_name;
1343 if (get_host_name (c, cert_host_name) == false)
1344 continue;
1345
1346 if (cert_host_name != host_name)
1347 {
1348 size_t dot;
1349 if ((dot = host_name.find ('.')) == string::npos
1350 || cert_host_name != host_name.substr(0,dot))
1351 continue;
1352 }
1353 }
1354
1355 // Now ask NSS to check the validity.
1356 if (cert_is_valid (c))
1357 {
1358 if (this_cert)
1359 *this_cert = *c;
1360 found_match = true;
1361 break;
1362 }
1363 }
1364 CERT_DestroyCertList (certs);
1365
1366 // NSS shutdown is global and is forceful
1367 if (!nss_already_init_p)
1368 nssCleanup (db_path.c_str ());
1369 return found_match;
1370 }
1371
1372
1373 bool
1374 cvt_nss_to_pem (CERTCertificate *c, string &cert_pem)
1375 {
1376 BIO *bio;
1377 X509 *certificate_509;
1378
1379 bio = BIO_new(BIO_s_mem());
1380 certificate_509 = X509_new();
1381 // Load the nss certificate into the openssl BIO
1382 int count = BIO_write (bio, (const void*)c->derCert.data, c->derCert.len);
1383 if (count == 0)
1384 return false;
1385 // Parse the BIO into an X509
1386 certificate_509 = d2i_X509_bio(bio, &certificate_509);
1387 // Convert the X509 to PEM form
1388 int rc = PEM_write_bio_X509(bio, certificate_509);
1389 if (rc != 1)
1390 return false;
1391 BUF_MEM *mem = NULL;
1392 // Load the buffer from the PEM form
1393 BIO_get_mem_ptr(bio, &mem);
1394 cert_pem = string(mem->data, mem->length);
1395 BIO_free(bio);
1396 X509_free(certificate_509);
1397 return true;
1398 }
1399
1400
1401 bool
1402 get_pem_cert (const string &db_path, const string &nss_cert_name, const string &host,
1403 string &cert)
1404 {
1405 CERTCertificate cc;
1406 CERTCertificate *c = &cc;
1407 // Do we have an nss certificate in the nss cert db for server host?
1408 if (get_pem_cert_is_valid (db_path, nss_cert_name, host, c))
1409 {
1410 // If we do, then convert to PEM form
1411 cvt_nss_to_pem (c, cert);
1412 return true;
1413 }
1414 return false;
1415 }
1416
1417 bool
1418 have_san_match (string & hostname, string & pem_cert)
1419 {
1420 bool have_match = false;
1421 STACK_OF (GENERAL_NAME) * san_names = NULL;
1422
1423 BIO *bio = BIO_new(BIO_s_mem());
1424 BIO_puts(bio, pem_cert.c_str());
1425 X509 *server_cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
1426
1427 // Extract the source alternate names from the certificate
1428 san_names =
1429 (stack_st_GENERAL_NAME *) X509_get_ext_d2i ((X509 *) server_cert,
1430 NID_subject_alt_name, NULL,
1431 NULL);
1432 if (san_names == NULL)
1433 {
1434 return false;
1435 }
1436
1437 for (int i = 0; i < sk_GENERAL_NAME_num (san_names); i++)
1438 {
1439 const GENERAL_NAME *current_name = sk_GENERAL_NAME_value (san_names, i);
1440
1441 if (current_name->type == GEN_DNS)
1442 {
1443 const int scheme_len = 8; // https://
1444 string dns_name =
1445 #if OPENSSL_VERSION_NUMBER<0x10100000L
1446 string ((char *) ASN1_STRING_data (current_name->d.dNSName));
1447 #else
1448 string ((char *) ASN1_STRING_get0_data (current_name->d.dNSName));
1449 #endif
1450
1451 if ((size_t) ASN1_STRING_length (current_name->d.dNSName) ==
1452 dns_name.length ())
1453 {
1454 if (hostname.compare(scheme_len,dns_name.length(), dns_name))
1455 {
1456 have_match = true;
1457 break;
1458 }
1459 }
1460 }
1461 }
1462 sk_GENERAL_NAME_pop_free (san_names, GENERAL_NAME_free);
1463 BIO_free(bio);
1464 X509_free(server_cert);
1465
1466 return have_match;
1467 }
1468 #endif
1469
1470
1471 // Ensure that our certificate exists and is valid. Generate a new one if not.
1472 int
1473 check_cert (const string &db_path, const string &nss_cert_name, bool use_db_password)
1474 {
1475 // Generate a new cert database if the current one does not exist or is not valid.
1476 if (! cert_db_is_valid (db_path, nss_cert_name, NULL))
1477 {
1478 if (gen_cert_db (db_path, "", use_db_password) != 0)
1479 {
1480 // NSS message already issued.
1481 nsscommon_error (_("Unable to generate new certificate"));
1482 return 1;
1483 }
1484 }
1485 return 0;
1486 }
1487
1488 void sign_file (
1489 const string &db_path,
1490 const string &nss_cert_name,
1491 const string &inputName,
1492 const string &outputName
1493 ) {
1494 /* Get own certificate and private key. */
1495 CERTCertificate *cert = PK11_FindCertFromNickname (nss_cert_name.c_str (), NULL);
1496 if (cert == NULL)
1497 {
1498 nsscommon_error (_F("Unable to find certificate with nickname %s in %s.",
1499 nss_cert_name.c_str (), db_path.c_str()));
1500 nssError ();
1501 return;
1502 }
1503
1504 // Predeclare these to keep C++ happy abount branches to 'done'.
1505 unsigned char buffer[4096];
1506 PRFileDesc *local_file_fd = NULL;
1507 PRInt32 numBytes;
1508 SECStatus secStatus;
1509 SGNContext *sgn;
1510 SECItem signedData;
1511
1512 /* db_path.c_str () gets passed to nssPasswordCallback */
1513 SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert (cert, (void *)db_path.c_str ());
1514 if (privKey == NULL)
1515 {
1516 nsscommon_error (_F("Unable to obtain private key from the certificate with nickname %s in %s.",
1517 nss_cert_name.c_str (), db_path.c_str()));
1518 nssError ();
1519 goto done;
1520 }
1521
1522 /* Sign the file. */
1523 /* Set up the signing context. */
1524 sgn = SGN_NewContext (SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, privKey);
1525 if (! sgn)
1526 {
1527 nsscommon_error (_("Could not create signing context"));
1528 nssError ();
1529 return;
1530 }
1531 secStatus = SGN_Begin (sgn);
1532 if (secStatus != SECSuccess)
1533 {
1534 nsscommon_error (_("Could not initialize signing context."));
1535 nssError ();
1536 return;
1537 }
1538
1539 /* Now read the data and add it to the signature. */
1540 local_file_fd = PR_Open (inputName.c_str(), PR_RDONLY, 0);
1541 if (local_file_fd == NULL)
1542 {
1543 nsscommon_error (_F("Could not open module file %s", inputName.c_str ()));
1544 nssError ();
1545 return;
1546 }
1547
1548 for (;;)
1549 {
1550 // No need for PR_Read_Complete here, since we're already managing multiple
1551 // reads to a fixed size buffer.
1552 numBytes = PR_Read (local_file_fd, buffer, sizeof (buffer));
1553 if (numBytes == 0)
1554 break; /* EOF */
1555
1556 if (numBytes < 0)
1557 {
1558 nsscommon_error (_F("Error reading module file %s", inputName.c_str ()));
1559 nssError ();
1560 goto done;
1561 }
1562
1563 /* Add the data to the signature. */
1564 secStatus = SGN_Update (sgn, buffer, numBytes);
1565 if (secStatus != SECSuccess)
1566 {
1567 nsscommon_error (_F("Error while signing module file %s", inputName.c_str ()));
1568 nssError ();
1569 goto done;
1570 }
1571 }
1572
1573 /* Complete the signature. */
1574 secStatus = SGN_End (sgn, & signedData);
1575 if (secStatus != SECSuccess)
1576 {
1577 nsscommon_error (_F("Could not complete signature of module file %s", inputName.c_str ()));
1578 nssError ();
1579 goto done;
1580 }
1581
1582 SGN_DestroyContext (sgn, PR_TRUE);
1583
1584 /* Now write the signed data to the output file. */
1585 if(local_file_fd != NULL)
1586 PR_Close (local_file_fd);
1587 local_file_fd = PR_Open (outputName.c_str(), PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
1588 PR_IRUSR | PR_IWUSR | PR_IRGRP | PR_IWGRP | PR_IROTH);
1589 if (local_file_fd == NULL)
1590 {
1591 nsscommon_error (_F("Could not open signature file %s", outputName.c_str ()));
1592 nssError ();
1593 goto done;
1594 }
1595
1596 numBytes = PR_Write (local_file_fd, signedData.data, signedData.len);
1597 if (numBytes < 0 || numBytes != (PRInt32)signedData.len)
1598 {
1599 nsscommon_error (_F("Error writing to signature file %s", outputName.c_str ()));
1600 nssError ();
1601 }
1602
1603 done:
1604 if (privKey)
1605 SECKEY_DestroyPrivateKey (privKey);
1606 CERT_DestroyCertificate (cert);
1607 if(local_file_fd != NULL)
1608 PR_Close (local_file_fd);
1609 }
1610
1611 // PR_Read() is not guaranteed to read all of the requested data in one call.
1612 // Iterate until all of the requested data has been read.
1613 // Return the same values as PR_Read() would.
1614 PRInt32 PR_Read_Complete (PRFileDesc *fd, void *buf, PRInt32 requestedBytes)
1615 {
1616 // Read until EOF or until the expected number of bytes has been read.
1617 // PR_Read wants (void*), but we need (char *) to do address arithmetic.
1618 char *buffer = (char *)buf;
1619 PRInt32 totalBytes;
1620 PRInt32 bytesRead;
1621 for (totalBytes = 0; totalBytes < requestedBytes; totalBytes += bytesRead)
1622 {
1623 // Now read the data.
1624 bytesRead = PR_Read (fd, (void *)(buffer + totalBytes), requestedBytes - totalBytes);
1625 if (bytesRead == 0)
1626 break; // EOF
1627 if (bytesRead < 0)
1628 return bytesRead; // Error
1629 }
1630
1631 // Return the number of bytes we managed to read.
1632 return totalBytes;
1633 }
1634
1635 SECStatus
1636 read_cert_info_from_file (const string &certPath, string &fingerprint)
1637 {
1638 FILE *certFile = fopen (certPath.c_str (), "rb");
1639 SECStatus secStatus = SECFailure;
1640
1641 if (! certFile)
1642 {
1643 nsscommon_error (_F("Could not open certificate file %s for reading\n%s",
1644 certPath.c_str (), strerror (errno)));
1645 return SECFailure;
1646 }
1647
1648 int fd = fileno (certFile);
1649 struct stat info;
1650 int rc = fstat (fd, &info);
1651 if (rc != 0)
1652 {
1653 nsscommon_error (_F("Could not obtain information about certificate file %s\n%s",
1654 certPath.c_str (), strerror (errno)));
1655 fclose (certFile);
1656 return SECFailure;
1657 }
1658
1659 PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1660 if (!arena)
1661 {
1662 nsscommon_error (_F("Could not create arena while decoding certificate from file %s",
1663 certPath.c_str ()));
1664 fclose (certFile);
1665 goto done;
1666 }
1667
1668 SECItem derCert;
1669 if (!SECITEM_AllocItem(arena, &derCert, info.st_size))
1670 {
1671 nsscommon_error (_F("Could not allocate DER cert\n%s",
1672 strerror (errno)));
1673 fclose (certFile);
1674 goto done;
1675 }
1676
1677 size_t read;
1678 read = fread (derCert.data, 1, derCert.len, certFile);
1679 fclose (certFile);
1680 if (read != derCert.len)
1681 {
1682 nsscommon_error (_F("Error reading from certificate file %s\n%s",
1683 certPath.c_str (), strerror (errno)));
1684 goto done;
1685 }
1686 derCert.type = siDERCertBuffer;
1687
1688 // Sigh. We'd like to use CERT_DecodeDERCertificate() here, but
1689 // although /usr/include/nss3/cert.h declares them, the shared
1690 // library doesn't export them.
1691
1692 CERTCertificate *cert;
1693 int rv;
1694 char *str;
1695
1696 // Strip off the signature.
1697 CERTSignedData *sd;
1698 sd = PORT_ArenaZNew(arena, CERTSignedData);
1699 if (!sd)
1700 {
1701 nsscommon_error (_F("Could not allocate signed data while decoding certificate from file %s",
1702 certPath.c_str ()));
1703 goto done;
1704 }
1705 rv = SEC_ASN1DecodeItem(arena, sd, SEC_ASN1_GET(CERT_SignedDataTemplate),
1706 &derCert);
1707 if (rv)
1708 {
1709 nsscommon_error (_F("Could not decode signature while decoding certificate from file %s",
1710 certPath.c_str ()));
1711 goto done;
1712 }
1713
1714 // Decode the certificate.
1715 cert = PORT_ArenaZNew(arena, CERTCertificate);
1716 if (!cert)
1717 {
1718 nsscommon_error (_F("Could not allocate cert while decoding certificate from file %s",
1719 certPath.c_str ()));
1720 goto done;
1721 }
1722 cert->arena = arena;
1723 rv = SEC_ASN1DecodeItem(arena, cert,
1724 SEC_ASN1_GET(CERT_CertificateTemplate), &sd->data);
1725 if (rv)
1726 {
1727 nsscommon_error (_F("Could not decode certificate from file %s",
1728 certPath.c_str ()));
1729 goto done;
1730 }
1731
1732 // Get the fingerprint from the signature.
1733 unsigned char fingerprint_buf[SHA1_LENGTH];
1734 SECItem fpItem;
1735 rv = PK11_HashBuf(SEC_OID_SHA1, fingerprint_buf, derCert.data, derCert.len);
1736 if (rv)
1737 {
1738 nsscommon_error (_F("Could not decode SHA1 fingerprint from file %s",
1739 certPath.c_str ()));
1740 goto done;
1741 }
1742 fpItem.data = fingerprint_buf;
1743 fpItem.len = sizeof(fingerprint_buf);
1744 str = CERT_Hexify(&fpItem, 1);
1745 if (! str)
1746 {
1747 nsscommon_error (_F("Could not hexify SHA1 fingerprint from file %s",
1748 certPath.c_str ()));
1749 goto done;
1750 }
1751 fingerprint = str;
1752 transform(fingerprint.begin(), fingerprint.end(), fingerprint.begin(),
1753 ::tolower);
1754 PORT_Free(str);
1755 secStatus = SECSuccess;
1756
1757 done:
1758 if (arena)
1759 PORT_FreeArena(arena, PR_FALSE);
1760 return secStatus;
1761 }
1762
1763 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.115803 seconds and 5 git commands to generate.