]> sourceware.org Git - systemtap.git/blame - nsscommon.cxx
Don't require openssl-devel for --enable-http
[systemtap.git] / nsscommon.cxx
CommitLineData
aeb9cc10
DB
1/*
2 Common functions used by the NSS-aware code in systemtap.
3
529ab2f8 4 Copyright (C) 2009-2018 Red Hat Inc.
aeb9cc10
DB
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
e8daaf60 17 along with this program. If not, see <http://www.gnu.org/licenses/>.
aeb9cc10
DB
18*/
19
a4ed6ee1
JS
20#include "config.h"
21
aeb9cc10
DB
22#include <iostream>
23#include <fstream>
24#include <sstream>
aeb9cc10
DB
25#include <cerrno>
26#include <cstdio>
27#include <cassert>
28
29extern "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>
aeb9cc10
DB
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>
b3367f63 45#include <cert.h>
ca8da51f 46#ifdef HAVE_HTTP_SUPPORT
638abbd0
SC
47#include <openssl/bio.h>
48#include <openssl/x509.h>
49#include <openssl/pem.h>
50#include <openssl/x509v3.h>
ca8da51f 51#endif
aeb9cc10
DB
52}
53
54#include "nsscommon.h"
55#include "util.h"
56
57using namespace std;
58
59// Common constants and settings.
60const char *
61server_cert_nickname ()
62{
63 return (const char *)"stap-server";
64}
65
c981acd9
DB
66string
67add_cert_db_prefix (const string &db_path) {
c2e2d5ff 68#if 0 && ((NSS_VMAJOR > 3) || (NSS_VMAJOR == 3 && NSS_VMINOR >= 37))
529ab2f8
FCE
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)
c981acd9
DB
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
aeb9cc10
DB
81string
82server_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
aeb9cc10
DB
93string
94local_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
aeb9cc10
DB
105// Common error handling for applications using this file.
106void
107nsscommon_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.
114static ofstream logfile;
115
116void
da43562f 117start_log (const char *arg, bool redirect_clog)
aeb9cc10
DB
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));
da43562f
DS
125 else if (redirect_clog)
126 clog.rdbuf(logfile.rdbuf());
aeb9cc10
DB
127}
128
129bool
130log_ok ()
131{
132 return logfile.good ();
133}
134
135void
136log (const string &msg)
137{
6ef4a081
DB
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
aeb9cc10 145 if (logfile.good ())
6ef4a081 146 logfile << nowStr << ": " << msg << endl << flush;
aeb9cc10 147 else
6ef4a081 148 clog << nowStr << ": " << msg << endl << flush;
aeb9cc10
DB
149}
150
151void
152end_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".
160extern "C"
161void
162nssError (void)
163{
164 // See if PR_GetError can tell us what the error is.
165 PRErrorCode errorNumber = PR_GetError ();
166
7e001983
FCE
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);
aeb9cc10 170
7e001983
FCE
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.
aeb9cc10
DB
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 }
aeb9cc10
DB
184 nsscommon_error (_F("(%d) %s", errorNumber, errorText));
185}
186
187extern "C"
188SECStatus
dff24561 189nssInit (const char *db_path, int readWrite, int issueMessage)
aeb9cc10
DB
190{
191 SECStatus secStatus;
c981acd9
DB
192 string full_db_path = add_cert_db_prefix (db_path);
193 db_path = full_db_path.c_str();
aeb9cc10
DB
194 if (readWrite)
195 secStatus = NSS_InitReadWrite (db_path);
196 else
197 secStatus = NSS_Init (db_path);
dff24561 198 if (secStatus != SECSuccess && issueMessage)
aeb9cc10
DB
199 {
200 nsscommon_error (_F("Error initializing NSS for %s", db_path));
201 nssError ();
202 }
203 return secStatus;
204}
205
206extern "C"
207void
208nssCleanup (const char *db_path)
209{
9447d916
DB
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 {
c981acd9
DB
222 string full_db_path = add_cert_db_prefix (db_path);
223 db_path = full_db_path.c_str();
9447d916
DB
224 nsscommon_error (_F("WARNING: Attempt to shutdown NSS for database %s, which was never initialized", db_path));
225 }
226 return;
227 }
228
aeb9cc10
DB
229 // Shutdown NSS and ensure that it went down successfully. This is because we can not
230 // initialize NSS again if it does not.
9447d916 231 if (NSS_Shutdown () != SECSuccess)
aeb9cc10
DB
232 {
233 if (db_path)
c981acd9
DB
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 }
aeb9cc10 239 else
9447d916 240 nsscommon_error (_("Unable to shutdown NSS"));
aeb9cc10
DB
241 nssError ();
242 }
243}
244
245// Certificate database password support functions.
246//
247// Disable character echoing, if the fd is a tty.
248static void
249echoOff(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. */
260static void
261echoOn(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 */
278extern "C"
279char *
280nssPasswordCallback (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
3dc20443 335static int
aeb9cc10
DB
336create_server_cert_db (const char *db_path)
337{
338 return create_dir (db_path, 0755);
339}
340
341static int
342create_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
348static int
349clean_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 }
03dd566c 364 globfree(& globbuf);
aeb9cc10
DB
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
378static int
379init_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
433static SECKEYPrivateKey *
434generate_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;
529ab2f8 455 rsaparams.keySizeInBits = 4096; /* 1024 too small; SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED */
aeb9cc10
DB
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
471static CERTCertificateRequest *
472generate_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
493static CERTCertificate *
494create_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
653static SECItem *
654sign_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
709static SECStatus
710add_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
759done:
760 CERT_DestroyCertificate (cert);
761 if (trust)
762 PORT_Free (trust);
763 return secStatus;
764}
765
766SECStatus
638abbd0 767add_client_cert (const string &inFileName, const string &db_path, bool init_db)
aeb9cc10
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);
1d7ae21b
FCE
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 }
aeb9cc10
DB
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
638abbd0
SC
807 SECStatus secStatus;
808 if (init_db)
aeb9cc10 809 {
638abbd0
SC
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*/);
aeb9cc10 814 if (secStatus != SECSuccess)
638abbd0
SC
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 }
aeb9cc10
DB
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 }
7502ddfd 901
aeb9cc10 902 done:
7502ddfd 903 // Free NSS/NSPR objects and shutdown NSS.
aeb9cc10
DB
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);
638abbd0
SC
912 if (init_db)
913 nssCleanup (db_path.c_str ());
7502ddfd
DB
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 }
03dd566c 933 globfree(&globbuf);
7502ddfd
DB
934 }
935
aeb9cc10
DB
936 return secStatus;
937}
938
939int
940gen_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;
704918e7 977 string hostname;
aeb9cc10
DB
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
12ef2e2d 1024 // For the cert, we need our host name.
aeb9cc10
DB
1025 struct utsname utsname;
1026 uname (& utsname);
1027 dnsNames = utsname.nodename;
12ef2e2d
DB
1028
1029 // Because avahi identifies hosts using a ".local" domain, add one to the list of names.
704918e7
DB
1030 hostname = dnsNames.substr (0, dnsNames.find ('.'));
1031 dnsNames += string(",") + hostname + ".local";
12ef2e2d
DB
1032
1033 // Add any extra names that were supplied.
aeb9cc10
DB
1034 if (! extraDnsNames.empty ())
1035 dnsNames += "," + extraDnsNames;
12ef2e2d
DB
1036
1037 // Now, generate the cert.
aeb9cc10
DB
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
1108CERTCertList *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
aeb9cc10
DB
1137static int
1138format_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:
eeacd9d4 1151 nsscommon_error (_("Could not decode certificate validity"));
aeb9cc10
DB
1152 return 1;
1153 }
1154 if (secStatus != SECSuccess)
1155 {
eeacd9d4 1156 nsscommon_error (_("Could not decode certificate validity time"));
aeb9cc10
DB
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 {
eeacd9d4 1165 nsscommon_error (_("Could not format certificate validity time"));
aeb9cc10
DB
1166 return 1;
1167 }
1168
1169 return 0;
1170}
1171
1172static bool
1173cert_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
638abbd0
SC
1185
1186bool
1187get_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
aeb9cc10 1231static bool
638abbd0
SC
1232cert_db_is_valid (const string &db_path, const string &nss_cert_name,
1233 CERTCertificate *this_cert)
aeb9cc10
DB
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)
6ef4a081 1278 log (_F(" Not Valid Before: %s UTC", timeString));
aeb9cc10 1279 if (format_cert_validity_time (v.notAfter, timeString, sizeof (timeString)) == 0)
6ef4a081 1280 log (_F(" Not Valid After: %s UTC", timeString));
aeb9cc10
DB
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.
6ef4a081 1286 log (_("Certificate is valid"));
aeb9cc10 1287 valid_p = true;
638abbd0
SC
1288 if (this_cert)
1289 *this_cert = *c;
aeb9cc10
DB
1290 break;
1291 }
1292
1293 // The cert is not valid. Look for another one.
6ef4a081 1294 log (_("Certificate is not valid"));
aeb9cc10
DB
1295 }
1296 CERT_DestroyCertList (certs);
1297
1298 done:
1299 nssCleanup (db_path.c_str ());
1300 return valid_p;
1301}
1302
638abbd0
SC
1303
1304#ifdef HAVE_HTTP_SUPPORT
1305/*
1306 * Similar to cert_db_is_valid; additionally it checks host_name and does no logging
1307 */
1308static bool
1309get_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
1373bool
1374cvt_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
1401bool
1402get_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
1417bool
1418have_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 string ((char *) ASN1_STRING_get0_data (current_name->d.dNSName));
1446
1447 if ((size_t) ASN1_STRING_length (current_name->d.dNSName) ==
1448 dns_name.length ())
1449 {
1450 if (hostname.compare(scheme_len,dns_name.length(), dns_name))
1451 {
1452 have_match = true;
1453 break;
1454 }
1455 }
1456 }
1457 }
1458 sk_GENERAL_NAME_pop_free (san_names, GENERAL_NAME_free);
1459 BIO_free(bio);
1460 X509_free(server_cert);
1461
1462 return have_match;
1463}
1464#endif
1465
1466
aeb9cc10
DB
1467// Ensure that our certificate exists and is valid. Generate a new one if not.
1468int
1469check_cert (const string &db_path, const string &nss_cert_name, bool use_db_password)
1470{
1471 // Generate a new cert database if the current one does not exist or is not valid.
638abbd0 1472 if (! cert_db_is_valid (db_path, nss_cert_name, NULL))
aeb9cc10
DB
1473 {
1474 if (gen_cert_db (db_path, "", use_db_password) != 0)
1475 {
1476 // NSS message already issued.
1477 nsscommon_error (_("Unable to generate new certificate"));
1478 return 1;
1479 }
1480 }
1481 return 0;
1482}
1483
1484void sign_file (
1485 const string &db_path,
1486 const string &nss_cert_name,
1487 const string &inputName,
1488 const string &outputName
1489) {
1490 /* Get own certificate and private key. */
1491 CERTCertificate *cert = PK11_FindCertFromNickname (nss_cert_name.c_str (), NULL);
1492 if (cert == NULL)
1493 {
1494 nsscommon_error (_F("Unable to find certificate with nickname %s in %s.",
1495 nss_cert_name.c_str (), db_path.c_str()));
1496 nssError ();
1497 return;
1498 }
1499
1500 // Predeclare these to keep C++ happy abount branches to 'done'.
1501 unsigned char buffer[4096];
63a1cb66 1502 PRFileDesc *local_file_fd = NULL;
aeb9cc10
DB
1503 PRInt32 numBytes;
1504 SECStatus secStatus;
1505 SGNContext *sgn;
1506 SECItem signedData;
1507
1508 /* db_path.c_str () gets passed to nssPasswordCallback */
1509 SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert (cert, (void *)db_path.c_str ());
1510 if (privKey == NULL)
1511 {
1512 nsscommon_error (_F("Unable to obtain private key from the certificate with nickname %s in %s.",
1513 nss_cert_name.c_str (), db_path.c_str()));
1514 nssError ();
1515 goto done;
1516 }
1517
1518 /* Sign the file. */
1519 /* Set up the signing context. */
1520 sgn = SGN_NewContext (SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, privKey);
1521 if (! sgn)
1522 {
1523 nsscommon_error (_("Could not create signing context"));
1524 nssError ();
1525 return;
1526 }
1527 secStatus = SGN_Begin (sgn);
1528 if (secStatus != SECSuccess)
1529 {
1530 nsscommon_error (_("Could not initialize signing context."));
1531 nssError ();
1532 return;
1533 }
1534
1535 /* Now read the data and add it to the signature. */
1536 local_file_fd = PR_Open (inputName.c_str(), PR_RDONLY, 0);
1537 if (local_file_fd == NULL)
1538 {
1539 nsscommon_error (_F("Could not open module file %s", inputName.c_str ()));
1540 nssError ();
1541 return;
1542 }
1543
1544 for (;;)
1545 {
5ab345f0
DB
1546 // No need for PR_Read_Complete here, since we're already managing multiple
1547 // reads to a fixed size buffer.
aeb9cc10
DB
1548 numBytes = PR_Read (local_file_fd, buffer, sizeof (buffer));
1549 if (numBytes == 0)
1550 break; /* EOF */
1551
1552 if (numBytes < 0)
1553 {
1554 nsscommon_error (_F("Error reading module file %s", inputName.c_str ()));
1555 nssError ();
1556 goto done;
1557 }
1558
1559 /* Add the data to the signature. */
1560 secStatus = SGN_Update (sgn, buffer, numBytes);
1561 if (secStatus != SECSuccess)
1562 {
1563 nsscommon_error (_F("Error while signing module file %s", inputName.c_str ()));
1564 nssError ();
1565 goto done;
1566 }
1567 }
1568
1569 /* Complete the signature. */
1570 secStatus = SGN_End (sgn, & signedData);
1571 if (secStatus != SECSuccess)
1572 {
1573 nsscommon_error (_F("Could not complete signature of module file %s", inputName.c_str ()));
1574 nssError ();
1575 goto done;
1576 }
1577
1578 SGN_DestroyContext (sgn, PR_TRUE);
1579
1580 /* Now write the signed data to the output file. */
93ea565a
LB
1581 if(local_file_fd != NULL)
1582 PR_Close (local_file_fd);
aeb9cc10
DB
1583 local_file_fd = PR_Open (outputName.c_str(), PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
1584 PR_IRUSR | PR_IWUSR | PR_IRGRP | PR_IWGRP | PR_IROTH);
1585 if (local_file_fd == NULL)
1586 {
1587 nsscommon_error (_F("Could not open signature file %s", outputName.c_str ()));
1588 nssError ();
1589 goto done;
1590 }
1591
1592 numBytes = PR_Write (local_file_fd, signedData.data, signedData.len);
1593 if (numBytes < 0 || numBytes != (PRInt32)signedData.len)
1594 {
1595 nsscommon_error (_F("Error writing to signature file %s", outputName.c_str ()));
1596 nssError ();
1597 }
1598
1599 done:
1600 if (privKey)
1601 SECKEY_DestroyPrivateKey (privKey);
1602 CERT_DestroyCertificate (cert);
97b1f2ee
LB
1603 if(local_file_fd != NULL)
1604 PR_Close (local_file_fd);
aeb9cc10
DB
1605}
1606
5ab345f0
DB
1607// PR_Read() is not guaranteed to read all of the requested data in one call.
1608// Iterate until all of the requested data has been read.
1609// Return the same values as PR_Read() would.
1610PRInt32 PR_Read_Complete (PRFileDesc *fd, void *buf, PRInt32 requestedBytes)
1611{
1612 // Read until EOF or until the expected number of bytes has been read.
1613 // PR_Read wants (void*), but we need (char *) to do address arithmetic.
1614 char *buffer = (char *)buf;
18c01558
FCE
1615 PRInt32 totalBytes;
1616 PRInt32 bytesRead;
1617 for (totalBytes = 0; totalBytes < requestedBytes; totalBytes += bytesRead)
5ab345f0
DB
1618 {
1619 // Now read the data.
1620 bytesRead = PR_Read (fd, (void *)(buffer + totalBytes), requestedBytes - totalBytes);
1621 if (bytesRead == 0)
1622 break; // EOF
1623 if (bytesRead < 0)
1624 return bytesRead; // Error
1625 }
1626
1627 // Return the number of bytes we managed to read.
1628 return totalBytes;
1629}
1630
b3367f63 1631SECStatus
cd1418c7 1632read_cert_info_from_file (const string &certPath, string &fingerprint)
b3367f63
DS
1633{
1634 FILE *certFile = fopen (certPath.c_str (), "rb");
1635 SECStatus secStatus = SECFailure;
1636
1637 if (! certFile)
1638 {
1639 nsscommon_error (_F("Could not open certificate file %s for reading\n%s",
1640 certPath.c_str (), strerror (errno)));
1641 return SECFailure;
1642 }
1643
1644 int fd = fileno (certFile);
1645 struct stat info;
1646 int rc = fstat (fd, &info);
1647 if (rc != 0)
1648 {
1649 nsscommon_error (_F("Could not obtain information about certificate file %s\n%s",
1650 certPath.c_str (), strerror (errno)));
1651 fclose (certFile);
1652 return SECFailure;
1653 }
1654
1655 PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1656 if (!arena)
1657 {
1658 nsscommon_error (_F("Could not create arena while decoding certificate from file %s",
1659 certPath.c_str ()));
82c6d474 1660 fclose (certFile);
b3367f63
DS
1661 goto done;
1662 }
1663
1664 SECItem derCert;
1665 if (!SECITEM_AllocItem(arena, &derCert, info.st_size))
1666 {
1667 nsscommon_error (_F("Could not allocate DER cert\n%s",
1668 strerror (errno)));
1669 fclose (certFile);
1670 goto done;
1671 }
1672
1673 size_t read;
1674 read = fread (derCert.data, 1, derCert.len, certFile);
1675 fclose (certFile);
1676 if (read != derCert.len)
1677 {
1678 nsscommon_error (_F("Error reading from certificate file %s\n%s",
1679 certPath.c_str (), strerror (errno)));
1680 goto done;
1681 }
1682 derCert.type = siDERCertBuffer;
1683
cd1418c7 1684 // Sigh. We'd like to use CERT_DecodeDERCertificate() here, but
b3367f63
DS
1685 // although /usr/include/nss3/cert.h declares them, the shared
1686 // library doesn't export them.
1687
1688 CERTCertificate *cert;
1689 int rv;
cd1418c7 1690 char *str;
b3367f63
DS
1691
1692 // Strip off the signature.
1693 CERTSignedData *sd;
1694 sd = PORT_ArenaZNew(arena, CERTSignedData);
1695 if (!sd)
1696 {
1697 nsscommon_error (_F("Could not allocate signed data while decoding certificate from file %s",
1698 certPath.c_str ()));
1699 goto done;
1700 }
1701 rv = SEC_ASN1DecodeItem(arena, sd, SEC_ASN1_GET(CERT_SignedDataTemplate),
1702 &derCert);
1703 if (rv)
1704 {
1705 nsscommon_error (_F("Could not decode signature while decoding certificate from file %s",
1706 certPath.c_str ()));
1707 goto done;
1708 }
1709
1710 // Decode the certificate.
1711 cert = PORT_ArenaZNew(arena, CERTCertificate);
1712 if (!cert)
1713 {
1714 nsscommon_error (_F("Could not allocate cert while decoding certificate from file %s",
1715 certPath.c_str ()));
1716 goto done;
1717 }
1718 cert->arena = arena;
1719 rv = SEC_ASN1DecodeItem(arena, cert,
1720 SEC_ASN1_GET(CERT_CertificateTemplate), &sd->data);
1721 if (rv)
1722 {
1723 nsscommon_error (_F("Could not decode certificate from file %s",
1724 certPath.c_str ()));
1725 goto done;
1726 }
1727
b3367f63 1728 // Get the fingerprint from the signature.
cd1418c7 1729 unsigned char fingerprint_buf[SHA1_LENGTH];
b3367f63
DS
1730 SECItem fpItem;
1731 rv = PK11_HashBuf(SEC_OID_SHA1, fingerprint_buf, derCert.data, derCert.len);
1732 if (rv)
1733 {
1734 nsscommon_error (_F("Could not decode SHA1 fingerprint from file %s",
1735 certPath.c_str ()));
1736 goto done;
1737 }
1738 fpItem.data = fingerprint_buf;
cd1418c7 1739 fpItem.len = sizeof(fingerprint_buf);
b3367f63
DS
1740 str = CERT_Hexify(&fpItem, 1);
1741 if (! str)
1742 {
1743 nsscommon_error (_F("Could not hexify SHA1 fingerprint from file %s",
1744 certPath.c_str ()));
1745 goto done;
1746 }
1747 fingerprint = str;
1748 transform(fingerprint.begin(), fingerprint.end(), fingerprint.begin(),
1749 ::tolower);
1750 PORT_Free(str);
1751 secStatus = SECSuccess;
1752
1753done:
1754 if (arena)
1755 PORT_FreeArena(arena, PR_FALSE);
1756 return secStatus;
1757}
1758
aeb9cc10 1759/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.289998 seconds and 5 git commands to generate.