CERT_PKIXVerifyCert() with "certificateUsageSSLServer" returns error but still log->count is 0

UNCONFIRMED
Unassigned

Status

UNCONFIRMED
8 years ago
7 years ago

People

(Reporter: meena.vyas, Unassigned)

Tracking

Firefox Tracking Flags

(Not tracked)

Details

Attachments

(1 attachment)

(Reporter)

Description

8 years ago
User-Agent:       Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.9.2.6) Gecko/20100627 Firefox/3.6.6
Build Identifier: SECURITY_3.12.6_20100315

Run the program attached in this bug. You get error SEC_ERROR_INVALID_ARGS but CERTVerifyLog *log count is zero. So we do not know what the real problem is.

CERT_PKIXVerifyCert(certificateUsageSSLServer) returned ERROR !!!
        Error = -8187
        Note -8187 => SEC_ERROR_INVALID_ARGS .
log->count is 0

Reproducible: Always

Steps to Reproduce:
1.export LD_LIBRARY_PATH=/share/builds/components/security/SECURITY_3.12.6_20100315/SunOS5.8_DBG.OBJ/lib:$LD_LIBRARY_PATH
2. /usr/dist/share/sunstudio_sparc,v12.0/SUNWspro/bin/CC -g -I -I/share/builds/components/security/SECURITY_3.12.6_20100315/SunOS5.8_DBG.OBJ//include -L/share/builds/components/security/SECURITY_3.12.6_20100315/SunOS5.8_DBG.OBJ//lib -lnspr4 -lnss3 server.cpp
3. ./a.out


Actual Results:  
CERT_PKIXVerifyCert(certificateUsageSSLServer) returned ERROR !!!
        Error = -8187
        Note -8187 => SEC_ERROR_INVALID_ARGS .
log->count is 0

Expected Results:  
log count should not be zero. And we should see a proper message why this certificate is not a valid "server" certificate. 

Note if arg is "certificateUsageSSLServerWithStepUp" in CERT_PKIXVerifyCert(), 
we get proper error message :

CERT_PKIXVerifyCert(certificateUsageSSLServer) returned ERROR !!!
        Error = -8102
        Note -8102 => SEC_ERROR_INADEQUATE_KEY_USAGE.

log->count is 1
certificate=Server-Cert depth node->error=-8102 Unknown Issue
        Note -8102=> SEC_ERROR_INADEQUATE_KEY_USAGE

$cat server.cpp

#include "nspr.h"
#include "ssl.h"
#include "certdb.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "cert.h"
#include "nspr.h"
#include "prerror.h"
#include "ssl.h"
#include "certt.h"
#include "nss.h"
#include "pk11pub.h"
#include "secerr.h"
#include "sslerr.h"

#define NSS_DB_PATH "."
#define CERT_NICKNAME "Server-Cert"

void printError(void)
{
    int e = PR_GetError();
    if (e != 0)
        printf("\tError = %d\n", e);
    int ose = PR_GetOSError();
    if (ose != 0)
        printf("\tOS Error = %d\n", PR_GetOSError());
    PRInt32 len = PR_GetErrorTextLength();
    if (len > 0) {
        char arr[1024];
        memset(arr,'\0',1024);
        PR_GetErrorText(arr);
        printf("\tErrorTxt = %s\n", arr);
    }
   if (e == SEC_ERROR_INADEQUATE_KEY_USAGE)
        printf("\tNote %d => SEC_ERROR_INADEQUATE_KEY_USAGE.\n", e);
   else if (e == SEC_ERROR_INVALID_ARGS )
        printf("\tNote %d => SEC_ERROR_INVALID_ARGS .\n", e);
}

