Closed Bug 122909 Opened 23 years ago Closed 4 months ago

NSS lacks sample code for CERT_VerifyCert's error log

Categories

(NSS :: Documentation, enhancement, P3)

3.3.1
enhancement

Tracking

(Not tracked)

RESOLVED INACTIVE

People

(Reporter: nelson, Unassigned)

Details

NSS's CERT_VerifyCert function has a valuable feature called the error log.
There is no documentation on how to use (AFAIK), and none of the NSS
command tools use it.  At least one of them should show how to use it.
I nominate tstclnt as the tool of choice for this purpose, but others
might be good for this, too.

Here's some documentation on how to use the feature that I wrote up 
recently.

The "error log" is a feature of only one function in NSS, the function
CERT_VerifyCert().  No other public NSS function has this feature, not 
even CERT_VerifyCertNow().

The last argument to CERT_VerifyCert is a pointer to a CERTVerifyLog ,
which is a structure declared in NSS in the header file certt.h.

If this argument is null, CERT_VerifyCert stops at the first error it 
encounters while verifying a cert chain, and returns the first error 
code it encountered via the usual error mechanism.  

If the last argument is non-null, CERT_VerifyCert performs the cert chain
verification until it is all done, or until it encounters some error that 
makes it unable to go any further.  It does not stop at the first error
it encounters.  Each error it encounters is placed into the error log.
After it returns, the caller can examine the error log structure to see
if it contains any errors, and can then deal with them. 

The error log structure contains head and tail pointers to a doubly-linked
list of CERTVerifyLogNode structures that are allocated from an ArenaPool.
It also contains a count of the number of error log nodes in the list,
and a pointer to the arena pool from which they have been allocated.

struct CERTVerifyLogStr {
    PRArenaPool *arena;
    unsigned int count;
    struct CERTVerifyLogNodeStr *head;
    struct CERTVerifyLogNodeStr *tail;
};

For each error, a Log node is created and added to the linked list.
The log nodes look like this:

struct CERTVerifyLogNodeStr {
    CERTCertificate *cert;      /* what cert had the error */
    long error;                 /* what error was it? */
    unsigned int depth;         /* how far up the chain are we */
    void *arg;                  /* error specific argument */
    struct CERTVerifyLogNodeStr *next; /* next in the list */
    struct CERTVerifyLogNodeStr *prev; /* next in the list */
};

Each CERTVerifyLogNode struct contains the following things:

1. a reference to the CERTCertificate structure for the cert that failed
the test.  This reference must be released to avoid leaking the cert.

2. an error code indiciating the particular problem

3. the "depth" in the cert chain where the error occurred (depth is 
distance down the chain from the leaf).  Errors on the leaf cert itself
have depth of zero.

4. an "argument" whose meaning depends on the particular error code given

5. The pointers the in the chain.

The error log is sorted by increasing "depth".  Because of the sorting, 
the errors may not appear in the log in the same order in which they were
generated.

All the errors of a given depth __should__ refer to the same cert.  
If they don't, I think that's a bug.

How to use it:

Prior to calling CERT_VerifyCert, one allocates a CERTVerifyLog and 
initializes it, e.g.

    CERTVerifyLog log;

    log.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if ( log.arena == NULL ) {
        goto done;
    }
    log.head = NULL;
    log.tail = NULL;
    log.count = 0;

Then one passes it to CERT_VerifyCert, like so:

    CERT_VerifyCert(cert->dbhandle, cert, PR_TRUE,
                         certUsage, PR_GMT(), proto_win, &log);
                                                         ^^^^

Afterwords, one checks for errors and handles them, as in this example:

    if ( log.count > 0 ) {
        CERTVerifyLogNode *node = log.head;

        while ( node ) {
            int error = node->error;

            /* deal with the error code */

            /* free the reference to the cert */
            CERT_DestroyCertificate(node->cert);

            node = node->next;
        }
    } /* end of log.count > 0 */


Finally, one frees the arena from which the error log nodes may have been
allocated:

    if ( log.arena != NULL ) {
        PORT_FreeArena(log.arena, PR_FALSE);
    }


The "arg" in the log node depends on the error code.  Here is a list of the
error codes that return non-null values for arg, and what the args mean:

SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID  
        arg is the path length limit (an int) that was exceeded

SEC_ERROR_INADEQUATE_KEY_USAGE
        arg is the required key usage (an int)

SEC_ERROR_EXPIRED_CERTIFICATE
        arg is a SECCertTimeValidity value (an enumerated type)

SEC_ERROR_INADEQUATE_CERT_TYPE
        arg is the requiredCertType (an int)

SEC_ERROR_UNTRUSTED_CERT
        arg is the trust flags (an int, bits have individual meaning)

In no case is "arg" ever a pointer, even though it is declared as a void *.
Component: Tools → Documentation
Changed the QA contact to Bishakha.
QA Contact: sonja.mirtitsch → bishakhabanerjee
I think one or more NSS commands now use the error log feature, so maybe this
bug is now resolved fixed.
Priority: -- → P3
QA Contact: bishakhabanerjee → jason.m.reid
Assignee: wtchang → nobody
QA Contact: jason.m.reid → documentation
Severity: normal → S3
Status: NEW → RESOLVED
Closed: 4 months ago
Resolution: --- → INACTIVE
You need to log in before you can comment on or make changes to this bug.