1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/freebl/shvfy.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,508 @@ 1.4 + 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifdef FREEBL_NO_DEPEND 1.10 +#include "stubs.h" 1.11 +#endif 1.12 + 1.13 +#include "shsign.h" 1.14 +#include "prlink.h" 1.15 +#include "prio.h" 1.16 +#include "blapi.h" 1.17 +#include "seccomon.h" 1.18 +#include "stdio.h" 1.19 +#include "prmem.h" 1.20 +#include "hasht.h" 1.21 +#include "pqg.h" 1.22 + 1.23 +/* 1.24 + * Most modern version of Linux support a speed optimization scheme where an 1.25 + * application called prelink modifies programs and shared libraries to quickly 1.26 + * load if they fit into an already designed address space. In short, prelink 1.27 + * scans the list of programs and libraries on your system, assigns them a 1.28 + * predefined space in the the address space, then provides the fixups to the 1.29 + * library. 1.30 + 1.31 + * The modification of the shared library is correctly detected by the freebl 1.32 + * FIPS checksum scheme where we check a signed hash of the library against the 1.33 + * library itself. 1.34 + * 1.35 + * The prelink command itself can reverse the process of modification and 1.36 + * output the prestine shared library as it was before prelink made it's 1.37 + * changes. If FREEBL_USE_PRELINK is set Freebl uses prelink to output the 1.38 + * original copy of the shared library before prelink modified it. 1.39 + */ 1.40 +#ifdef FREEBL_USE_PRELINK 1.41 +#ifndef FREELB_PRELINK_COMMAND 1.42 +#define FREEBL_PRELINK_COMMAND "/usr/sbin/prelink -u -o -" 1.43 +#endif 1.44 +#include "private/pprio.h" 1.45 + 1.46 +#include <stdlib.h> 1.47 +#include <unistd.h> 1.48 +#include <fcntl.h> 1.49 +#include <sys/wait.h> 1.50 +#include <sys/stat.h> 1.51 + 1.52 +/* 1.53 + * This function returns an NSPR PRFileDesc * which the caller can read to 1.54 + * obtain the prestine value of the shared library, before any OS related 1.55 + * changes to it (usually address fixups). 1.56 + * 1.57 + * If prelink is installed, this 1.58 + * file descriptor is a pipe connecting the output of 1.59 + * /usr/sbin/prelink -u -o - {Library} 1.60 + * and *pid returns the process id of the prelink child. 1.61 + * 1.62 + * If prelink is not installed, it returns a normal readonly handle to the 1.63 + * library itself and *pid is set to '0'. 1.64 + */ 1.65 +PRFileDesc * 1.66 +bl_OpenUnPrelink(const char *shName, int *pid) 1.67 +{ 1.68 + char *command= strdup(FREEBL_PRELINK_COMMAND); 1.69 + char *argString = NULL; 1.70 + char **argv = NULL; 1.71 + char *shNameArg = NULL; 1.72 + char *cp; 1.73 + pid_t child; 1.74 + int argc = 0, argNext = 0; 1.75 + struct stat statBuf; 1.76 + int pipefd[2] = {-1,-1}; 1.77 + int ret; 1.78 + 1.79 + *pid = 0; 1.80 + 1.81 + /* make sure the prelink command exists first. If not, fall back to 1.82 + * just reading the file */ 1.83 + for (cp = command; *cp ; cp++) { 1.84 + if (*cp == ' ') { 1.85 + *cp++ = 0; 1.86 + argString = cp; 1.87 + break; 1.88 + } 1.89 + } 1.90 + memset (&statBuf, 0, sizeof(statBuf)); 1.91 + /* stat the file, follow the link */ 1.92 + ret = stat(command, &statBuf); 1.93 + if (ret < 0) { 1.94 + free(command); 1.95 + return PR_Open(shName, PR_RDONLY, 0); 1.96 + } 1.97 + /* file exits, make sure it's an executable */ 1.98 + if (!S_ISREG(statBuf.st_mode) || 1.99 + ((statBuf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0)) { 1.100 + free(command); 1.101 + return PR_Open(shName, PR_RDONLY, 0); 1.102 + } 1.103 + 1.104 + /* OK, the prelink command exists and looks correct, use it */ 1.105 + /* build the arglist while we can still malloc */ 1.106 + /* count the args if any */ 1.107 + if (argString && *argString) { 1.108 + /* argString may have leading spaces, strip them off*/ 1.109 + for (cp = argString; *cp && *cp == ' '; cp++); 1.110 + argString = cp; 1.111 + if (*cp) { 1.112 + /* there is at least one arg.. */ 1.113 + argc = 1; 1.114 + } 1.115 + 1.116 + /* count the rest: Note there is no provision for escaped 1.117 + * spaces here */ 1.118 + for (cp = argString; *cp ; cp++) { 1.119 + if (*cp == ' ') { 1.120 + while (*cp && *cp == ' ') cp++; 1.121 + if (*cp) argc++; 1.122 + } 1.123 + } 1.124 + } 1.125 + 1.126 + /* add the additional args: argv[0] (command), shName, NULL*/ 1.127 + argc += 3; 1.128 + argv = PORT_NewArray(char *, argc); 1.129 + if (argv == NULL) { 1.130 + goto loser; 1.131 + } 1.132 + 1.133 + /* fill in the arglist */ 1.134 + argv[argNext++] = command; 1.135 + if (argString && *argString) { 1.136 + argv[argNext++] = argString; 1.137 + for (cp = argString; *cp; cp++) { 1.138 + if (*cp == ' ') { 1.139 + *cp++ = 0; 1.140 + while (*cp && *cp == ' ') cp++; 1.141 + if (*cp) argv[argNext++] = cp; 1.142 + } 1.143 + } 1.144 + } 1.145 + /* exec doesn't advertise taking const char **argv, do the paranoid 1.146 + * copy */ 1.147 + shNameArg = strdup(shName); 1.148 + if (shNameArg == NULL) { 1.149 + goto loser; 1.150 + } 1.151 + argv[argNext++] = shNameArg; 1.152 + argv[argNext++] = 0; 1.153 + 1.154 + ret = pipe(pipefd); 1.155 + if (ret < 0) { 1.156 + goto loser; 1.157 + } 1.158 + 1.159 + /* use vfork() so we don't trigger the pthread_at_fork() handlers */ 1.160 + child = vfork(); 1.161 + if (child < 0) goto loser; 1.162 + if (child == 0) { 1.163 + /* set up the file descriptors */ 1.164 + /* if we need to support BSD, this will need to be an open of 1.165 + * /dev/null and dup2(nullFD, 0)*/ 1.166 + close(0); 1.167 + /* associate pipefd[1] with stdout */ 1.168 + if (pipefd[1] != 1) dup2(pipefd[1], 1); 1.169 + close(2); 1.170 + close(pipefd[0]); 1.171 + /* should probably close the other file descriptors? */ 1.172 + 1.173 + 1.174 + execv(command, argv); 1.175 + /* avoid at_exit() handlers */ 1.176 + _exit(1); /* shouldn't reach here except on an error */ 1.177 + } 1.178 + close(pipefd[1]); 1.179 + pipefd[1] = -1; 1.180 + 1.181 + /* this is safe because either vfork() as full fork() semantics, and thus 1.182 + * already has it's own address space, or because vfork() has paused 1.183 + * the parent util the exec or exit */ 1.184 + free(command); 1.185 + free(shNameArg); 1.186 + PORT_Free(argv); 1.187 + 1.188 + *pid = child; 1.189 + 1.190 + return PR_ImportPipe(pipefd[0]); 1.191 + 1.192 +loser: 1.193 + if (pipefd[0] != -1) { 1.194 + close(pipefd[0]); 1.195 + } 1.196 + if (pipefd[1] != -1) { 1.197 + close(pipefd[1]); 1.198 + } 1.199 + free(command); 1.200 + free(shNameArg); 1.201 + PORT_Free(argv); 1.202 + 1.203 + return NULL; 1.204 +} 1.205 + 1.206 +/* 1.207 + * bl_CloseUnPrelink - 1.208 + * 1.209 + * This closes the file descripter and reaps and children openned and crated by 1.210 + * b;_OpenUnprelink. It's primary difference between it and just close is 1.211 + * that it calls wait on the pid if one is supplied, preventing zombie children 1.212 + * from hanging around. 1.213 + */ 1.214 +void 1.215 +bl_CloseUnPrelink( PRFileDesc *file, int pid) 1.216 +{ 1.217 + /* close the file descriptor */ 1.218 + PR_Close(file); 1.219 + /* reap the child */ 1.220 + if (pid) { 1.221 + waitpid(pid, NULL, 0); 1.222 + } 1.223 +} 1.224 +#endif 1.225 + 1.226 +/* #define DEBUG_SHVERIFY 1 */ 1.227 + 1.228 +static char * 1.229 +mkCheckFileName(const char *libName) 1.230 +{ 1.231 + int ln_len = PORT_Strlen(libName); 1.232 + char *output = PORT_Alloc(ln_len+sizeof(SGN_SUFFIX)); 1.233 + int index = ln_len + 1 - sizeof("."SHLIB_SUFFIX); 1.234 + 1.235 + if ((index > 0) && 1.236 + (PORT_Strncmp(&libName[index], 1.237 + "."SHLIB_SUFFIX,sizeof("."SHLIB_SUFFIX)) == 0)) { 1.238 + ln_len = index; 1.239 + } 1.240 + PORT_Memcpy(output,libName,ln_len); 1.241 + PORT_Memcpy(&output[ln_len],SGN_SUFFIX,sizeof(SGN_SUFFIX)); 1.242 + return output; 1.243 +} 1.244 + 1.245 +static int 1.246 +decodeInt(unsigned char *buf) 1.247 +{ 1.248 + return (buf[3]) | (buf[2] << 8) | (buf[1] << 16) | (buf[0] << 24); 1.249 +} 1.250 + 1.251 +static SECStatus 1.252 +readItem(PRFileDesc *fd, SECItem *item) 1.253 +{ 1.254 + unsigned char buf[4]; 1.255 + int bytesRead; 1.256 + 1.257 + 1.258 + bytesRead = PR_Read(fd, buf, 4); 1.259 + if (bytesRead != 4) { 1.260 + return SECFailure; 1.261 + } 1.262 + item->len = decodeInt(buf); 1.263 + 1.264 + item->data = PORT_Alloc(item->len); 1.265 + if (item->data == NULL) { 1.266 + item->len = 0; 1.267 + return SECFailure; 1.268 + } 1.269 + bytesRead = PR_Read(fd, item->data, item->len); 1.270 + if (bytesRead != item->len) { 1.271 + PORT_Free(item->data); 1.272 + item->data = NULL; 1.273 + item->len = 0; 1.274 + return SECFailure; 1.275 + } 1.276 + return SECSuccess; 1.277 +} 1.278 + 1.279 +PRBool 1.280 +BLAPI_SHVerify(const char *name, PRFuncPtr addr) 1.281 +{ 1.282 + PRBool result = PR_FALSE; /* if anything goes wrong, 1.283 + * the signature does not verify */ 1.284 + /* find our shared library name */ 1.285 + char *shName = PR_GetLibraryFilePathname(name, addr); 1.286 + if (!shName) { 1.287 + goto loser; 1.288 + } 1.289 + result = BLAPI_SHVerifyFile(shName); 1.290 + 1.291 +loser: 1.292 + if (shName != NULL) { 1.293 + PR_Free(shName); 1.294 + } 1.295 + 1.296 + return result; 1.297 +} 1.298 + 1.299 +PRBool 1.300 +BLAPI_SHVerifyFile(const char *shName) 1.301 +{ 1.302 + char *checkName = NULL; 1.303 + PRFileDesc *checkFD = NULL; 1.304 + PRFileDesc *shFD = NULL; 1.305 + void *hashcx = NULL; 1.306 + const SECHashObject *hashObj = NULL; 1.307 + SECItem signature = { 0, NULL, 0 }; 1.308 + SECItem hash; 1.309 + int bytesRead, offset; 1.310 + SECStatus rv; 1.311 + DSAPublicKey key; 1.312 + int count; 1.313 +#ifdef FREEBL_USE_PRELINK 1.314 + int pid = 0; 1.315 +#endif 1.316 + 1.317 + PRBool result = PR_FALSE; /* if anything goes wrong, 1.318 + * the signature does not verify */ 1.319 + unsigned char buf[4096]; 1.320 + unsigned char hashBuf[HASH_LENGTH_MAX]; 1.321 + 1.322 + PORT_Memset(&key,0,sizeof(key)); 1.323 + hash.data = hashBuf; 1.324 + hash.len = sizeof(hashBuf); 1.325 + 1.326 + if (!shName) { 1.327 + goto loser; 1.328 + } 1.329 + 1.330 + /* figure out the name of our check file */ 1.331 + checkName = mkCheckFileName(shName); 1.332 + if (!checkName) { 1.333 + goto loser; 1.334 + } 1.335 + 1.336 + /* open the check File */ 1.337 + checkFD = PR_Open(checkName, PR_RDONLY, 0); 1.338 + if (checkFD == NULL) { 1.339 +#ifdef DEBUG_SHVERIFY 1.340 + fprintf(stderr, "Failed to open the check file %s: (%d, %d)\n", 1.341 + checkName, (int)PR_GetError(), (int)PR_GetOSError()); 1.342 +#endif /* DEBUG_SHVERIFY */ 1.343 + goto loser; 1.344 + } 1.345 + 1.346 + /* read and Verify the headerthe header */ 1.347 + bytesRead = PR_Read(checkFD, buf, 12); 1.348 + if (bytesRead != 12) { 1.349 + goto loser; 1.350 + } 1.351 + if ((buf[0] != NSS_SIGN_CHK_MAGIC1) || (buf[1] != NSS_SIGN_CHK_MAGIC2)) { 1.352 + goto loser; 1.353 + } 1.354 + if ((buf[2] != NSS_SIGN_CHK_MAJOR_VERSION) || 1.355 + (buf[3] < NSS_SIGN_CHK_MINOR_VERSION)) { 1.356 + goto loser; 1.357 + } 1.358 +#ifdef notdef 1.359 + if (decodeInt(&buf[8]) != CKK_DSA) { 1.360 + goto loser; 1.361 + } 1.362 +#endif 1.363 + 1.364 + /* seek past any future header extensions */ 1.365 + offset = decodeInt(&buf[4]); 1.366 + PR_Seek(checkFD, offset, PR_SEEK_SET); 1.367 + 1.368 + /* read the key */ 1.369 + rv = readItem(checkFD,&key.params.prime); 1.370 + if (rv != SECSuccess) { 1.371 + goto loser; 1.372 + } 1.373 + rv = readItem(checkFD,&key.params.subPrime); 1.374 + if (rv != SECSuccess) { 1.375 + goto loser; 1.376 + } 1.377 + rv = readItem(checkFD,&key.params.base); 1.378 + if (rv != SECSuccess) { 1.379 + goto loser; 1.380 + } 1.381 + rv = readItem(checkFD,&key.publicValue); 1.382 + if (rv != SECSuccess) { 1.383 + goto loser; 1.384 + } 1.385 + /* read the siganture */ 1.386 + rv = readItem(checkFD,&signature); 1.387 + if (rv != SECSuccess) { 1.388 + goto loser; 1.389 + } 1.390 + 1.391 + /* done with the check file */ 1.392 + PR_Close(checkFD); 1.393 + checkFD = NULL; 1.394 + 1.395 + hashObj = HASH_GetRawHashObject(PQG_GetHashType(&key.params)); 1.396 + if (hashObj == NULL) { 1.397 + goto loser; 1.398 + } 1.399 + 1.400 + /* open our library file */ 1.401 +#ifdef FREEBL_USE_PRELINK 1.402 + shFD = bl_OpenUnPrelink(shName,&pid); 1.403 +#else 1.404 + shFD = PR_Open(shName, PR_RDONLY, 0); 1.405 +#endif 1.406 + if (shFD == NULL) { 1.407 +#ifdef DEBUG_SHVERIFY 1.408 + fprintf(stderr, "Failed to open the library file %s: (%d, %d)\n", 1.409 + shName, (int)PR_GetError(), (int)PR_GetOSError()); 1.410 +#endif /* DEBUG_SHVERIFY */ 1.411 + goto loser; 1.412 + } 1.413 + 1.414 + /* hash our library file with SHA1 */ 1.415 + hashcx = hashObj->create(); 1.416 + if (hashcx == NULL) { 1.417 + goto loser; 1.418 + } 1.419 + hashObj->begin(hashcx); 1.420 + 1.421 + count = 0; 1.422 + while ((bytesRead = PR_Read(shFD, buf, sizeof(buf))) > 0) { 1.423 + hashObj->update(hashcx, buf, bytesRead); 1.424 + count += bytesRead; 1.425 + } 1.426 +#ifdef FREEBL_USE_PRELINK 1.427 + bl_CloseUnPrelink(shFD, pid); 1.428 +#else 1.429 + PR_Close(shFD); 1.430 +#endif 1.431 + shFD = NULL; 1.432 + 1.433 + hashObj->end(hashcx, hash.data, &hash.len, hash.len); 1.434 + 1.435 + 1.436 + /* verify the hash against the check file */ 1.437 + if (DSA_VerifyDigest(&key, &signature, &hash) == SECSuccess) { 1.438 + result = PR_TRUE; 1.439 + } 1.440 +#ifdef DEBUG_SHVERIFY 1.441 + { 1.442 + int i,j; 1.443 + fprintf(stderr,"File %s: %d bytes\n",shName, count); 1.444 + fprintf(stderr," hash: %d bytes\n", hash.len); 1.445 +#define STEP 10 1.446 + for (i=0; i < hash.len; i += STEP) { 1.447 + fprintf(stderr," "); 1.448 + for (j=0; j < STEP && (i+j) < hash.len; j++) { 1.449 + fprintf(stderr," %02x", hash.data[i+j]); 1.450 + } 1.451 + fprintf(stderr,"\n"); 1.452 + } 1.453 + fprintf(stderr," signature: %d bytes\n", signature.len); 1.454 + for (i=0; i < signature.len; i += STEP) { 1.455 + fprintf(stderr," "); 1.456 + for (j=0; j < STEP && (i+j) < signature.len; j++) { 1.457 + fprintf(stderr," %02x", signature.data[i+j]); 1.458 + } 1.459 + fprintf(stderr,"\n"); 1.460 + } 1.461 + fprintf(stderr,"Verified : %s\n",result?"TRUE": "FALSE"); 1.462 + } 1.463 +#endif /* DEBUG_SHVERIFY */ 1.464 + 1.465 + 1.466 +loser: 1.467 + if (checkName != NULL) { 1.468 + PORT_Free(checkName); 1.469 + } 1.470 + if (checkFD != NULL) { 1.471 + PR_Close(checkFD); 1.472 + } 1.473 + if (shFD != NULL) { 1.474 + PR_Close(shFD); 1.475 + } 1.476 + if (hashcx != NULL) { 1.477 + if (hashObj) { 1.478 + hashObj->destroy(hashcx,PR_TRUE); 1.479 + } 1.480 + } 1.481 + if (signature.data != NULL) { 1.482 + PORT_Free(signature.data); 1.483 + } 1.484 + if (key.params.prime.data != NULL) { 1.485 + PORT_Free(key.params.prime.data); 1.486 + } 1.487 + if (key.params.subPrime.data != NULL) { 1.488 + PORT_Free(key.params.subPrime.data); 1.489 + } 1.490 + if (key.params.base.data != NULL) { 1.491 + PORT_Free(key.params.base.data); 1.492 + } 1.493 + if (key.publicValue.data != NULL) { 1.494 + PORT_Free(key.publicValue.data); 1.495 + } 1.496 + 1.497 + return result; 1.498 +} 1.499 + 1.500 +PRBool 1.501 +BLAPI_VerifySelf(const char *name) 1.502 +{ 1.503 + if (name == NULL) { 1.504 + /* 1.505 + * If name is NULL, freebl is statically linked into softoken. 1.506 + * softoken will call BLAPI_SHVerify next to verify itself. 1.507 + */ 1.508 + return PR_TRUE; 1.509 + } 1.510 + return BLAPI_SHVerify(name, (PRFuncPtr) decodeInt); 1.511 +}