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