shvfy.c (17891B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #ifdef FREEBL_NO_DEPEND 6 #include "stubs.h" 7 #endif 8 9 #include "shsign.h" 10 #include "prlink.h" 11 #include "prio.h" 12 #include "blapi.h" 13 #include "seccomon.h" 14 #include "secerr.h" 15 #include "stdio.h" 16 #include "prmem.h" 17 #include "hasht.h" 18 #include "pqg.h" 19 #include "blapii.h" 20 #include "secitem.h" 21 #include "pkcs11t.h" 22 23 #ifndef NSS_FIPS_DISABLED 24 25 /* 26 * Most modern version of Linux support a speed optimization scheme where an 27 * application called prelink modifies programs and shared libraries to quickly 28 * load if they fit into an already designed address space. In short, prelink 29 * scans the list of programs and libraries on your system, assigns them a 30 * predefined space in the the address space, then provides the fixups to the 31 * library. 32 33 * The modification of the shared library is correctly detected by the freebl 34 * FIPS checksum scheme where we check a signed hash of the library against the 35 * library itself. 36 * 37 * The prelink command itself can reverse the process of modification and 38 * output the prestine shared library as it was before prelink made it's 39 * changes. If FREEBL_USE_PRELINK is set Freebl uses prelink to output the 40 * original copy of the shared library before prelink modified it. 41 */ 42 #ifdef FREEBL_USE_PRELINK 43 #ifndef FREELB_PRELINK_COMMAND 44 #define FREEBL_PRELINK_COMMAND "/usr/sbin/prelink -u -o -" 45 #endif 46 #include "private/pprio.h" 47 48 #include <stdlib.h> 49 #include <unistd.h> 50 #include <fcntl.h> 51 #include <sys/wait.h> 52 #include <sys/stat.h> 53 54 /* 55 * This function returns an NSPR PRFileDesc * which the caller can read to 56 * obtain the prestine value of the shared library, before any OS related 57 * changes to it (usually address fixups). 58 * 59 * If prelink is installed, this 60 * file descriptor is a pipe connecting the output of 61 * /usr/sbin/prelink -u -o - {Library} 62 * and *pid returns the process id of the prelink child. 63 * 64 * If prelink is not installed, it returns a normal readonly handle to the 65 * library itself and *pid is set to '0'. 66 */ 67 PRFileDesc * 68 bl_OpenUnPrelink(const char *shName, int *pid) 69 { 70 char *command = strdup(FREEBL_PRELINK_COMMAND); 71 char *argString = NULL; 72 char **argv = NULL; 73 char *shNameArg = NULL; 74 char *cp; 75 pid_t child; 76 int argc = 0, argNext = 0; 77 struct stat statBuf; 78 int pipefd[2] = { -1, -1 }; 79 int ret; 80 81 *pid = 0; 82 83 /* make sure the prelink command exists first. If not, fall back to 84 * just reading the file */ 85 for (cp = command; *cp; cp++) { 86 if (*cp == ' ') { 87 *cp++ = 0; 88 argString = cp; 89 break; 90 } 91 } 92 memset(&statBuf, 0, sizeof(statBuf)); 93 /* stat the file, follow the link */ 94 ret = stat(command, &statBuf); 95 if (ret < 0) { 96 free(command); 97 return PR_Open(shName, PR_RDONLY, 0); 98 } 99 /* file exits, make sure it's an executable */ 100 if (!S_ISREG(statBuf.st_mode) || 101 ((statBuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)) { 102 free(command); 103 return PR_Open(shName, PR_RDONLY, 0); 104 } 105 106 /* OK, the prelink command exists and looks correct, use it */ 107 /* build the arglist while we can still malloc */ 108 /* count the args if any */ 109 if (argString && *argString) { 110 /* argString may have leading spaces, strip them off*/ 111 for (cp = argString; *cp && *cp == ' '; cp++) 112 ; 113 argString = cp; 114 if (*cp) { 115 /* there is at least one arg.. */ 116 argc = 1; 117 } 118 119 /* count the rest: Note there is no provision for escaped 120 * spaces here */ 121 for (cp = argString; *cp; cp++) { 122 if (*cp == ' ') { 123 while (*cp && *cp == ' ') 124 cp++; 125 if (*cp) 126 argc++; 127 } 128 } 129 } 130 131 /* add the additional args: argv[0] (command), shName, NULL*/ 132 argc += 3; 133 argv = PORT_NewArray(char *, argc); 134 if (argv == NULL) { 135 goto loser; 136 } 137 138 /* fill in the arglist */ 139 argv[argNext++] = command; 140 if (argString && *argString) { 141 argv[argNext++] = argString; 142 for (cp = argString; *cp; cp++) { 143 if (*cp == ' ') { 144 *cp++ = 0; 145 while (*cp && *cp == ' ') 146 cp++; 147 if (*cp) 148 argv[argNext++] = cp; 149 } 150 } 151 } 152 /* exec doesn't advertise taking const char **argv, do the paranoid 153 * copy */ 154 shNameArg = strdup(shName); 155 if (shNameArg == NULL) { 156 goto loser; 157 } 158 argv[argNext++] = shNameArg; 159 argv[argNext++] = 0; 160 161 ret = pipe(pipefd); 162 if (ret < 0) { 163 goto loser; 164 } 165 166 /* use vfork() so we don't trigger the pthread_at_fork() handlers */ 167 child = vfork(); 168 if (child < 0) 169 goto loser; 170 if (child == 0) { 171 /* set up the file descriptors */ 172 /* if we need to support BSD, this will need to be an open of 173 * /dev/null and dup2(nullFD, 0)*/ 174 close(0); 175 /* associate pipefd[1] with stdout */ 176 if (pipefd[1] != 1) 177 dup2(pipefd[1], 1); 178 close(2); 179 close(pipefd[0]); 180 /* should probably close the other file descriptors? */ 181 182 execv(command, argv); 183 /* avoid at_exit() handlers */ 184 _exit(1); /* shouldn't reach here except on an error */ 185 } 186 close(pipefd[1]); 187 pipefd[1] = -1; 188 189 /* this is safe because either vfork() as full fork() semantics, and thus 190 * already has it's own address space, or because vfork() has paused 191 * the parent util the exec or exit */ 192 free(command); 193 free(shNameArg); 194 PORT_Free(argv); 195 196 *pid = child; 197 198 return PR_ImportPipe(pipefd[0]); 199 200 loser: 201 if (pipefd[0] != -1) { 202 close(pipefd[0]); 203 } 204 if (pipefd[1] != -1) { 205 close(pipefd[1]); 206 } 207 free(command); 208 free(shNameArg); 209 PORT_Free(argv); 210 211 return NULL; 212 } 213 214 /* 215 * bl_CloseUnPrelink - 216 * 217 * This closes the file descripter and reaps and children openned and crated by 218 * b;_OpenUnprelink. It's primary difference between it and just close is 219 * that it calls wait on the pid if one is supplied, preventing zombie children 220 * from hanging around. 221 */ 222 void 223 bl_CloseUnPrelink(PRFileDesc *file, int pid) 224 { 225 /* close the file descriptor */ 226 PR_Close(file); 227 /* reap the child */ 228 if (pid) { 229 waitpid(pid, NULL, 0); 230 } 231 } 232 #endif 233 234 /* #define DEBUG_SHVERIFY 1 */ 235 236 static char * 237 mkCheckFileName(const char *libName) 238 { 239 int ln_len = PORT_Strlen(libName); 240 int index = ln_len + 1 - sizeof("." SHLIB_SUFFIX); 241 char *output = PORT_Alloc(ln_len + sizeof(SGN_SUFFIX)); 242 if (!output) { 243 PORT_SetError(SEC_ERROR_NO_MEMORY); 244 return NULL; 245 } 246 247 if ((index > 0) && 248 (PORT_Strncmp(&libName[index], 249 "." SHLIB_SUFFIX, sizeof("." SHLIB_SUFFIX)) == 0)) { 250 ln_len = index; 251 } 252 PORT_Memcpy(output, libName, ln_len); 253 PORT_Memcpy(&output[ln_len], SGN_SUFFIX, sizeof(SGN_SUFFIX)); 254 return output; 255 } 256 257 static int 258 decodeInt(unsigned char *buf) 259 { 260 return (buf[3]) | (buf[2] << 8) | (buf[1] << 16) | (buf[0] << 24); 261 } 262 263 static SECStatus 264 readItem(PRFileDesc *fd, SECItem *item) 265 { 266 unsigned char buf[4]; 267 int bytesRead; 268 269 bytesRead = PR_Read(fd, buf, 4); 270 if (bytesRead != 4) { 271 return SECFailure; 272 } 273 item->len = decodeInt(buf); 274 275 item->data = PORT_Alloc(item->len); 276 if (item->data == NULL) { 277 item->len = 0; 278 return SECFailure; 279 } 280 bytesRead = PR_Read(fd, item->data, item->len); 281 if (bytesRead != item->len) { 282 PORT_Free(item->data); 283 item->data = NULL; 284 item->len = 0; 285 return SECFailure; 286 } 287 return SECSuccess; 288 } 289 290 static PRBool blapi_SHVerifyFile(const char *shName, PRBool self, PRBool rerun); 291 292 static PRBool 293 blapi_SHVerify(const char *name, PRFuncPtr addr, PRBool self, PRBool rerun) 294 { 295 PRBool result = PR_FALSE; /* if anything goes wrong, 296 * the signature does not verify */ 297 /* find our shared library name */ 298 char *shName = PR_GetLibraryFilePathname(name, addr); 299 if (!shName) { 300 goto loser; 301 } 302 result = blapi_SHVerifyFile(shName, self, rerun); 303 304 loser: 305 if (shName != NULL) { 306 PR_Free(shName); 307 } 308 309 return result; 310 } 311 312 PRBool 313 BLAPI_SHVerify(const char *name, PRFuncPtr addr) 314 { 315 PRBool rerun = PR_FALSE; 316 if (name && *name == BLAPI_FIPS_RERUN_FLAG) { 317 name++; 318 rerun = PR_TRUE; 319 } 320 return blapi_SHVerify(name, addr, PR_FALSE, rerun); 321 } 322 323 PRBool 324 BLAPI_SHVerifyFile(const char *shName) 325 { 326 PRBool rerun = PR_FALSE; 327 if (shName && *shName == BLAPI_FIPS_RERUN_FLAG) { 328 shName++; 329 rerun = PR_TRUE; 330 } 331 return blapi_SHVerifyFile(shName, PR_FALSE, rerun); 332 } 333 334 #ifndef NSS_STRICT_INTEGRITY 335 /* This allows checks with old shlibsign .chk files. If NSS_STRICT_INTEGRITY 336 * is set, we don't accept DSA */ 337 static PRBool 338 blapi_SHVerifyDSACheck(PRFileDesc *shFD, const SECHashObject *hashObj, 339 DSAPublicKey *key, const SECItem *signature) 340 { 341 void *hashcx = NULL; 342 SECItem hash; 343 int bytesRead; 344 unsigned char hashBuf[HASH_LENGTH_MAX]; 345 unsigned char buf[4096]; 346 SECStatus rv; 347 348 hash.type = siBuffer; 349 hash.data = hashBuf; 350 hash.len = sizeof(hashBuf); 351 352 /* hash our library file */ 353 hashcx = hashObj->create(); 354 if (hashcx == NULL) { 355 return PR_FALSE; 356 } 357 hashObj->begin(hashcx); 358 359 while ((bytesRead = PR_Read(shFD, buf, sizeof(buf))) > 0) { 360 hashObj->update(hashcx, buf, bytesRead); 361 } 362 hashObj->end(hashcx, hash.data, &hash.len, hash.len); 363 hashObj->destroy(hashcx, PR_TRUE); 364 365 /* verify the hash against the check file */ 366 rv = DSA_VerifyDigest(key, signature, &hash); 367 PORT_SafeZero(hashBuf, sizeof hashBuf); 368 return (rv == SECSuccess) ? PR_TRUE : PR_FALSE; 369 } 370 #endif 371 372 #ifdef NSS_STRICT_INTEGRITY 373 /* don't allow MD2, MD5, SHA1 or SHA224 as your integrity hash */ 374 static PRBool 375 blapi_HashAllowed(SECHashObject *hashObj) 376 { 377 switch (hashObj->type) { 378 case HASH_AlgSHA256: 379 case HASH_AlgSHA384: 380 case HASH_AlgSHA512: 381 return PR_TRUE; 382 default: 383 break; 384 } 385 return PR_FALSE; 386 } 387 #endif 388 389 static PRBool 390 blapi_SHVerifyHMACCheck(PRFileDesc *shFD, const SECHashObject *hashObj, 391 const SECItem *key, const SECItem *signature) 392 { 393 HMACContext *hmaccx = NULL; 394 SECItem hash; 395 int bytesRead; 396 unsigned char hashBuf[HASH_LENGTH_MAX]; 397 unsigned char buf[4096]; 398 SECStatus rv; 399 PRBool result = PR_FALSE; 400 401 #ifdef NSS_STRICT_INTEGRITY 402 if (!blapi_HashAllowed(hashObj)) { 403 return PR_FALSE; 404 } 405 #endif 406 407 hash.type = siBuffer; 408 hash.data = hashBuf; 409 hash.len = hashObj->length; 410 411 /* create an hmac for the library file */ 412 hmaccx = HMAC_Create(hashObj, key->data, key->len, PR_TRUE); 413 if (hmaccx == NULL) { 414 return PR_FALSE; 415 } 416 HMAC_Begin(hmaccx); 417 418 while ((bytesRead = PR_Read(shFD, buf, sizeof(buf))) > 0) { 419 HMAC_Update(hmaccx, buf, bytesRead); 420 } 421 rv = HMAC_Finish(hmaccx, hash.data, &hash.len, hash.len); 422 423 HMAC_Destroy(hmaccx, PR_TRUE); 424 425 /* verify the hmac against the check file */ 426 if (rv == SECSuccess) { 427 result = SECITEM_ItemsAreEqual(signature, &hash); 428 } 429 PORT_SafeZero(hashBuf, sizeof hashBuf); 430 return result; 431 } 432 433 static PRBool 434 blapi_SHVerifyFile(const char *shName, PRBool self, PRBool rerun) 435 { 436 char *checkName = NULL; 437 PRFileDesc *checkFD = NULL; 438 PRFileDesc *shFD = NULL; 439 const SECHashObject *hashObj = NULL; 440 SECItem signature = { 0, NULL, 0 }; 441 int bytesRead, offset, type; 442 SECStatus rv; 443 SECItem hmacKey = { 0, NULL, 0 }; 444 #ifdef FREEBL_USE_PRELINK 445 int pid = 0; 446 #endif 447 PRBool result = PR_FALSE; /* if anything goes wrong, 448 * the signature does not verify */ 449 NSSSignChkHeader header; 450 #ifndef NSS_STRICT_INTEGRITY 451 DSAPublicKey key; 452 453 PORT_SafeZero(&key, sizeof(key)); 454 #endif 455 456 /* If our integrity check was never ran or failed, fail any other 457 * integrity checks to prevent any token going into FIPS mode. */ 458 if (!self && (BL_FIPSEntryOK(PR_FALSE, rerun) != SECSuccess)) { 459 return PR_FALSE; 460 } 461 462 if (!shName) { 463 goto loser; 464 } 465 466 /* figure out the name of our check file */ 467 checkName = mkCheckFileName(shName); 468 if (!checkName) { 469 goto loser; 470 } 471 472 /* open the check File */ 473 checkFD = PR_Open(checkName, PR_RDONLY, 0); 474 if (checkFD == NULL) { 475 #ifdef DEBUG_SHVERIFY 476 fprintf(stderr, "Failed to open the check file %s: (%d, %d)\n", 477 checkName, (int)PR_GetError(), (int)PR_GetOSError()); 478 #endif /* DEBUG_SHVERIFY */ 479 goto loser; 480 } 481 482 /* read and Verify the headerthe header */ 483 bytesRead = PR_Read(checkFD, &header, sizeof(header)); 484 if (bytesRead != sizeof(header)) { 485 goto loser; 486 } 487 if ((header.magic1 != NSS_SIGN_CHK_MAGIC1) || 488 (header.magic2 != NSS_SIGN_CHK_MAGIC2)) { 489 goto loser; 490 } 491 /* we've bumped the version number so that newly signed .check 492 * files will fail nicely on old version of nss */ 493 if (header.majorVersion > NSS_SIGN_CHK_MAJOR_VERSION) { 494 goto loser; 495 } 496 if (header.minorVersion < NSS_SIGN_CHK_MINOR_VERSION) { 497 goto loser; 498 } 499 type = decodeInt(header.type); 500 501 /* seek past any future header extensions */ 502 offset = decodeInt(header.offset); 503 if (PR_Seek(checkFD, offset, PR_SEEK_SET) < 0) { 504 goto loser; 505 } 506 507 switch (type) { 508 case CKK_DSA: 509 #ifdef NSS_STRICT_INTEGRITY 510 goto loser; 511 #else 512 /* accept old dsa check files if NSS_STRICT_INTEGRITY is not set*/ 513 /* read the key */ 514 rv = readItem(checkFD, &key.params.prime); 515 if (rv != SECSuccess) { 516 goto loser; 517 } 518 rv = readItem(checkFD, &key.params.subPrime); 519 if (rv != SECSuccess) { 520 goto loser; 521 } 522 rv = readItem(checkFD, &key.params.base); 523 if (rv != SECSuccess) { 524 goto loser; 525 } 526 rv = readItem(checkFD, &key.publicValue); 527 if (rv != SECSuccess) { 528 goto loser; 529 } 530 /* read the signature */ 531 rv = readItem(checkFD, &signature); 532 if (rv != SECSuccess) { 533 goto loser; 534 } 535 hashObj = HASH_GetRawHashObject(PQG_GetHashType(&key.params)); 536 break; 537 #endif 538 default: 539 if ((type & NSS_SIGN_CHK_TYPE_FLAGS) != NSS_SIGN_CHK_FLAG_HMAC) { 540 goto loser; 541 } 542 /* read the HMAC Key */ 543 rv = readItem(checkFD, &hmacKey); 544 if (rv != SECSuccess) { 545 goto loser; 546 } 547 /* read the siganture */ 548 rv = readItem(checkFD, &signature); 549 if (rv != SECSuccess) { 550 goto loser; 551 } 552 hashObj = HASH_GetRawHashObject(type & ~NSS_SIGN_CHK_TYPE_FLAGS); 553 } 554 555 /* done with the check file */ 556 PR_Close(checkFD); 557 checkFD = NULL; 558 559 if (hashObj == NULL) { 560 goto loser; 561 } 562 563 /* open our library file */ 564 #ifdef FREEBL_USE_PRELINK 565 shFD = bl_OpenUnPrelink(shName, &pid); 566 #else 567 shFD = PR_Open(shName, PR_RDONLY, 0); 568 #endif 569 if (shFD == NULL) { 570 #ifdef DEBUG_SHVERIFY 571 fprintf(stderr, "Failed to open the library file %s: (%d, %d)\n", 572 shName, (int)PR_GetError(), (int)PR_GetOSError()); 573 #endif /* DEBUG_SHVERIFY */ 574 goto loser; 575 } 576 577 switch (type) { 578 case CKK_DSA: 579 #ifndef NSS_STRICT_INTEGRITY 580 result = blapi_SHVerifyDSACheck(shFD, hashObj, &key, &signature); 581 #endif 582 break; 583 default: 584 if ((type & NSS_SIGN_CHK_TYPE_FLAGS) != NSS_SIGN_CHK_FLAG_HMAC) { 585 break; 586 } 587 result = blapi_SHVerifyHMACCheck(shFD, hashObj, &hmacKey, &signature); 588 break; 589 } 590 591 #ifdef FREEBL_USE_PRELINK 592 bl_CloseUnPrelink(shFD, pid); 593 #else 594 PR_Close(shFD); 595 #endif 596 shFD = NULL; 597 598 loser: 599 PORT_SafeZero(&header, sizeof header); 600 if (checkName != NULL) { 601 PORT_Free(checkName); 602 } 603 if (checkFD != NULL) { 604 PR_Close(checkFD); 605 } 606 if (shFD != NULL) { 607 PR_Close(shFD); 608 } 609 if (hmacKey.data != NULL) { 610 SECITEM_ZfreeItem(&hmacKey, PR_FALSE); 611 } 612 if (signature.data != NULL) { 613 SECITEM_ZfreeItem(&signature, PR_FALSE); 614 } 615 #ifndef NSS_STRICT_INTEGRITY 616 if (key.params.prime.data != NULL) { 617 SECITEM_ZfreeItem(&key.params.prime, PR_FALSE); 618 } 619 if (key.params.subPrime.data != NULL) { 620 SECITEM_ZfreeItem(&key.params.subPrime, PR_FALSE); 621 } 622 if (key.params.base.data != NULL) { 623 SECITEM_ZfreeItem(&key.params.base, PR_FALSE); 624 } 625 if (key.publicValue.data != NULL) { 626 SECITEM_ZfreeItem(&key.publicValue, PR_FALSE); 627 } 628 #endif 629 return result; 630 } 631 632 PRBool 633 BLAPI_VerifySelf(const char *name) 634 { 635 if (name == NULL) { 636 /* 637 * If name is NULL, freebl is statically linked into softoken. 638 * softoken will call BLAPI_SHVerify next to verify itself. 639 */ 640 return PR_TRUE; 641 } 642 return blapi_SHVerify(name, (PRFuncPtr)decodeInt, PR_TRUE, PR_FALSE); 643 } 644 645 #else /* NSS_FIPS_DISABLED */ 646 647 PRBool 648 BLAPI_SHVerifyFile(const char *shName) 649 { 650 return PR_FALSE; 651 } 652 PRBool 653 BLAPI_SHVerify(const char *name, PRFuncPtr addr) 654 { 655 return PR_FALSE; 656 } 657 PRBool 658 BLAPI_VerifySelf(const char *name) 659 { 660 return PR_FALSE; 661 } 662 663 #endif /* NSS_FIPS_DISABLED */