Fix certauth built-in module returns

The PKINIT certauth eku module should never authoritatively authorize
a certificate, because an extended key usage does not establish a
relationship between the certificate and any specific user; it only
establishes that the certificate was created for PKINIT client
authentication.  Therefore, pkinit_eku_authorize() should return
KRB5_PLUGIN_NO_HANDLE on success, not 0.

The certauth san module should pass if it does not find any SANs of
the types it can match against; the presence of other types of SANs
should not cause it to explicitly deny a certificate.  Check for an
empty result from crypto_retrieve_cert_sans() in verify_client_san(),
instead of returning ENOENT from crypto_retrieve_cert_sans() when
there are no SANs at all.

ticket: 8561
authorize_cert(krb5_context context, certauth_handle *certauth_modules,
               pkinit_kdc_context plgctx, pkinit_kdc_req_context reqctx,
               krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
               krb5_principal client)
{
    krb5_error_code ret;
    certauth_handle h;
    struct certauth_req_opts opts;
    krb5_boolean accepted = FALSE;
    uint8_t *cert;
    size_t i, cert_len;
    void *db_ent = NULL;
    char **ais = NULL, **ai = NULL;

    /* Re-encode the received certificate into DER, which is extra work, but
     * avoids creating an X.509 library dependency in the interface. */
    ret = crypto_encode_der_cert(context, reqctx->cryptoctx, &cert, &cert_len);
    if (ret)
        goto cleanup;

    /* Set options for the builtin module. */
    opts.plgctx = plgctx;
    opts.reqctx = reqctx;
    opts.cb = cb;
    opts.rock = rock;

    db_ent = cb->client_entry(context, rock);

    /*
     * Check the certificate against each certauth module.  For the certificate
     * to be authorized at least one module must return 0, and no module can an
     * error code other than KRB5_PLUGIN_NO_HANDLE (pass).  Add indicators from
     * modules that return 0 or pass.
     */
    ret = KRB5_PLUGIN_NO_HANDLE;
    for (i = 0; certauth_modules != NULL && certauth_modules[i] != NULL; i++) {
        h = certauth_modules[i];
        TRACE_PKINIT_SERVER_CERT_AUTH(context, h->vt.name);
        ret = h->vt.authorize(context, h->moddata, cert, cert_len, client,
                              &opts, db_ent, &ais);
        if (ret == 0)
            accepted = TRUE;
        else if (ret != KRB5_PLUGIN_NO_HANDLE)
            goto cleanup;

        if (ais != NULL) {
            /* Assert authentication indicators from the module. */
            for (ai = ais; *ai != NULL; ai++) {
                ret = cb->add_auth_indicator(context, rock, *ai);
                if (ret)
                    goto cleanup;
            }
            h->vt.free_ind(context, h->moddata, ais);
            ais = NULL;
        }
    }

    ret = accepted ? 0 : KRB5KDC_ERR_CLIENT_NAME_MISMATCH;

cleanup:
    free(cert);
    return ret;
}

authorize_cert(krb5_context context, certauth_handle *certauth_modules,
               pkinit_kdc_context plgctx, pkinit_kdc_req_context reqctx,
               krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
               krb5_principal client)
{
    krb5_error_code ret;
    certauth_handle h;
    struct certauth_req_opts opts;
    krb5_boolean accepted = FALSE;
    uint8_t *cert;
    size_t i, cert_len;
    void *db_ent = NULL;
    char **ais = NULL, **ai = NULL;

    /* Re-encode the received certificate into DER, which is extra work, but
     * avoids creating an X.509 library dependency in the interface. */
    ret = crypto_encode_der_cert(context, reqctx->cryptoctx, &cert, &cert_len);
    if (ret)
        goto cleanup;

    /* Set options for the builtin module. */
    opts.plgctx = plgctx;
    opts.reqctx = reqctx;
    opts.cb = cb;
    opts.rock = rock;

    db_ent = cb->client_entry(context, rock);

    /*
     * Check the certificate against each certauth module.  For the certificate
     * to be authorized at least one module must return 0, and no module can an
     * error code other than KRB5_PLUGIN_NO_HANDLE (pass).  Add indicators from
     * modules that return 0 or pass.
     */
    ret = KRB5_PLUGIN_NO_HANDLE;
    for (i = 0; certauth_modules != NULL && certauth_modules[i] != NULL; i++) {
        h = certauth_modules[i];
        TRACE_PKINIT_SERVER_CERT_AUTH(context, h->vt.name);
        ret = h->vt.authorize(context, h->moddata, cert, cert_len, client,
                              &opts, db_ent, &ais);
        if (ret == 0)
            accepted = TRUE;
        else if (ret != KRB5_PLUGIN_NO_HANDLE)
            goto cleanup;

        if (ais != NULL) {
            /* Assert authentication indicators from the module. */
            for (ai = ais; *ai != NULL; ai++) {
                ret = cb->add_auth_indicator(context, rock, *ai);
                if (ret)
                    goto cleanup;
            }
            h->vt.free_ind(context, h->moddata, ais);
            ais = NULL;
        }
    }

    ret = accepted ? 0 : KRB5KDC_ERR_CLIENT_NAME_MISMATCH;

cleanup:
    free(cert);
    return ret;
}
