Coverage Report

Created: 2024-11-21 07:03

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