char * parseArg(CERTVerifyLogNode *node)
{
     char *errstr = NULL;
     char *issuer = NULL;
     unsigned int flags = 0;
     switch (node->error) {
         case SEC_ERROR_INADEQUATE_KEY_USAGE:
              flags = (unsigned int)((unsigned long)node->arg);
              switch (flags) {
                  case KU_DIGITAL_SIGNATURE:
                      errstr = strdup("Certificate cannot sign.");
                      break;
                  case KU_KEY_ENCIPHERMENT:
                      errstr = strdup("Certificate cannot encrypt.");
                      break;
                  case KU_KEY_CERT_SIGN:
                      errstr = strdup("Certificate cannot sign other certs.");
                      break;
                  default:
                      errstr = strdup("[unknown usage].");
                      break;
              }
          case SEC_ERROR_INADEQUATE_CERT_TYPE:
              flags = (unsigned int)((unsigned long)node->arg);
              switch (flags) {
                  case NS_CERT_TYPE_SSL_CLIENT:
                  case NS_CERT_TYPE_SSL_SERVER:
                      errstr = strdup("Certificate cannot be used for SSL.");
                      break;
                  case NS_CERT_TYPE_SSL_CA:
                      errstr = strdup("Certificate cannot be used as an SSL CA.");
                      break;
                  case NS_CERT_TYPE_EMAIL:
                      errstr = strdup("Certificate cannot be used for SMIME.");
                      break;
                  case NS_CERT_TYPE_EMAIL_CA:
                      errstr = strdup("Certificate cannot be used as an SMIME CA.");
                      break;
                  case NS_CERT_TYPE_OBJECT_SIGNING:
                      errstr = strdup("Certificate cannot be used for object signing.");
                      break;
                  case NS_CERT_TYPE_OBJECT_SIGNING_CA:
                      errstr = strdup("Certificate cannot be used as an object signing CA.");
                  break;
                default:
                     errstr = strdup("[unknown usage].");
                     break;
             }
         case SEC_ERROR_UNKNOWN_ISSUER:
            errstr = "Unknown Issuer";
            issuer = node->cert->issuerName;
            break;
         case SEC_ERROR_UNTRUSTED_ISSUER:
            errstr = "Untrusted Issuer";
            issuer = node->cert->issuerName;
            break;
         case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
            errstr = "Expired Issuer Certificate";
            issuer = node->cert->issuerName;
             break;
         default:
             break;
     }
     int len = errstr?strlen(errstr):0 + issuer?strlen(issuer):0 + 3;
     char *arr = (char *)malloc(len);
     memset(arr,'\0',len);
     sprintf(arr,"%s %s", errstr?errstr:"",  issuer?issuer:"");
     arr[len-1] = '\0';
     return arr;
}

void displayLog(CERTVerifyLog *log)
{
   printf("\nlog->count is %d\n", log->count);
   if (log->count > 0) {
       for (CERTVerifyLogNode *node=log->head; node; node=node->next) {
         printf("certificate=%s depth%s node->error=%ld %s\n",
                node->cert->nickname, node->depth ? "CA": "",
                node->error,  parseArg(node));
         if (node->error == SEC_ERROR_INADEQUATE_KEY_USAGE)
             printf("\tNote -8102=> SEC_ERROR_INADEQUATE_KEY_USAGE\n");
         } // for loop
  }
}

class VerifyCert
{
    public :
        VerifyCert(CERTCertificate *cert, void *pinArg);
        ~VerifyCert();
        SECStatus checkCertUsage(SECCertificateUsage certusage) { return CERT_PKIXVerifyCert(_cert, certusage, _paramsIn, _paramsOut, _pinArg); }
        void log(void) { displayLog(this->_log); }

    private :
        CERTValInParam _paramsIn[1];
        CERTValOutParam _paramsOut[2];

        PLArenaPool *_arena;
        CERTVerifyLog *_log;

        CERTCertificate *_cert;
        void *_pinArg;
};

VerifyCert::VerifyCert(CERTCertificate *cert, void *pinArg)
{
    _cert = cert;
    _pinArg = pinArg;

    // Set the first input params entry to be the end of list marker.
    _paramsIn[0].type = cert_pi_end;

    _arena = PORT_NewArena(512);
    _log = PORT_ArenaZNew(_arena, CERTVerifyLog);
    _log->arena = _arena;
    _log->head = _log->tail = NULL;
    _log->count = 0;

    // Set the first entry to CertVerifyLog
    _paramsOut[0].type = cert_po_errorLog;
    _paramsOut[0].value.pointer.log = _log;

    // Set the second entry to be the end of list marker.
    _paramsOut[1].type = cert_po_end;
}

