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