]> sourceware.org Git - systemtap.git/blame - nsscommon.cxx
Bump product number for beginners guide.
[systemtap.git] / nsscommon.cxx
CommitLineData
aeb9cc10
DB
1/*
2 Common functions used by the NSS-aware code in systemtap.
3
4 Copyright (C) 2009-2011 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, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19*/
20
21#include <iostream>
22#include <fstream>
23#include <sstream>
24#include <iomanip>
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>
36
37#include <nss.h>
38#include <nspr.h>
39#include <ssl.h>
40#include <prerror.h>
41#include <secerr.h>
42#include <sslerr.h>
43#include <cryptohi.h>
44#include <keyhi.h>
45#include <secder.h>
46}
47
48#include "nsscommon.h"
49#include "util.h"
50
51using namespace std;
52
53// Common constants and settings.
54const char *
55server_cert_nickname ()
56{
57 return (const char *)"stap-server";
58}
59
60string
61server_cert_db_path ()
62{
63 string data_path;
64 const char* s_d = getenv ("SYSTEMTAP_DIR");
65 if (s_d != NULL)
66 data_path = s_d;
67 else
68 data_path = get_home_directory() + string("/.systemtap");
69 return data_path + "/ssl/server";
70}
71
72string
73server_cert_file ()
74{
75 return server_cert_db_path () + "/stap.cert";
76}
77
78string
79local_client_cert_db_path ()
80{
81 string data_path;
82 const char* s_d = getenv ("SYSTEMTAP_DIR");
83 if (s_d != NULL)
84 data_path = s_d;
85 else
86 data_path = get_home_directory() + string("/.systemtap");
87 return data_path + "/ssl/client";
88}
89
90string
91global_client_cert_db_path ()
92{
93 return SYSCONFDIR "/systemtap/ssl/client";
94}
95
96// Common error handling for applications using this file.
97void
98nsscommon_error (const string &msg, int logit)
99{
100 // Call the extern "C" version supplied by each application.
101 nsscommon_error (msg.c_str (), logit);
102}
103
104// Logging. Enabled only by stap-serverd but called from some common methods.
105static ofstream logfile;
106
107void
108start_log (const char *arg)
109{
110 if (logfile.is_open ())
111 logfile.close ();
112
113 logfile.open (arg, ios_base::app);
114 if (! logfile.good ())
115 nsscommon_error (_F("Could not open log file %s", arg));
116}
117
118bool
119log_ok ()
120{
121 return logfile.good ();
122}
123
124void
125log (const string &msg)
126{
127 if (logfile.good ())
128 logfile << msg << endl << flush;
129 else
130 clog << msg << endl << flush;
131}
132
133void
134end_log ()
135{
136 if (logfile.is_open ())
137 logfile.close ();
138}
139
140// NSS/NSPR error reporting and cleanup.
141// These functions are called from C code as well as C++, so make them extern "C".
142extern "C"
143void
144nssError (void)
145{
146 // See if PR_GetError can tell us what the error is.
147 PRErrorCode errorNumber = PR_GetError ();
148
149 // PR_ErrorToString always returns a valid string for errors in this range.
150 if (errorNumber >= PR_NSPR_ERROR_BASE && errorNumber <= PR_MAX_ERROR)
151 {
152 nsscommon_error (_F("(%d) %s", errorNumber, PR_ErrorToString (errorNumber, PR_LANGUAGE_EN)));
153 return;
154 }
155
156 // PR_ErrorToString does not handle errors outside the range above, so we handle them ourselves.
157 const char *errorText;
158 switch (errorNumber) {
159 default: errorText = "Unknown error"; break;
160#define NSSYERROR(code,msg) case code: errorText = msg; break
161#include "stapsslerr.h"
162#undef NSSYERROR
163 }
164
165 nsscommon_error (_F("(%d) %s", errorNumber, errorText));
166}
167
168extern "C"
169SECStatus
dff24561 170nssInit (const char *db_path, int readWrite, int issueMessage)
aeb9cc10
DB
171{
172 SECStatus secStatus;
173 if (readWrite)
174 secStatus = NSS_InitReadWrite (db_path);
175 else
176 secStatus = NSS_Init (db_path);
dff24561 177 if (secStatus != SECSuccess && issueMessage)
aeb9cc10
DB
178 {
179 nsscommon_error (_F("Error initializing NSS for %s", db_path));
180 nssError ();
181 }
182 return secStatus;
183}
184
185extern "C"
186void
187nssCleanup (const char *db_path)
188{
189 // Shutdown NSS and ensure that it went down successfully. This is because we can not
190 // initialize NSS again if it does not.
191 if (NSS_Shutdown () != SECSuccess)
192 {
193 if (db_path)
194 nsscommon_error (_F("Unable to shutdown NSS for database %s", db_path));
195 else
196 nsscommon_error (_("Unable to shutdown NSS"));
197 nssError ();
198 }
199}
200
201// Certificate database password support functions.
202//
203// Disable character echoing, if the fd is a tty.
204static void
205echoOff(int fd)
206{
207 if (isatty(fd)) {
208 struct termios tio;
209 tcgetattr(fd, &tio);
210 tio.c_lflag &= ~ECHO;
211 tcsetattr(fd, TCSAFLUSH, &tio);
212 }
213}
214
215/* Enable character echoing, if the fd is a tty. */
216static void
217echoOn(int fd)
218{
219 if (isatty(fd)) {
220 struct termios tio;
221 tcgetattr(fd, &tio);
222 tio.c_lflag |= ECHO;
223 tcsetattr(fd, TCSAFLUSH, &tio);
224 }
225}
226
227/*
228 * This function is our custom password handler that is called by
229 * NSS when retrieving private certs and keys from the database. Returns a
230 * pointer to a string with a password for the database. Password pointer
231 * must be allocated by one of the NSPR memory allocation functions, or by PORT_Strdup,
232 * and will be freed by the caller.
233 */
234extern "C"
235char *
236nssPasswordCallback (PK11SlotInfo *info __attribute ((unused)), PRBool retry, void *arg)
237{
238 static int retries = 0;
239 #define PW_MAX 200
240 char* password = NULL;
241 char* password_ret = NULL;
242 const char *dbname ;
243 int infd;
244 int isTTY;
245
246 if (! retry)
247 {
248 /* Not a retry. */
249 retries = 0;
250 }
251 else
252 {
253 /* Maximum of 2 retries for bad password. */
254 if (++retries > 2)
255 return NULL; /* No more retries */
256 }
257
258 /* Can only prompt for a password if stdin is a tty. */
259 infd = fileno (stdin);
260 isTTY = isatty (infd);
261 if (! isTTY)
262 {
263 nsscommon_error (_("Cannot prompt for certificate database password. stdin is not a tty"));
264 return NULL;
265 }
266
267 /* Prompt for password */
268 password = (char *)PORT_Alloc (PW_MAX);
269 if (! password)
270 {
271 nssError ();
272 return NULL;
273 }
274
275 dbname = (const char *)arg;
276 cerr << _F("Password for certificate database in %s: ", dbname) << flush;
277 echoOff (infd);
278 password_ret = fgets (password, PW_MAX, stdin);
279 cerr << endl << flush;
280 echoOn(infd);
281
282 if (password_ret)
283 /* stomp on the newline */
284 *strchrnul (password, '\n') = '\0';
285 else
286 PORT_Free (password);
287
288 return password_ret;
289}
290
291extern "C"
292int
293create_server_cert_db (const char *db_path)
294{
295 return create_dir (db_path, 0755);
296}
297
298static int
299create_client_cert_db (const char *db_path)
300{
301 // Same properties as the server's database, at present.
302 return create_server_cert_db (db_path);
303}
304
305static int
306clean_cert_db (const string &db_path)
307{
308 // First remove all files from the directory
309 glob_t globbuf;
310 string filespec = db_path + "/*";
311 int r = glob (filespec.c_str (), 0, NULL, & globbuf);
312 if (r == GLOB_NOSPACE || r == GLOB_ABORTED)
313 nsscommon_error (_F("Could not search certificate database directory %s", db_path.c_str ()));
314 else if (r != GLOB_NOMATCH)
315 {
316 for (unsigned i = 0; i < globbuf.gl_pathc; ++i)
317 {
318 if (remove_file_or_dir (globbuf.gl_pathv[i]) != 0)
319 nsscommon_error (_F("Could not remove %s", globbuf.gl_pathv[i]));
320 }
321 }
322
323 // Now remove the directory itself.
324 if (remove_file_or_dir (db_path.c_str ()) != 0)
325 {
326 nsscommon_error (_F("Could not remove certificate database directory %s\n%s",
327 db_path.c_str (), strerror (errno)));
328 return 1;
329 }
330
331 return 0;
332}
333
334static int
335init_password (PK11SlotInfo *slot, const string &db_path, bool use_password)
336{
337 // Prompt for the database password, if we're using one. Keep the passwords in memory for as
338 // little time as possible.
339 SECStatus secStatus;
340 if (use_password)
341 {
342 char *pw1 = 0;
343 int attempts;
344 const int max_attempts = 3;
345 for (attempts = 0; attempts < max_attempts; ++attempts)
346 {
347 pw1 = nssPasswordCallback (slot, false, (void*)db_path.c_str ());
348 if (! pw1)
349 continue;
350 cerr << "Confirm ";
351 bool match = false;
352 char *pw2 = nssPasswordCallback (slot, false, (void*)db_path.c_str ());
353 if (pw2)
354 {
355 if (strcmp (pw1, pw2) == 0)
356 match = true;
357 else
358 nsscommon_error (_("Passwords do not match"));
359 memset (pw2, 0, strlen (pw2));
360 PORT_Free (pw2);
361 }
362 if (match)
363 break;
364 memset (pw1, 0, strlen (pw1));
365 PORT_Free (pw1);
366 }
367 if (attempts >= max_attempts)
368 {
369 nsscommon_error (_("Too many password attempts"));
370 return 1;
371 }
372 secStatus = PK11_InitPin (slot, 0, pw1);
373 memset (pw1, 0, strlen (pw1));
374 PORT_Free (pw1);
375 }
376 else
377 secStatus = PK11_InitPin (slot, 0, 0);
378
379 if (secStatus != SECSuccess)
380 {
381 nsscommon_error (_F("Could not initialize pin for certificate database %s", db_path.c_str()));
382 nssError ();
383 return 1;
384 }
385
386 return 0;
387}
388
389static SECKEYPrivateKey *
390generate_private_key (const string &db_path, PK11SlotInfo *slot, SECKEYPublicKey **pubkeyp)
391{
392 if (PK11_Authenticate (slot, PR_TRUE, 0) != SECSuccess)
393 {
394 nsscommon_error (_F("Unable to authenticate the default slot for certificate database %s",
395 db_path.c_str ()));
396 nssError ();
397 return 0;
398 }
399
400 // Do some random-number initialization.
401 // TODO: We can do better.
402 srand (time (NULL));
403 char randbuf[64];
404 for (unsigned i = 0; i < sizeof (randbuf); ++i)
405 randbuf[i] = rand ();
406 PK11_RandomUpdate (randbuf, sizeof (randbuf));
407 memset (randbuf, 0, sizeof (randbuf));
408
409 // Set up for RSA.
410 PK11RSAGenParams rsaparams;
411 rsaparams.keySizeInBits = 1024;
412 rsaparams.pe = 0x010001;
413 CK_MECHANISM_TYPE mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
414
415 // Generate the key pair.
416 SECKEYPrivateKey *privKey = PK11_GenerateKeyPair (slot, mechanism, & rsaparams, pubkeyp,
417 PR_TRUE /*isPerm*/, PR_TRUE /*isSensitive*/,
418 0/*pwdata*/);
419 if (! privKey)
420 {
421 nsscommon_error (_("Unable to generate public/private key pair"));
422 nssError ();
423 }
424 return privKey;
425}
426
427static CERTCertificateRequest *
428generate_cert_request (SECKEYPublicKey *pubk, CERTName *subject)
429{
430 CERTSubjectPublicKeyInfo *spki = SECKEY_CreateSubjectPublicKeyInfo (pubk);
431 if (! spki)
432 {
433 nsscommon_error (_("Unable to create subject public key info for certificate request"));
434 nssError ();
435 return 0;
436 }
437
438 /* Generate certificate request */
439 CERTCertificateRequest *cr = CERT_CreateCertificateRequest (subject, spki, 0);
440 SECKEY_DestroySubjectPublicKeyInfo (spki);
441 if (! cr)
442 {
443 nsscommon_error (_("Unable to create certificate request"));
444 nssError ();
445 }
446 return cr;
447}
448
449static CERTCertificate *
450create_cert (CERTCertificateRequest *certReq, const string &dnsNames)
451{
452 // What is the current date and time?
453 PRTime now = PR_Now ();
454
455 // What is the date and time 1 year from now?
456 PRExplodedTime printableTime;
457 PR_ExplodeTime (now, PR_GMTParameters, & printableTime);
458 printableTime.tm_month += 12;
459 PRTime after = PR_ImplodeTime (& printableTime);
460
461 // Note that the time is now in micro-second units.
462 CERTValidity *validity = CERT_CreateValidity (now, after);
463 if (! validity)
464 {
465 nsscommon_error (_("Unable to create certificate validity dates"));
466 nssError ();
467 return 0;
468 }
469
470 // Create a default serial number using the current time.
471 PRTime serialNumber = now >> 19; // copied from certutil.
472
473 // Create the certificate.
474 CERTCertificate *cert = CERT_CreateCertificate (serialNumber, & certReq->subject, validity,
475 certReq);
476 CERT_DestroyValidity (validity);
477 if (! cert)
478 {
479 nsscommon_error (_("Unable to create certificate"));
480 nssError ();
481 return 0;
482 }
483
484 // Predeclare these to keep C++ happy about jumps to the label 'error'.
485 SECStatus secStatus = SECSuccess;
486 unsigned char keyUsage = 0x0;
487 PRArenaPool *arena = 0;
488
489 // Add the extensions that we need.
490 void *extHandle = CERT_StartCertExtensions (cert);
491 if (! extHandle)
492 {
493 nsscommon_error (_("Unable to allocate certificate extensions"));
494 nssError ();
495 goto error;
496 }
497
498 // Cert type extension.
499 keyUsage |= (0x80 >> 1); // SSL Server
500 keyUsage |= (0x80 >> 3); // Object signer
501 keyUsage |= (0x80 >> 7); // Object signing CA
502
503 SECItem bitStringValue;
504 bitStringValue.data = & keyUsage;
505 bitStringValue.len = 1;
506
507 secStatus = CERT_EncodeAndAddBitStrExtension (extHandle,
508 SEC_OID_NS_CERT_EXT_CERT_TYPE,
509 & bitStringValue, PR_TRUE);
510 if (secStatus != SECSuccess)
511 {
512 nsscommon_error (_("Unable to encode certificate type extensions"));
513 nssError ();
514 goto error;
515 }
516
517 // Alternate dns name extension.
518 if (! dnsNames.empty ())
519 {
520 arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE);
521 if (! arena)
522 {
523 nsscommon_error (_("Unable to allocate alternate DNS name extension for certificate"));
524 goto error;
525 }
526
527 // Walk down the comma separated list of names.
528 CERTGeneralName *nameList = 0;
529 CERTGeneralName *current = 0;
530 PRCList *prev = 0;
531 vector<string>components;
532 tokenize (dnsNames, components, ",");
533 for (unsigned i = 0; i < components.size (); ++i)
534 {
535 char *tbuf = (char *)PORT_ArenaAlloc (arena, components[i].size () + 1);
536 strcpy (tbuf, components[i].c_str ());
537
538 current = (CERTGeneralName *)PORT_ZAlloc (sizeof (CERTGeneralName));
539 if (! current)
540 {
541 nsscommon_error (_("Unable to allocate alternate DNS name extension for certificate"));
542 goto error;
543 }
544 if (prev)
545 {
546 current->l.prev = prev;
547 prev->next = & current->l;
548 }
549 else
550 nameList = current;
551
552 current->type = certDNSName;
553 current->name.other.data = (unsigned char *)tbuf;
554 current->name.other.len = strlen (tbuf);
555 prev = & current->l;
556 }
557
558 // At this point nameList points to the head of a doubly linked,
559 // but not yet circular, list and current points to its tail.
560 if (nameList)
561 {
562 // Make nameList circular.
563 nameList->l.prev = prev;
564 current->l.next = & nameList->l;
565
566 // Encode and add the extension.
567 SECItem item;
568 secStatus = CERT_EncodeAltNameExtension (arena, nameList, & item);
569 if (secStatus != SECSuccess)
570 {
571 nsscommon_error (_("Unable to encode alternate DNS name extension for certificate"));
572 nssError ();
573 goto error;
574 }
575 secStatus = CERT_AddExtension(extHandle,
576 SEC_OID_X509_SUBJECT_ALT_NAME,
577 & item, PR_FALSE, PR_TRUE);
578 if (secStatus != SECSuccess)
579 {
580 nsscommon_error (_("Unable to add alternate DNS name extension for certificate"));
581 nssError ();
582 goto error;
583 }
584 }
585 } // extra dns names specified.
586
587 // We did not create any extensions on the cert request.
588 assert (certReq->attributes != NULL);
589 assert (certReq->attributes[0] == NULL);
590
591 // Finished with cert extensions.
592 secStatus = CERT_FinishExtensions (extHandle);
593 if (secStatus != SECSuccess)
594 {
595 nsscommon_error (_("Unable to complete alternate DNS name extension for certificate"));
596 nssError ();
597 goto error;
598 }
599
600 return cert;
601
602 error:
603 if (arena)
604 PORT_FreeArena (arena, PR_FALSE);
605 CERT_DestroyCertificate (cert);
606 return 0;
607}
608
609static SECItem *
610sign_cert (CERTCertificate *cert, SECKEYPrivateKey *privKey)
611{
612 SECOidTag algID = SEC_GetSignatureAlgorithmOidTag (privKey->keyType,
613 SEC_OID_UNKNOWN);
614 if (algID == SEC_OID_UNKNOWN)
615 {
616 nsscommon_error (_("Unable to determine the signature algorithm for the signing the certificate"));
617 nssError ();
618 return 0;
619 }
620
621 PRArenaPool *arena = cert->arena;
622 SECStatus rv = SECOID_SetAlgorithmID (arena, & cert->signature, algID, 0);
623 if (rv != SECSuccess)
624 {
625 nsscommon_error (_("Unable to set the signature algorithm for signing the certificate"));
626 nssError ();
627 return 0;
628 }
629
630 /* we only deal with cert v3 here */
631 *(cert->version.data) = 2;
632 cert->version.len = 1;
633
634 SECItem der;
635 der.len = 0;
636 der.data = 0;
637 void *dummy = SEC_ASN1EncodeItem (arena, & der, cert,
638 SEC_ASN1_GET (CERT_CertificateTemplate));
639 if (! dummy)
640 {
641 nsscommon_error (_("Unable to encode the certificate for signing"));
642 nssError ();
643 return 0;
644 }
645
646 SECItem *result = (SECItem *)PORT_ArenaZAlloc (arena, sizeof (SECItem));
647 if (! result)
648 {
649 nsscommon_error (_("Unable to allocate memory for signing the certificate"));
650 return 0;
651 }
652
653 rv = SEC_DerSignData (arena, result, der.data, der.len, privKey, algID);
654 if (rv != SECSuccess)
655 {
656 nsscommon_error (_("Unable to sign the certificate"));
657 nssError ();
658 return 0;
659 }
660
661 cert->derCert = *result;
662 return result;
663}
664
665static SECStatus
666add_server_cert (const string &db_path, SECItem *certDER, PK11SlotInfo *slot)
667{
668 // Decode the cert.
669 CERTCertificate *cert = CERT_DecodeCertFromPackage((char *)certDER->data, certDER->len);
670 if (! cert)
671 {
672 nsscommon_error (_("Unable to decode certificate"));
673 nssError ();
674 return SECFailure;
675 }
676
677 // Import it into the database.
678 CERTCertDBHandle *handle = 0;
679 CERTCertTrust *trust = NULL;
680 SECStatus secStatus = PK11_ImportCert (slot, cert, CK_INVALID_HANDLE,
681 server_cert_nickname (), PR_FALSE);
682 if (secStatus != SECSuccess)
683 {
684 nsscommon_error (_F("Unable to import certificate into the database at %s", db_path.c_str ()));
685 nssError ();
686 goto done;
687 }
688
689 // Make it a trusted server and signer.
690 trust = (CERTCertTrust *)PORT_ZAlloc (sizeof (CERTCertTrust));
691 if (! trust)
692 {
693 nsscommon_error (_("Unable to allocate certificate trust"));
694 secStatus = SECFailure;
695 goto done;
696 }
697
698 secStatus = CERT_DecodeTrustString (trust, "PCu,,PCu");
699 if (secStatus != SECSuccess)
700 {
701 nsscommon_error (_("Unable decode trust string 'PCu,,PCu'"));
702 nssError ();
703 goto done;
704 }
705
706 handle = CERT_GetDefaultCertDB ();
707 assert (handle);
708 secStatus = CERT_ChangeCertTrust (handle, cert, trust);
709 if (secStatus != SECSuccess)
710 {
711 nsscommon_error (_("Unable to change certificate trust"));
712 nssError ();
713 }
714
715done:
716 CERT_DestroyCertificate (cert);
717 if (trust)
718 PORT_Free (trust);
719 return secStatus;
720}
721
722SECStatus
723add_client_cert (const string &inFileName, const string &db_path)
724{
725 FILE *inFile = fopen (inFileName.c_str (), "rb");
726 if (! inFile)
727 {
728 nsscommon_error (_F("Could not open certificate file %s for reading\n%s",
729 inFileName.c_str (), strerror (errno)));
730 return SECFailure;
731 }
732
733 int fd = fileno (inFile);
734 struct stat info;
735 int rc = fstat (fd, &info);
736 if (rc != 0)
737 {
738 nsscommon_error (_F("Could not obtain information about certificate file %s\n%s",
739 inFileName.c_str (), strerror (errno)));
740 fclose (inFile);
741 return SECFailure;
742 }
743
744 SECItem certDER;
745 certDER.len = info.st_size;
746 certDER.data = (unsigned char *)PORT_Alloc (certDER.len);
747 size_t read = fread (certDER.data, 1, certDER.len, inFile);
748 fclose (inFile);
749 if (read != certDER.len)
750 {
751 nsscommon_error (_F("Error reading from certificate file %s\n%s",
752 inFileName.c_str (), strerror (errno)));
753 return SECFailure;
754 }
755
756 // See if the database already exists and can be initialized.
dff24561 757 SECStatus secStatus = nssInit (db_path.c_str (), 1/*readwrite*/, 0/*issueMessage*/);
aeb9cc10
DB
758 if (secStatus != SECSuccess)
759 {
760 // Try again with a fresh database.
761 if (clean_cert_db (db_path.c_str ()) != 0)
762 {
763 // Message already issued.
764 return SECFailure;
765 }
766
767 // Make sure the given path exists.
768 if (create_client_cert_db (db_path.c_str ()) != 0)
769 {
770 nsscommon_error (_F("Could not create certificate database directory %s",
771 db_path.c_str ()));
772 return SECFailure;
773 }
774
775 // Initialize the new database.
776 secStatus = nssInit (db_path.c_str (), 1/*readwrite*/);
777 if (secStatus != SECSuccess)
778 {
779 // Message already issued.
780 return SECFailure;
781 }
782 }
783
784 // Predeclare these to keep C++ happy about jumps to the label 'done'.
785 CERTCertificate *cert = 0;
786 CERTCertDBHandle *handle = 0;
787 CERTCertTrust *trust = 0;
788 PK11SlotInfo *slot = 0;
789
790 // Add the cert to the database
791 // Decode the cert.
792 secStatus = SECFailure;
793 cert = CERT_DecodeCertFromPackage ((char *)certDER.data, certDER.len);
794 if (! cert)
795 {
796 nsscommon_error (_("Unable to decode certificate"));
797 nssError ();
798 goto done;
799 }
800
801 // We need the internal slot for this database.
802 slot = PK11_GetInternalKeySlot ();
803 if (! slot)
804 {
805 nsscommon_error (_F("Could not obtain internal key slot for certificate database %s", db_path.c_str()));
806 nssError ();
807 goto done;
808 }
809
810 // Import it into the database.
811 secStatus = PK11_ImportCert (slot, cert, CK_INVALID_HANDLE,
812 server_cert_nickname (), PR_FALSE);
813 if (secStatus != SECSuccess)
814 {
815 nsscommon_error (_F("Could not import certificate into the database at %s", db_path.c_str()));
816 nssError ();
817 goto done;
818 }
819
820 // Make it a trusted SSL peer.
821 trust = (CERTCertTrust *)PORT_ZAlloc (sizeof (CERTCertTrust));
822 if (! trust)
823 {
824 nsscommon_error (_("Could not allocate certificate trust"));
825 goto done;
826 }
827
828 secStatus = CERT_DecodeTrustString (trust, "P,P,P");
829 if (secStatus != SECSuccess)
830 {
831 nsscommon_error (_("Unable decode trust string 'P,P,P'"));
832 nssError ();
833 goto done;
834 }
835
836 handle = CERT_GetDefaultCertDB ();
837 assert (handle);
838 secStatus = CERT_ChangeCertTrust (handle, cert, trust);
839 if (secStatus != SECSuccess)
840 {
841 nsscommon_error (_("Unable to change certificate trust"));
842 nssError ();
843 }
844
845 done:
846 if (slot)
847 PK11_FreeSlot (slot);
848 if (trust)
849 PORT_Free (trust);
850 if (cert)
851 CERT_DestroyCertificate (cert);
852 if (certDER.data)
853 PORT_Free (certDER.data);
854 nssCleanup (db_path.c_str ());
855 return secStatus;
856}
857
858int
859gen_cert_db (const string &db_path, const string &extraDnsNames, bool use_password)
860{
861 // Log the generation of a new database.
862 log (_F("Generating a new certificate database directory in %s",
863 db_path.c_str ()));
864
865 // Start with a clean cert database.
866 if (clean_cert_db (db_path.c_str ()) != 0)
867 {
868 // Message already issued.
869 return 1;
870 }
871
872 // Make sure the given path exists.
873 if (create_server_cert_db (db_path.c_str ()) != 0)
874 {
875 nsscommon_error (_F("Could not create certificate database directory %s",
876 db_path.c_str ()));
877 return 1;
878 }
879
880 // Initialize the new database.
881 SECStatus secStatus = nssInit (db_path.c_str (), 1/*readwrite*/);
882 if (secStatus != SECSuccess)
883 {
884 // Message already issued.
885 return 1;
886 }
887
888 // Pre declare these to keep g++ happy about jumps to the label 'error'.
889 CERTName *subject = 0;
890 SECKEYPublicKey *pubkey = 0;
891 SECKEYPrivateKey *privkey = 0;
892 CERTCertificateRequest *cr = 0;
893 CERTCertificate *cert = 0;
894 SECItem *certDER = 0;
895 string dnsNames;
896 int rc;
897 string outFileName;
898 FILE *outFile = 0;
899
900 // We need the internal slot for this database.
901 PK11SlotInfo *slot = PK11_GetInternalKeySlot ();
902 if (! slot)
903 {
904 nsscommon_error (_F("Could not obtain internal key slot for certificate database %s", db_path.c_str()));
905 nssError ();
906 goto error;
907 }
908
909 // Establish the password (if any) for the new database.
910 rc = init_password (slot, db_path, use_password);
911 if (rc != 0)
912 {
913 // Messages already issued.
914 goto error;
915 }
916
917 // Format the cert subject.
918 subject = CERT_AsciiToName ((char *)"CN=Systemtap Compile Server, OU=Systemtap");
919 if (! subject)
920 {
921 nsscommon_error (_("Unable to encode certificate common header"));
922 nssError ();
923 goto error;
924 }
925
926 // Next, generate the private key.
927 privkey = generate_private_key (db_path, slot, & pubkey);
928 if (! privkey)
929 {
930 // Message already issued.
931 goto error;
932 }
933
934 // Next, generate a cert request.
935 cr = generate_cert_request (pubkey, subject);
936 if (! cr)
937 {
938 // Message already issued.
939 goto error;
940 }
941
942 // Now, generate the cert. We need our host name and the supplied additional dns names (if any).
943 struct utsname utsname;
944 uname (& utsname);
945 dnsNames = utsname.nodename;
946 if (! extraDnsNames.empty ())
947 dnsNames += "," + extraDnsNames;
948 cert = create_cert (cr, dnsNames);
949 CERT_DestroyCertificateRequest (cr);
950 if (! cert)
951 {
952 // NSS error already issued.
953 nsscommon_error (_("Unable to create certificate"));
954 goto error;
955 }
956
957 // Sign the cert.
958 certDER = sign_cert (cert, privkey);
959 if (! certDER)
960 {
961 // Message already issued.
962 goto error;
963 }
964
965 // Now output it to a file.
966 outFileName = db_path + "/stap.cert";
967 outFile = fopen (outFileName.c_str (), "wb");
968 if (outFile)
969 {
970 size_t written = fwrite (certDER->data, 1, certDER->len, outFile);
971 if (written != certDER->len)
972 {
973 nsscommon_error (_F("Error writing to certificate file %s\n%s",
974 outFileName.c_str (), strerror (errno)));
975 }
976 fclose (outFile);
977 }
978 else
979 {
980 nsscommon_error (_F("Could not open certificate file %s for writing\n%s",
981 outFileName.c_str (), strerror (errno)));
982 }
983
984 // Add the cert to the database
985 secStatus = add_server_cert (db_path, certDER, slot);
986 CERT_DestroyCertificate (cert);
987 if (secStatus != SECSuccess)
988 {
989 // NSS error already issued.
990 nsscommon_error (_F("Unable to add certificate to %s", db_path.c_str ()));
991 goto error;
992 }
993
994 // Done with the certificate database
995 PK11_FreeSlot (slot);
996 CERT_DestroyName (subject);
997 SECKEY_DestroyPublicKey (pubkey);
998 SECKEY_DestroyPrivateKey (privkey);
999 goto done;
1000
1001 error:
1002 if (slot)
1003 PK11_FreeSlot (slot);
1004 if (subject)
1005 CERT_DestroyName (subject);
1006 if (pubkey)
1007 SECKEY_DestroyPublicKey (pubkey);
1008 if (privkey)
1009 SECKEY_DestroyPrivateKey (privkey);
1010 if (cert)
1011 CERT_DestroyCertificate (cert); // Also destroys certDER.
1012
1013 done:
1014 nssCleanup (db_path.c_str ());
1015 return secStatus != SECSuccess;
1016}
1017
1018CERTCertList *get_cert_list_from_db (const string &cert_nickname)
1019{
1020 // Search the client-side database of trusted servers.
1021 CERTCertDBHandle *handle = CERT_GetDefaultCertDB ();
1022 assert (handle);
1023 CERTCertificate *db_cert = PK11_FindCertFromNickname (cert_nickname.c_str (), 0);
1024 if (! db_cert)
1025 {
1026 // No trusted servers. Not an error. Just an empty list returned.
1027 return 0;
1028 }
1029
1030 // Here, we have one cert with the desired nickname.
1031 // Now, we will attempt to get a list of ALL certs
1032 // with the same subject name as the cert we have. That list
1033 // should contain, at a minimum, the one cert we have already found.
1034 // If the list of certs is empty (0), the libraries have failed.
1035 CERTCertList *certs = CERT_CreateSubjectCertList (0, handle, & db_cert->derSubject,
1036 PR_Now (), PR_FALSE);
1037 CERT_DestroyCertificate (db_cert);
1038 if (! certs)
1039 {
1040 nsscommon_error (_("NSS library failure in CERT_CreateSubjectCertList"));
1041 nssError ();
1042 }
1043
1044 return certs;
1045}
1046
1047string get_cert_serial_number (const CERTCertificate *cert)
1048{
1049 ostringstream serialNumber;
1050 serialNumber << hex << setfill('0') << right;
1051 for (unsigned i = 0; i < cert->serialNumber.len; ++i)
1052 {
1053 if (i > 0)
1054 serialNumber << ':';
1055 serialNumber << setw(2) << (unsigned)cert->serialNumber.data[i];
1056 }
1057 return serialNumber.str ();
1058}
1059
1060static int
1061format_cert_validity_time (SECItem &vTime, char *timeString, size_t ts_size)
1062{
1063 int64 time;
1064 SECStatus secStatus;
1065
1066 switch (vTime.type) {
1067 case siUTCTime:
1068 secStatus = DER_UTCTimeToTime (& time, & vTime);
1069 break;
1070 case siGeneralizedTime:
1071 secStatus = DER_GeneralizedTimeToTime (& time, & vTime);
1072 break;
1073 default:
eeacd9d4 1074 nsscommon_error (_("Could not decode certificate validity"));
aeb9cc10
DB
1075 return 1;
1076 }
1077 if (secStatus != SECSuccess)
1078 {
eeacd9d4 1079 nsscommon_error (_("Could not decode certificate validity time"));
aeb9cc10
DB
1080 return 1;
1081 }
1082
1083 // Convert to local time.
1084 PRExplodedTime printableTime;
1085 PR_ExplodeTime (time, PR_GMTParameters, & printableTime);
1086 if (! PR_FormatTime (timeString, ts_size, "%a %b %d %H:%M:%S %Y", & printableTime))
1087 {
eeacd9d4 1088 nsscommon_error (_("Could not format certificate validity time"));
aeb9cc10
DB
1089 return 1;
1090 }
1091
1092 return 0;
1093}
1094
1095static bool
1096cert_is_valid (CERTCertificate *cert)
1097{
1098 // Verify the the certificate is valid as an SSL server and as an object signer and that
1099 // it is valid now.
1100 CERTCertDBHandle *handle = CERT_GetDefaultCertDB ();
1101 assert (handle);
1102 SECCertificateUsage usage = certificateUsageSSLServer | certificateUsageObjectSigner;
1103 SECStatus secStatus = CERT_VerifyCertificate (handle, cert, PR_TRUE/*checkSig*/, usage,
1104 PR_Now (), NULL, NULL/*log*/, & usage);
1105 return secStatus == SECSuccess;
1106}
1107
1108static bool
1109cert_db_is_valid (const string &db_path, const string &nss_cert_name)
1110{
1111 // Make sure the given path exists.
1112 if (! file_exists (db_path))
1113 {
1114 log (_F("Certificate database %s does not exist", db_path.c_str ()));
1115 return false;
1116 }
1117
1118 // If a 'pw' file exists, then this is an old database. Treat any certs as invalid.
1119 if (file_exists (db_path + "/pw"))
1120 {
1121 log (_F("Certificate database %s is obsolete", db_path.c_str ()));
1122 return false;
1123 }
1124
1125 // Initialize the NSS libraries -- readonly
1126 SECStatus secStatus = nssInit (db_path.c_str ());
1127 if (secStatus != SECSuccess)
1128 {
1129 // Message already issued.
1130 return false;
1131 }
1132
1133 // Obtain a list of our certs from the database.
1134 bool valid_p = false;
1135 CERTCertList *certs = get_cert_list_from_db (nss_cert_name);
1136 if (! certs)
1137 {
1138 log (_F("No certificate found in database %s", db_path.c_str ()));
1139 goto done;
1140 }
1141
1142 log (_F("Certificate found in database %s", db_path.c_str ()));
1143 for (CERTCertListNode *node = CERT_LIST_HEAD (certs);
1144 ! CERT_LIST_END (node, certs);
1145 node = CERT_LIST_NEXT (node))
1146 {
1147 // The certificate we're working with.
1148 CERTCertificate *c = node->cert;
1149
1150 // Print the validity dates of the certificate.
1151 CERTValidity &v = c->validity;
1152 char timeString[256];
1153 if (format_cert_validity_time (v.notBefore, timeString, sizeof (timeString)) == 0)
1154 log (_F(" Not Valid Before: %s", timeString));
1155 if (format_cert_validity_time (v.notAfter, timeString, sizeof (timeString)) == 0)
1156 log (_F(" Not Valid After: %s", timeString));
1157
1158 // Now ask NSS to check the validity.
1159 if (cert_is_valid (c))
1160 {
1161 // The cert is valid. One valid cert is enough.
1162 log (_(" Certificate is valid"));
1163 valid_p = true;
1164 break;
1165 }
1166
1167 // The cert is not valid. Look for another one.
1168 log (_(" Certificate is not valid"));
1169 }
1170 CERT_DestroyCertList (certs);
1171
1172 done:
1173 nssCleanup (db_path.c_str ());
1174 return valid_p;
1175}
1176
1177// Ensure that our certificate exists and is valid. Generate a new one if not.
1178int
1179check_cert (const string &db_path, const string &nss_cert_name, bool use_db_password)
1180{
1181 // Generate a new cert database if the current one does not exist or is not valid.
1182 if (! cert_db_is_valid (db_path, nss_cert_name))
1183 {
1184 if (gen_cert_db (db_path, "", use_db_password) != 0)
1185 {
1186 // NSS message already issued.
1187 nsscommon_error (_("Unable to generate new certificate"));
1188 return 1;
1189 }
1190 }
1191 return 0;
1192}
1193
1194void sign_file (
1195 const string &db_path,
1196 const string &nss_cert_name,
1197 const string &inputName,
1198 const string &outputName
1199) {
1200 /* Get own certificate and private key. */
1201 CERTCertificate *cert = PK11_FindCertFromNickname (nss_cert_name.c_str (), NULL);
1202 if (cert == NULL)
1203 {
1204 nsscommon_error (_F("Unable to find certificate with nickname %s in %s.",
1205 nss_cert_name.c_str (), db_path.c_str()));
1206 nssError ();
1207 return;
1208 }
1209
1210 // Predeclare these to keep C++ happy abount branches to 'done'.
1211 unsigned char buffer[4096];
63a1cb66 1212 PRFileDesc *local_file_fd = NULL;
aeb9cc10
DB
1213 PRInt32 numBytes;
1214 SECStatus secStatus;
1215 SGNContext *sgn;
1216 SECItem signedData;
1217
1218 /* db_path.c_str () gets passed to nssPasswordCallback */
1219 SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert (cert, (void *)db_path.c_str ());
1220 if (privKey == NULL)
1221 {
1222 nsscommon_error (_F("Unable to obtain private key from the certificate with nickname %s in %s.",
1223 nss_cert_name.c_str (), db_path.c_str()));
1224 nssError ();
1225 goto done;
1226 }
1227
1228 /* Sign the file. */
1229 /* Set up the signing context. */
1230 sgn = SGN_NewContext (SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, privKey);
1231 if (! sgn)
1232 {
1233 nsscommon_error (_("Could not create signing context"));
1234 nssError ();
1235 return;
1236 }
1237 secStatus = SGN_Begin (sgn);
1238 if (secStatus != SECSuccess)
1239 {
1240 nsscommon_error (_("Could not initialize signing context."));
1241 nssError ();
1242 return;
1243 }
1244
1245 /* Now read the data and add it to the signature. */
1246 local_file_fd = PR_Open (inputName.c_str(), PR_RDONLY, 0);
1247 if (local_file_fd == NULL)
1248 {
1249 nsscommon_error (_F("Could not open module file %s", inputName.c_str ()));
1250 nssError ();
1251 return;
1252 }
1253
1254 for (;;)
1255 {
1256 numBytes = PR_Read (local_file_fd, buffer, sizeof (buffer));
1257 if (numBytes == 0)
1258 break; /* EOF */
1259
1260 if (numBytes < 0)
1261 {
1262 nsscommon_error (_F("Error reading module file %s", inputName.c_str ()));
1263 nssError ();
1264 goto done;
1265 }
1266
1267 /* Add the data to the signature. */
1268 secStatus = SGN_Update (sgn, buffer, numBytes);
1269 if (secStatus != SECSuccess)
1270 {
1271 nsscommon_error (_F("Error while signing module file %s", inputName.c_str ()));
1272 nssError ();
1273 goto done;
1274 }
1275 }
1276
1277 /* Complete the signature. */
1278 secStatus = SGN_End (sgn, & signedData);
1279 if (secStatus != SECSuccess)
1280 {
1281 nsscommon_error (_F("Could not complete signature of module file %s", inputName.c_str ()));
1282 nssError ();
1283 goto done;
1284 }
1285
1286 SGN_DestroyContext (sgn, PR_TRUE);
1287
1288 /* Now write the signed data to the output file. */
93ea565a
LB
1289 if(local_file_fd != NULL)
1290 PR_Close (local_file_fd);
aeb9cc10
DB
1291 local_file_fd = PR_Open (outputName.c_str(), PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
1292 PR_IRUSR | PR_IWUSR | PR_IRGRP | PR_IWGRP | PR_IROTH);
1293 if (local_file_fd == NULL)
1294 {
1295 nsscommon_error (_F("Could not open signature file %s", outputName.c_str ()));
1296 nssError ();
1297 goto done;
1298 }
1299
1300 numBytes = PR_Write (local_file_fd, signedData.data, signedData.len);
1301 if (numBytes < 0 || numBytes != (PRInt32)signedData.len)
1302 {
1303 nsscommon_error (_F("Error writing to signature file %s", outputName.c_str ()));
1304 nssError ();
1305 }
1306
1307 done:
1308 if (privKey)
1309 SECKEY_DestroyPrivateKey (privKey);
1310 CERT_DestroyCertificate (cert);
97b1f2ee
LB
1311 if(local_file_fd != NULL)
1312 PR_Close (local_file_fd);
aeb9cc10
DB
1313}
1314
1315/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.137101 seconds and 5 git commands to generate.