VerifyCert::~VerifyCert()
{
    // free memory
    for (CERTVerifyLogNode *node = _log->head; node; node = node->next) {
        if (node->cert)
            CERT_DestroyCertificate(node->cert);
    }
    PORT_FreeArena(_log->arena, PR_FALSE);
    _log = NULL;
    _arena = NULL;
}

int main(int argc, char **argv)
{
    PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
    PK11_ConfigurePKCS11(NULL, NULL, NULL, "internal", NULL, NULL, NULL, NULL, 8, 1);
    PK11_SetPasswordFunc(NULL);
    SECStatus status = NSS_Initialize(NSS_DB_PATH, "", "", "secmod.db", NSS_INIT_READONLY);
    if (status != SECSuccess) {
        printf("ERROR in NSS_Initialize\n");
        goto error;
    }

    CERTCertificate  *cert = PK11_FindCertFromNickname(CERT_NICKNAME, NULL);
    if (cert == NULL) {
        printf("ERROR : PK11_FindCertFromNickname returned NULL cert\n");
        goto error;
    }

    VerifyCert *v1 = new VerifyCert(cert, NULL);
    SECStatus rv = v1->checkCertUsage(certificateUsageSSLServer);
    if (rv == SECSuccess) {
        printf("CERT_PKIXVerifyCert(certificateUsageSSLServer) returned success !! \n");
    } else {
        // print error in error log
        printf("CERT_PKIXVerifyCert(certificateUsageSSLServer) returned ERROR !!! \n");
        printError();
        v1->log();

   }
   delete v1;
   return 0;
error:
        printError();
        exit(-1);
}
(Reporter)

Comment 1

8 years ago
Created attachment 471441 [details]
cert8.db and key3.db are attached
(Reporter)

Comment 2

8 years ago
Why we are getting PKIX_NULLARGUMENT in stdVars.aPkixErrorCode :

PKIX_ForwardBuilderState state->buildConstants.revChecker is NULL. This revChecker is being passed to the function pkix_CheckChain() which is assuming revChecker is non-NULL as per the documentation
http://mxr.mozilla.org/security/source/security/nss/lib/libpkix/pkix/top/pkix_validate.c#669

What is wrong revChecker is NULL or pkix_CheckChain()'s assumption that revChecker is non-NULL?

Debugger info :

