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