security/nss/lib/freebl/shvfy.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #ifdef FREEBL_NO_DEPEND
michael@0 7 #include "stubs.h"
michael@0 8 #endif
michael@0 9
michael@0 10 #include "shsign.h"
michael@0 11 #include "prlink.h"
michael@0 12 #include "prio.h"
michael@0 13 #include "blapi.h"
michael@0 14 #include "seccomon.h"
michael@0 15 #include "stdio.h"
michael@0 16 #include "prmem.h"
michael@0 17 #include "hasht.h"
michael@0 18 #include "pqg.h"
michael@0 19
michael@0 20 /*
michael@0 21 * Most modern version of Linux support a speed optimization scheme where an
michael@0 22 * application called prelink modifies programs and shared libraries to quickly
michael@0 23 * load if they fit into an already designed address space. In short, prelink
michael@0 24 * scans the list of programs and libraries on your system, assigns them a
michael@0 25 * predefined space in the the address space, then provides the fixups to the
michael@0 26 * library.
michael@0 27
michael@0 28 * The modification of the shared library is correctly detected by the freebl
michael@0 29 * FIPS checksum scheme where we check a signed hash of the library against the
michael@0 30 * library itself.
michael@0 31 *
michael@0 32 * The prelink command itself can reverse the process of modification and
michael@0 33 * output the prestine shared library as it was before prelink made it's
michael@0 34 * changes. If FREEBL_USE_PRELINK is set Freebl uses prelink to output the
michael@0 35 * original copy of the shared library before prelink modified it.
michael@0 36 */
michael@0 37 #ifdef FREEBL_USE_PRELINK
michael@0 38 #ifndef FREELB_PRELINK_COMMAND
michael@0 39 #define FREEBL_PRELINK_COMMAND "/usr/sbin/prelink -u -o -"
michael@0 40 #endif
michael@0 41 #include "private/pprio.h"
michael@0 42
michael@0 43 #include <stdlib.h>
michael@0 44 #include <unistd.h>
michael@0 45 #include <fcntl.h>
michael@0 46 #include <sys/wait.h>
michael@0 47 #include <sys/stat.h>
michael@0 48
michael@0 49 /*
michael@0 50 * This function returns an NSPR PRFileDesc * which the caller can read to
michael@0 51 * obtain the prestine value of the shared library, before any OS related
michael@0 52 * changes to it (usually address fixups).
michael@0 53 *
michael@0 54 * If prelink is installed, this
michael@0 55 * file descriptor is a pipe connecting the output of
michael@0 56 * /usr/sbin/prelink -u -o - {Library}
michael@0 57 * and *pid returns the process id of the prelink child.
michael@0 58 *
michael@0 59 * If prelink is not installed, it returns a normal readonly handle to the
michael@0 60 * library itself and *pid is set to '0'.
michael@0 61 */
michael@0 62 PRFileDesc *
michael@0 63 bl_OpenUnPrelink(const char *shName, int *pid)
michael@0 64 {
michael@0 65 char *command= strdup(FREEBL_PRELINK_COMMAND);
michael@0 66 char *argString = NULL;
michael@0 67 char **argv = NULL;
michael@0 68 char *shNameArg = NULL;
michael@0 69 char *cp;
michael@0 70 pid_t child;
michael@0 71 int argc = 0, argNext = 0;
michael@0 72 struct stat statBuf;
michael@0 73 int pipefd[2] = {-1,-1};
michael@0 74 int ret;
michael@0 75
michael@0 76 *pid = 0;
michael@0 77
michael@0 78 /* make sure the prelink command exists first. If not, fall back to
michael@0 79 * just reading the file */
michael@0 80 for (cp = command; *cp ; cp++) {
michael@0 81 if (*cp == ' ') {
michael@0 82 *cp++ = 0;
michael@0 83 argString = cp;
michael@0 84 break;
michael@0 85 }
michael@0 86 }
michael@0 87 memset (&statBuf, 0, sizeof(statBuf));
michael@0 88 /* stat the file, follow the link */
michael@0 89 ret = stat(command, &statBuf);
michael@0 90 if (ret < 0) {
michael@0 91 free(command);
michael@0 92 return PR_Open(shName, PR_RDONLY, 0);
michael@0 93 }
michael@0 94 /* file exits, make sure it's an executable */
michael@0 95 if (!S_ISREG(statBuf.st_mode) ||
michael@0 96 ((statBuf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0)) {
michael@0 97 free(command);
michael@0 98 return PR_Open(shName, PR_RDONLY, 0);
michael@0 99 }
michael@0 100
michael@0 101 /* OK, the prelink command exists and looks correct, use it */
michael@0 102 /* build the arglist while we can still malloc */
michael@0 103 /* count the args if any */
michael@0 104 if (argString && *argString) {
michael@0 105 /* argString may have leading spaces, strip them off*/
michael@0 106 for (cp = argString; *cp && *cp == ' '; cp++);
michael@0 107 argString = cp;
michael@0 108 if (*cp) {
michael@0 109 /* there is at least one arg.. */
michael@0 110 argc = 1;
michael@0 111 }
michael@0 112
michael@0 113 /* count the rest: Note there is no provision for escaped
michael@0 114 * spaces here */
michael@0 115 for (cp = argString; *cp ; cp++) {
michael@0 116 if (*cp == ' ') {
michael@0 117 while (*cp && *cp == ' ') cp++;
michael@0 118 if (*cp) argc++;
michael@0 119 }
michael@0 120 }
michael@0 121 }
michael@0 122
michael@0 123 /* add the additional args: argv[0] (command), shName, NULL*/
michael@0 124 argc += 3;
michael@0 125 argv = PORT_NewArray(char *, argc);
michael@0 126 if (argv == NULL) {
michael@0 127 goto loser;
michael@0 128 }
michael@0 129
michael@0 130 /* fill in the arglist */
michael@0 131 argv[argNext++] = command;
michael@0 132 if (argString && *argString) {
michael@0 133 argv[argNext++] = argString;
michael@0 134 for (cp = argString; *cp; cp++) {
michael@0 135 if (*cp == ' ') {
michael@0 136 *cp++ = 0;
michael@0 137 while (*cp && *cp == ' ') cp++;
michael@0 138 if (*cp) argv[argNext++] = cp;
michael@0 139 }
michael@0 140 }
michael@0 141 }
michael@0 142 /* exec doesn't advertise taking const char **argv, do the paranoid
michael@0 143 * copy */
michael@0 144 shNameArg = strdup(shName);
michael@0 145 if (shNameArg == NULL) {
michael@0 146 goto loser;
michael@0 147 }
michael@0 148 argv[argNext++] = shNameArg;
michael@0 149 argv[argNext++] = 0;
michael@0 150
michael@0 151 ret = pipe(pipefd);
michael@0 152 if (ret < 0) {
michael@0 153 goto loser;
michael@0 154 }
michael@0 155
michael@0 156 /* use vfork() so we don't trigger the pthread_at_fork() handlers */
michael@0 157 child = vfork();
michael@0 158 if (child < 0) goto loser;
michael@0 159 if (child == 0) {
michael@0 160 /* set up the file descriptors */
michael@0 161 /* if we need to support BSD, this will need to be an open of
michael@0 162 * /dev/null and dup2(nullFD, 0)*/
michael@0 163 close(0);
michael@0 164 /* associate pipefd[1] with stdout */
michael@0 165 if (pipefd[1] != 1) dup2(pipefd[1], 1);
michael@0 166 close(2);
michael@0 167 close(pipefd[0]);
michael@0 168 /* should probably close the other file descriptors? */
michael@0 169
michael@0 170
michael@0 171 execv(command, argv);
michael@0 172 /* avoid at_exit() handlers */
michael@0 173 _exit(1); /* shouldn't reach here except on an error */
michael@0 174 }
michael@0 175 close(pipefd[1]);
michael@0 176 pipefd[1] = -1;
michael@0 177
michael@0 178 /* this is safe because either vfork() as full fork() semantics, and thus
michael@0 179 * already has it's own address space, or because vfork() has paused
michael@0 180 * the parent util the exec or exit */
michael@0 181 free(command);
michael@0 182 free(shNameArg);
michael@0 183 PORT_Free(argv);
michael@0 184
michael@0 185 *pid = child;
michael@0 186
michael@0 187 return PR_ImportPipe(pipefd[0]);
michael@0 188
michael@0 189 loser:
michael@0 190 if (pipefd[0] != -1) {
michael@0 191 close(pipefd[0]);
michael@0 192 }
michael@0 193 if (pipefd[1] != -1) {
michael@0 194 close(pipefd[1]);
michael@0 195 }
michael@0 196 free(command);
michael@0 197 free(shNameArg);
michael@0 198 PORT_Free(argv);
michael@0 199
michael@0 200 return NULL;
michael@0 201 }
michael@0 202
michael@0 203 /*
michael@0 204 * bl_CloseUnPrelink -
michael@0 205 *
michael@0 206 * This closes the file descripter and reaps and children openned and crated by
michael@0 207 * b;_OpenUnprelink. It's primary difference between it and just close is
michael@0 208 * that it calls wait on the pid if one is supplied, preventing zombie children
michael@0 209 * from hanging around.
michael@0 210 */
michael@0 211 void
michael@0 212 bl_CloseUnPrelink( PRFileDesc *file, int pid)
michael@0 213 {
michael@0 214 /* close the file descriptor */
michael@0 215 PR_Close(file);
michael@0 216 /* reap the child */
michael@0 217 if (pid) {
michael@0 218 waitpid(pid, NULL, 0);
michael@0 219 }
michael@0 220 }
michael@0 221 #endif
michael@0 222
michael@0 223 /* #define DEBUG_SHVERIFY 1 */
michael@0 224
michael@0 225 static char *
michael@0 226 mkCheckFileName(const char *libName)
michael@0 227 {
michael@0 228 int ln_len = PORT_Strlen(libName);
michael@0 229 char *output = PORT_Alloc(ln_len+sizeof(SGN_SUFFIX));
michael@0 230 int index = ln_len + 1 - sizeof("."SHLIB_SUFFIX);
michael@0 231
michael@0 232 if ((index > 0) &&
michael@0 233 (PORT_Strncmp(&libName[index],
michael@0 234 "."SHLIB_SUFFIX,sizeof("."SHLIB_SUFFIX)) == 0)) {
michael@0 235 ln_len = index;
michael@0 236 }
michael@0 237 PORT_Memcpy(output,libName,ln_len);
michael@0 238 PORT_Memcpy(&output[ln_len],SGN_SUFFIX,sizeof(SGN_SUFFIX));
michael@0 239 return output;
michael@0 240 }
michael@0 241
michael@0 242 static int
michael@0 243 decodeInt(unsigned char *buf)
michael@0 244 {
michael@0 245 return (buf[3]) | (buf[2] << 8) | (buf[1] << 16) | (buf[0] << 24);
michael@0 246 }
michael@0 247
michael@0 248 static SECStatus
michael@0 249 readItem(PRFileDesc *fd, SECItem *item)
michael@0 250 {
michael@0 251 unsigned char buf[4];
michael@0 252 int bytesRead;
michael@0 253
michael@0 254
michael@0 255 bytesRead = PR_Read(fd, buf, 4);
michael@0 256 if (bytesRead != 4) {
michael@0 257 return SECFailure;
michael@0 258 }
michael@0 259 item->len = decodeInt(buf);
michael@0 260
michael@0 261 item->data = PORT_Alloc(item->len);
michael@0 262 if (item->data == NULL) {
michael@0 263 item->len = 0;
michael@0 264 return SECFailure;
michael@0 265 }
michael@0 266 bytesRead = PR_Read(fd, item->data, item->len);
michael@0 267 if (bytesRead != item->len) {
michael@0 268 PORT_Free(item->data);
michael@0 269 item->data = NULL;
michael@0 270 item->len = 0;
michael@0 271 return SECFailure;
michael@0 272 }
michael@0 273 return SECSuccess;
michael@0 274 }
michael@0 275
michael@0 276 PRBool
michael@0 277 BLAPI_SHVerify(const char *name, PRFuncPtr addr)
michael@0 278 {
michael@0 279 PRBool result = PR_FALSE; /* if anything goes wrong,
michael@0 280 * the signature does not verify */
michael@0 281 /* find our shared library name */
michael@0 282 char *shName = PR_GetLibraryFilePathname(name, addr);
michael@0 283 if (!shName) {
michael@0 284 goto loser;
michael@0 285 }
michael@0 286 result = BLAPI_SHVerifyFile(shName);
michael@0 287
michael@0 288 loser:
michael@0 289 if (shName != NULL) {
michael@0 290 PR_Free(shName);
michael@0 291 }
michael@0 292
michael@0 293 return result;
michael@0 294 }
michael@0 295
michael@0 296 PRBool
michael@0 297 BLAPI_SHVerifyFile(const char *shName)
michael@0 298 {
michael@0 299 char *checkName = NULL;
michael@0 300 PRFileDesc *checkFD = NULL;
michael@0 301 PRFileDesc *shFD = NULL;
michael@0 302 void *hashcx = NULL;
michael@0 303 const SECHashObject *hashObj = NULL;
michael@0 304 SECItem signature = { 0, NULL, 0 };
michael@0 305 SECItem hash;
michael@0 306 int bytesRead, offset;
michael@0 307 SECStatus rv;
michael@0 308 DSAPublicKey key;
michael@0 309 int count;
michael@0 310 #ifdef FREEBL_USE_PRELINK
michael@0 311 int pid = 0;
michael@0 312 #endif
michael@0 313
michael@0 314 PRBool result = PR_FALSE; /* if anything goes wrong,
michael@0 315 * the signature does not verify */
michael@0 316 unsigned char buf[4096];
michael@0 317 unsigned char hashBuf[HASH_LENGTH_MAX];
michael@0 318
michael@0 319 PORT_Memset(&key,0,sizeof(key));
michael@0 320 hash.data = hashBuf;
michael@0 321 hash.len = sizeof(hashBuf);
michael@0 322
michael@0 323 if (!shName) {
michael@0 324 goto loser;
michael@0 325 }
michael@0 326
michael@0 327 /* figure out the name of our check file */
michael@0 328 checkName = mkCheckFileName(shName);
michael@0 329 if (!checkName) {
michael@0 330 goto loser;
michael@0 331 }
michael@0 332
michael@0 333 /* open the check File */
michael@0 334 checkFD = PR_Open(checkName, PR_RDONLY, 0);
michael@0 335 if (checkFD == NULL) {
michael@0 336 #ifdef DEBUG_SHVERIFY
michael@0 337 fprintf(stderr, "Failed to open the check file %s: (%d, %d)\n",
michael@0 338 checkName, (int)PR_GetError(), (int)PR_GetOSError());
michael@0 339 #endif /* DEBUG_SHVERIFY */
michael@0 340 goto loser;
michael@0 341 }
michael@0 342
michael@0 343 /* read and Verify the headerthe header */
michael@0 344 bytesRead = PR_Read(checkFD, buf, 12);
michael@0 345 if (bytesRead != 12) {
michael@0 346 goto loser;
michael@0 347 }
michael@0 348 if ((buf[0] != NSS_SIGN_CHK_MAGIC1) || (buf[1] != NSS_SIGN_CHK_MAGIC2)) {
michael@0 349 goto loser;
michael@0 350 }
michael@0 351 if ((buf[2] != NSS_SIGN_CHK_MAJOR_VERSION) ||
michael@0 352 (buf[3] < NSS_SIGN_CHK_MINOR_VERSION)) {
michael@0 353 goto loser;
michael@0 354 }
michael@0 355 #ifdef notdef
michael@0 356 if (decodeInt(&buf[8]) != CKK_DSA) {
michael@0 357 goto loser;
michael@0 358 }
michael@0 359 #endif
michael@0 360
michael@0 361 /* seek past any future header extensions */
michael@0 362 offset = decodeInt(&buf[4]);
michael@0 363 PR_Seek(checkFD, offset, PR_SEEK_SET);
michael@0 364
michael@0 365 /* read the key */
michael@0 366 rv = readItem(checkFD,&key.params.prime);
michael@0 367 if (rv != SECSuccess) {
michael@0 368 goto loser;
michael@0 369 }
michael@0 370 rv = readItem(checkFD,&key.params.subPrime);
michael@0 371 if (rv != SECSuccess) {
michael@0 372 goto loser;
michael@0 373 }
michael@0 374 rv = readItem(checkFD,&key.params.base);
michael@0 375 if (rv != SECSuccess) {
michael@0 376 goto loser;
michael@0 377 }
michael@0 378 rv = readItem(checkFD,&key.publicValue);
michael@0 379 if (rv != SECSuccess) {
michael@0 380 goto loser;
michael@0 381 }
michael@0 382 /* read the siganture */
michael@0 383 rv = readItem(checkFD,&signature);
michael@0 384 if (rv != SECSuccess) {
michael@0 385 goto loser;
michael@0 386 }
michael@0 387
michael@0 388 /* done with the check file */
michael@0 389 PR_Close(checkFD);
michael@0 390 checkFD = NULL;
michael@0 391
michael@0 392 hashObj = HASH_GetRawHashObject(PQG_GetHashType(&key.params));
michael@0 393 if (hashObj == NULL) {
michael@0 394 goto loser;
michael@0 395 }
michael@0 396
michael@0 397 /* open our library file */
michael@0 398 #ifdef FREEBL_USE_PRELINK
michael@0 399 shFD = bl_OpenUnPrelink(shName,&pid);
michael@0 400 #else
michael@0 401 shFD = PR_Open(shName, PR_RDONLY, 0);
michael@0 402 #endif
michael@0 403 if (shFD == NULL) {
michael@0 404 #ifdef DEBUG_SHVERIFY
michael@0 405 fprintf(stderr, "Failed to open the library file %s: (%d, %d)\n",
michael@0 406 shName, (int)PR_GetError(), (int)PR_GetOSError());
michael@0 407 #endif /* DEBUG_SHVERIFY */
michael@0 408 goto loser;
michael@0 409 }
michael@0 410
michael@0 411 /* hash our library file with SHA1 */
michael@0 412 hashcx = hashObj->create();
michael@0 413 if (hashcx == NULL) {
michael@0 414 goto loser;
michael@0 415 }
michael@0 416 hashObj->begin(hashcx);
michael@0 417
michael@0 418 count = 0;
michael@0 419 while ((bytesRead = PR_Read(shFD, buf, sizeof(buf))) > 0) {
michael@0 420 hashObj->update(hashcx, buf, bytesRead);
michael@0 421 count += bytesRead;
michael@0 422 }
michael@0 423 #ifdef FREEBL_USE_PRELINK
michael@0 424 bl_CloseUnPrelink(shFD, pid);
michael@0 425 #else
michael@0 426 PR_Close(shFD);
michael@0 427 #endif
michael@0 428 shFD = NULL;
michael@0 429
michael@0 430 hashObj->end(hashcx, hash.data, &hash.len, hash.len);
michael@0 431
michael@0 432
michael@0 433 /* verify the hash against the check file */
michael@0 434 if (DSA_VerifyDigest(&key, &signature, &hash) == SECSuccess) {
michael@0 435 result = PR_TRUE;
michael@0 436 }
michael@0 437 #ifdef DEBUG_SHVERIFY
michael@0 438 {
michael@0 439 int i,j;
michael@0 440 fprintf(stderr,"File %s: %d bytes\n",shName, count);
michael@0 441 fprintf(stderr," hash: %d bytes\n", hash.len);
michael@0 442 #define STEP 10
michael@0 443 for (i=0; i < hash.len; i += STEP) {
michael@0 444 fprintf(stderr," ");
michael@0 445 for (j=0; j < STEP && (i+j) < hash.len; j++) {
michael@0 446 fprintf(stderr," %02x", hash.data[i+j]);
michael@0 447 }
michael@0 448 fprintf(stderr,"\n");
michael@0 449 }
michael@0 450 fprintf(stderr," signature: %d bytes\n", signature.len);
michael@0 451 for (i=0; i < signature.len; i += STEP) {
michael@0 452 fprintf(stderr," ");
michael@0 453 for (j=0; j < STEP && (i+j) < signature.len; j++) {
michael@0 454 fprintf(stderr," %02x", signature.data[i+j]);
michael@0 455 }
michael@0 456 fprintf(stderr,"\n");
michael@0 457 }
michael@0 458 fprintf(stderr,"Verified : %s\n",result?"TRUE": "FALSE");
michael@0 459 }
michael@0 460 #endif /* DEBUG_SHVERIFY */
michael@0 461
michael@0 462
michael@0 463 loser:
michael@0 464 if (checkName != NULL) {
michael@0 465 PORT_Free(checkName);
michael@0 466 }
michael@0 467 if (checkFD != NULL) {
michael@0 468 PR_Close(checkFD);
michael@0 469 }
michael@0 470 if (shFD != NULL) {
michael@0 471 PR_Close(shFD);
michael@0 472 }
michael@0 473 if (hashcx != NULL) {
michael@0 474 if (hashObj) {
michael@0 475 hashObj->destroy(hashcx,PR_TRUE);
michael@0 476 }
michael@0 477 }
michael@0 478 if (signature.data != NULL) {
michael@0 479 PORT_Free(signature.data);
michael@0 480 }
michael@0 481 if (key.params.prime.data != NULL) {
michael@0 482 PORT_Free(key.params.prime.data);
michael@0 483 }
michael@0 484 if (key.params.subPrime.data != NULL) {
michael@0 485 PORT_Free(key.params.subPrime.data);
michael@0 486 }
michael@0 487 if (key.params.base.data != NULL) {
michael@0 488 PORT_Free(key.params.base.data);
michael@0 489 }
michael@0 490 if (key.publicValue.data != NULL) {
michael@0 491 PORT_Free(key.publicValue.data);
michael@0 492 }
michael@0 493
michael@0 494 return result;
michael@0 495 }
michael@0 496
michael@0 497 PRBool
michael@0 498 BLAPI_VerifySelf(const char *name)
michael@0 499 {
michael@0 500 if (name == NULL) {
michael@0 501 /*
michael@0 502 * If name is NULL, freebl is statically linked into softoken.
michael@0 503 * softoken will call BLAPI_SHVerify next to verify itself.
michael@0 504 */
michael@0 505 return PR_TRUE;
michael@0 506 }
michael@0 507 return BLAPI_SHVerify(name, (PRFuncPtr) decodeInt);
michael@0 508 }

mercurial