(dbx) p state
state = 0x5ddd0
(dbx) p *state
*state = {
    status              = BUILD_CHECKTRUSTED2
    traversedCACerts    = 0
    certStoreIndex      = 1U
    numCerts            = 1U
    numAias             = 0
    certIndex           = 0
    aiaIndex            = 0
    certCheckedIndex    = 0
    checkerIndex        = 0
    hintCertIndex       = 0
    numFanout           = 0
    numDepth            = 0
    reasonCode          = 0
    revCheckDelayed     = 0
    canBeCached         = 1
    useOnlyLocal        = 1
    revChecking         = 0
    usingHintCerts      = 0
    certLoopingDetected = 0
    validityDate        = 0x5e7b0
    prevCert            = 0x59930
    candidateCert       = 0x5f770
    traversedSubjNames  = 0x59880
    trustChain          = 0x569d0
    aia                 = (nil)
    candidateCerts      = 0x5e518
    reversedCertChain   = 0x5e978
    checkedCritExtOIDs  = 0x66a30
    checkerChain        = 0x5e8d8
    certSel             = 0x5e1a0
    verifyNode          = 0x597e8
    client              = (nil)
    parentState         = (nil)
    buildConstants      = {
        numAnchors            = 0
        numCertStores         = 1U
        numHintCerts          = 0
        maxDepth              = 0
        maxFanout             = 0
        maxTime               = 0
        procParams            = 0x592b8
        testDate              = 0x550c8
        timeLimit             = (nil)
        targetCert            = 0x59930
        targetPubKey          = 0x59820
        certStores            = 0x59538
        anchors               = 0x59388
        userCheckers          = (nil)
        hintCerts             = (nil)
        revChecker            = (nil)
        aiaMgr                = (nil)
        useAIAForCertFetching = 0
    }
}
(dbx) where
current thread: t@1
=>[1] pkix_CheckChain(certs = 0x5e978, numCerts = 1U, anchor = 0x5e840, checkers = 0x5e8d8, revChecker = (nil), removeCheckedExtOIDs = 0x66a30, procParams = 0x592b8, pCertCheckedIndex = 0x5ddec, pCheckerIndex = 0x5ddf0, pRevChecking = 0x5de10, pReasonCode = 0x5de00, pNBIOContext = 0xffbfe930, pFinalSubjPubKey = 0xffbfe93c, pPolicyTree = 0xffbfe938, pVerifyTree = (nil), plContext = 0x55078), line 738 in "pkix_validate.c"
  [2] pkix_Build_ValidateEntireChain(state = 0x5ddd0, anchor = 0x5e840, pNBIOContext = 0xffbfea00, pValResult = 0xffbfea28, verifyNode = 0x5e658, plContext = 0x55078), line 1348 in "pkix_build.c"
  [3] pkix_BuildForwardDepthFirstSearch(pNBIOContext = 0xffbfeb9c, state = 0x5ddd0, pValResult = 0xffbfeb4c, plContext = 0x55078), line 2535 in "pkix_build.c"
  [4] pkix_Build_InitiateBuildChain(procParams = 0x592b8, pNBIOContext = 0xffbfec80, pState = 0xffbfec88, pBuildResult = 0xffbfec84, pVerifyNode = 0xffbfed0c, plContext = 0x55078), line 3553 in "pkix_build.c"
  [5] PKIX_BuildChain(procParams = 0x592b8, pNBIOContext = 0xffbfed20, pState = 0xffbfed1c, pBuildResult = 0xffbfed24, pVerifyNode = 0xffbfed0c, plContext = 0x55078), line 3726 in "pkix_build.c"
  [6] CERT_PKIXVerifyCert(cert = 0x56be0, usages = 2LL, paramsIn = 0x59220, paramsOut = 0x59240, wincx = (nil)), line 2150 in "certvfypkix.c"
  [7] VerifyCert::checkCertUsage(this = 0x59220, certusage = 2LL), line 131 in "server.cpp"
  [8] main(argc = 1, argv = 0xffbfee94), line 197 in "server.cpp"

  738       PKIX_NULLCHECK_FOUR(certs, checkers, revChecker, pCertCheckedIndex);
(dbx) p revChecker
revChecker = (nil)
(Reporter)

Comment 3

8 years ago
WORKAROUND:

Add some bare minimum revocation flags (like CRL revocation flags and forbid network fetching) and it works.

$./a.out
CERT_PKIXVerifyCert(certificateUsageSSLServer) returned success !!

$diff server.cpp server.cpp.old
135,137c135
<         CERTValInParam _paramsIn[2];
<         CERTRevocationFlags _rev;
<
---
>         CERTValInParam _paramsIn[1];
152,168d149
<     // Set the first input params entry
<     _paramsIn[0].type = cert_pi_revocationFlags;
<     _paramsIn[0].value.pointer.revocation = &_rev;
<     _rev.leafTests.number_of_defined_methods = 1;
<     _rev.chainTests.number_of_defined_methods = 1;
<     _rev.leafTests.number_of_preferred_methods = 1;
<     _rev.chainTests.number_of_preferred_methods = 1;
<     PRUint64 flags[] = { (CERT_REV_M_TEST_USING_THIS_METHOD | CERT_REV_M_FORBID_NETWORK_FETCHING) };
<     _rev.leafTests.cert_rev_flags_per_method = flags;
<     _rev.chainTests.cert_rev_flags_per_method = flags;
<     CERTRevocationMethodIndex mthds[] = { cert_revocation_method_crl };
<     _rev.leafTests.preferred_methods = mthds;
<     _rev.chainTests.preferred_methods = mthds;
<
<     _rev.leafTests.cert_rev_method_independent_flags = CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST;
<     _rev.chainTests.cert_rev_method_independent_flags = CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST;
<
170c151
<     _paramsIn[1].type = cert_pi_end;
---
>     _paramsIn[0].type = cert_pi_end;
You need to log in before you can comment on or make changes to this bug.