security/nss/lib/freebl/shvfy.c

changeset 0
6474c204b198
     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 +}

mercurial