security/nss/lib/freebl/shvfy.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial