/* ssl-criteria.c, part of netpipes: network pipe utilities Copyright (C) 1997-98 Robert Forsman This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include "common.h" #include #include #include #include #include #include "ssl-criteria.h" extern BIO *bio_err; /********************************************************************/ int replacement_X509_verify_cert(ctx) X509_STORE_CTX *ctx; { int ok; struct ssl_criteria_node *root; SSL *ssl; /*fprintf(stderr, "replacement X509_verify_cert\n");*/ ok = X509_verify_cert(ctx); if (!ok) return ok; ssl = (SSL*)X509_STORE_CTX_get_app_data(ctx); root = (struct ssl_criteria_node *)ssl->ctx->app_verify_arg; ok = ssl_criteria_audit_chain(ctx, root, (X509*)0); return ok; } /********************************************************************/ static void del_node(); static void del_node_gamma(condemned) struct ssl_criteria_node *condemned; { int i; for (i=0; iu.gamma.nterms; i++) { del_node(condemned->u.gamma.terms[i]); } free( (char*)condemned->u.gamma.terms ); free( (char*)condemned ); } static void del_node_unary(condemned) struct ssl_criteria_node *condemned; { del_node(condemned->u.unary.arg); free( (char*)condemned ); } static void del_node(condemned) struct ssl_criteria_node *condemned; { if (condemned == 0) return; switch (condemned->type) { case SSL_CRITERIA_AND: case SSL_CRITERIA_OR: del_node_gamma(condemned); case SSL_CRITERIA_UNARY: del_node_unary(condemned); break; case SSL_CRITERIA_PRIMITIVE: free((char*)condemned); break; } } /********************************************************************/ static struct ssl_criteria_node * new_gamma(type, nterms) enum ssl_criteria_node_type type; int nterms; { struct ssl_criteria_node * rval; rval = (struct ssl_criteria_node *)malloc(sizeof(*rval)); if (rval == 0) { fprintf(stderr, "%s: malloc(%d) failed\n", progname, sizeof(*rval)); return 0; } rval->type = type; rval->u.gamma.terms = (struct ssl_criteria_node **)malloc(nterms * sizeof(*rval->u.gamma.terms) ); if (rval->u.gamma.terms == 0) { fprintf(stderr, "%s: malloc(%d) failed\n", progname, sizeof(*rval)); free(rval); return 0; } rval->u.gamma.nterms = nterms; return rval; } static struct ssl_criteria_node * new_unary(type) enum ssl_criteria_unary_type type; { struct ssl_criteria_node * rval; rval = (struct ssl_criteria_node *)malloc(sizeof(*rval)); if (rval == 0) { fprintf(stderr, "%s: malloc(%d) failed\n", progname, sizeof(*rval)); return 0; } rval->type = SSL_CRITERIA_UNARY; rval->u.unary.type = type; rval->u.unary.arg = 0; rval->u.unary.i = 0; return rval; } static struct ssl_criteria_node * new_primitive(type) enum ssl_criteria_primitive_type type; { struct ssl_criteria_node * rval; rval = (struct ssl_criteria_node *)malloc(sizeof(*rval)); if (rval == 0) { fprintf(stderr, "%s: malloc(%d) failed\n", progname, sizeof(*rval)); return 0; } rval->type = SSL_CRITERIA_PRIMITIVE; rval->u.primitive.type = type; rval->u.primitive.s = 0; rval->u.primitive.i = 0; rval->u.primitive.x = 0; return rval; } /********************************************************************/ /********************************************************************/ /* convenience functions for building expression trees */ /********************************************************************/ struct ssl_criteria_node * ssl_criteria_node_and( lhs, rhs ) struct ssl_criteria_node * lhs; struct ssl_criteria_node * rhs; { if (lhs == 0) return rhs; if (rhs == 0) return lhs; if (lhs->type == SSL_CRITERIA_AND) { struct ssl_criteria_node **new_terms; int nterms=lhs->u.gamma.nterms; int i; if (rhs->type == SSL_CRITERIA_AND) { nterms += rhs->u.gamma.nterms; } else { nterms ++; } new_terms = (struct ssl_criteria_node **)malloc(sizeof(*new_terms) * nterms ); for (i=0; iu.gamma.nterms; i++) { new_terms[i] = lhs->u.gamma.terms[i]; } if (rhs->type == SSL_CRITERIA_AND) { for (i=0; iu.gamma.nterms; i++) { new_terms[i+lhs->u.gamma.nterms] = rhs->u.gamma.terms[i]; } free(rhs->u.gamma.terms); free(rhs); rhs = 0; } else { new_terms[lhs->u.gamma.nterms] = rhs; } free(lhs->u.gamma.terms); lhs->u.gamma.terms = new_terms; lhs->u.gamma.nterms = nterms; return lhs; } else if (rhs->type == SSL_CRITERIA_AND) { return ssl_criteria_node_and(rhs, lhs); /* I love commutativity */ } else { struct ssl_criteria_node * rval; rval = new_gamma(SSL_CRITERIA_AND, 2); if (rval ==0) return 0; rval->u.gamma.terms[0] = lhs; rval->u.gamma.terms[1] = rhs; return rval; } } struct ssl_criteria_node * ssl_criteria_node_or( lhs, rhs ) struct ssl_criteria_node * lhs; struct ssl_criteria_node * rhs; { if (lhs == 0) return rhs; if (rhs == 0) return lhs; if (lhs->type == SSL_CRITERIA_OR) { struct ssl_criteria_node **new_terms; int nterms=lhs->u.gamma.nterms; int i; if (rhs->type == SSL_CRITERIA_OR) { nterms += rhs->u.gamma.nterms; } else { nterms ++; } new_terms = (struct ssl_criteria_node **)malloc(sizeof(*new_terms) * nterms ); for (i=0; iu.gamma.nterms; i++) { new_terms[i] = lhs->u.gamma.terms[i]; } if (rhs->type == SSL_CRITERIA_OR) { for (i=0; iu.gamma.nterms; i++) { new_terms[i+lhs->u.gamma.nterms] = rhs->u.gamma.terms[i]; } free(rhs->u.gamma.terms); free(rhs); rhs = 0; } else { new_terms[lhs->u.gamma.nterms] = rhs; } free(lhs->u.gamma.terms); lhs->u.gamma.terms = new_terms; lhs->u.gamma.nterms = nterms; return lhs; } else if (rhs->type == SSL_CRITERIA_OR) { return ssl_criteria_node_or(rhs, lhs); /* I love commutativity */ } else { struct ssl_criteria_node * rval; rval = new_gamma(SSL_CRITERIA_OR, 2); if (rval ==0) return 0; rval->u.gamma.terms[0] = lhs; rval->u.gamma.terms[1] = rhs; return rval; } } /********************************************************************/ /********************************************************************/ /* helper functions to postprocess nodes */ /********************************************************************/ struct ssl_criteria_node *postprocess_parse_PUBKEY_MATCH_CERT(node) struct ssl_criteria_node *node; { char *fname = node->u.primitive.s; BIO *in; X509 *x; in = BIO_new(BIO_s_file()); if (in == 0) { fprintf(stderr, "%s: Unable to allocate BIO\n", progname); return 0; } if (0 >= BIO_read_filename(in, fname)) { fprintf(stderr, "%s: Unable to open %s as BIO for reading\n", progname, fname); return 0; } x = PEM_read_bio_X509(in, NULL, (int(*)())0); BIO_free(in); if (x == 0) { fprintf(stderr, "%s: unable to load certificate from PEM file %s\n", progname, fname); return 0; } if (0==X509_PUBKEY_get(x->cert_info->key)) { fprintf(stderr, "%s: unable to extract public key from certificate in %s\n", progname, fname); X509_free(x); return 0; } node->u.primitive.x = x; return node; } /* transform the 32-digit hexadecimal string in node->u.primitive.s into a 16-byte block stored on the heap and replacing the value of .s . */ struct ssl_criteria_node *postprocess_parse_CERT_DIGEST(node) struct ssl_criteria_node *node; { char *hexstr = node->u.primitive.s; char *block; int i; node->u.primitive.md_type = EVP_md5(); node->u.primitive.i = 16; block = malloc(node->u.primitive.i); if (strlen(hexstr) != 2*node->u.primitive.i) { fprintf(stderr, "%s: %s is not a valid certificate digest it only has %d digits instead of %d\n", progname, hexstr, strlen(hexstr), 2*node->u.primitive.i); return 0; } for (i=0; iu.primitive.i; i++) { int tmp; if (1 != sscanf(hexstr+i*2, "%2x", &tmp)) { fprintf(stderr, "%s: parse error at offset %d in hex string %s\n", progname, i*2, hexstr); return 0; } block[i] = tmp; } node->u.primitive.s = block; /* will leak memory. */ return node; } /********************************************************************/ /********************************************************************/ /* Recursive descent parser functions */ /********************************************************************/ struct ssl_criteria_node * ssl_criteria_argv_rdp_unary(argv, argc, d_argc) char **argv; int argc; int *d_argc; { if (0==strcmp("!", argv[0]) || 0==strcmp("--not", argv[0])) { struct ssl_criteria_node *temp, *rval; int temp_delta; if (argc<2) { fprintf(stderr, "%s: %s requires an argument\n", progname, argv[0]); return 0; } temp = ssl_criteria_argv_rdp_term(argv+1, argc+1, &temp_delta); if (temp == 0) { return 0; } rval = new_unary(SSL_CRITERIA_NOT); rval->u.unary.arg = temp; *d_argc = temp_delta +1; return rval; } else if (0==strcmp("-d", argv[0]) || 0==strcmp("--depth", argv[0])) { struct ssl_criteria_node *temp, *rval; int temp_delta; long int_arg; char *endptr; if (argc<3) { fprintf(stderr, "%s: %s requires two arguments\n", progname, argv[0]); return 0; } int_arg = strtol(argv[1], &endptr, 0); if (argv[1][0]==0 || *endptr!=0) { fprintf(stderr, "%s: %s requires its first argument to be integer, not %s\n", progname, argv[0], argv[1]); } temp = ssl_criteria_argv_rdp_term(argv+2, argc+2, &temp_delta); if (temp == 0) { return 0; } rval = new_unary(SSL_CRITERIA_DEPTH); rval->u.unary.arg = temp; rval->u.unary.i = int_arg; /* yeah, a demotion */ *d_argc = temp_delta +2; return rval; } else { return ssl_criteria_argv_rdp_term(argv, argc, d_argc); } } struct ssl_criteria_node * ssl_criteria_argv_rdp_andlist(argv, argc, d_argc) char **argv; int argc; int *d_argc; { struct ssl_criteria_node *rval = 0; int temp_delta; *d_argc = 0; while (1) { struct ssl_criteria_node *item; item = ssl_criteria_argv_rdp_unary(argv, argc, &temp_delta); if (item==0) { del_node(rval); return 0; } rval = ssl_criteria_node_and(rval, item); if ( ! ( temp_delta < argc && ( 0== strcmp("-a", argv[temp_delta]) || 0== strcmp("--and", argv[temp_delta]) ) ) ) { *d_argc += temp_delta; break; } /* otherwise, get another term for the and-list */ temp_delta++; argv += temp_delta; argc -= temp_delta; *d_argc += temp_delta; } return rval; } struct ssl_criteria_node * ssl_criteria_argv_rdp_orlist(argv, argc, d_argc) char **argv; int argc; int *d_argc; { struct ssl_criteria_node *rval = 0; int temp_delta; *d_argc = 0; while (1) { struct ssl_criteria_node *item; item = ssl_criteria_argv_rdp_andlist(argv, argc, &temp_delta); if (item==0) { del_node(rval); return 0; } rval = ssl_criteria_node_or(rval, item); if ( ! ( temp_delta < argc && ( 0== strcmp("-o", argv[temp_delta]) || 0== strcmp("--or", argv[temp_delta]) ) ) ) { *d_argc += temp_delta; break; } /* otherwise, get another term for the or-list */ temp_delta++; argv += temp_delta; argc -= temp_delta; *d_argc += temp_delta; } return rval; } struct ssl_criteria_node * ssl_criteria_argv_rdp_parenlist(argv, argc, d_argc) char **argv; int argc; int *d_argc; { struct ssl_criteria_node *rval; *d_argc = 0; rval = ssl_criteria_argv_rdp_orlist(argv, argc, d_argc); if (rval==0) return rval; if ( *d_argcu.primitive.s = argv[1]; *d_argc = 2; } else if (0==strcmp(argv[0], "--country-name")) { if (argc<2) { fprintf(stderr, "%s: %s requires a string parameter\n", progname, argv[0]); return 0; } rval = new_primitive(SSL_CRITERIA_COUNTRY_NAME); if (rval==0) return 0; rval->u.primitive.s = argv[1]; *d_argc = 2; } else if (0==strcmp(argv[0], "--state-name") || 0==strcmp(argv[0], "--province-name")) { if (argc<2) { fprintf(stderr, "%s: %s requires a string parameter\n", progname, argv[0]); return 0; } rval = new_primitive(SSL_CRITERIA_STATE_NAME); if (rval==0) return 0; rval->u.primitive.s = argv[1]; *d_argc = 2; } else if (0==strcmp(argv[0], "--locality-name")) { if (argc<2) { fprintf(stderr, "%s: %s requires a string parameter\n", progname, argv[0]); return 0; } rval = new_primitive(SSL_CRITERIA_LOCALITY_NAME); if (rval==0) return 0; rval->u.primitive.s = argv[1]; *d_argc = 2; } else if (0==strcmp(argv[0], "--organization-name")) { if (argc<2) { fprintf(stderr, "%s: %s requires a string parameter\n", progname, argv[0]); return 0; } rval = new_primitive(SSL_CRITERIA_ORGANIZATION_NAME); if (rval==0) return 0; rval->u.primitive.s = argv[1]; *d_argc = 2; } else if (0==strcmp(argv[0], "--organizational-unit-name") || 0==strcmp(argv[0], "--department-name")) { if (argc<2) { fprintf(stderr, "%s: %s requires a string parameter\n", progname, argv[0]); return 0; } rval = new_primitive(SSL_CRITERIA_DEPARTMENT_NAME); if (rval==0) return 0; rval->u.primitive.s = argv[1]; *d_argc = 2; } else if (0==strcmp(argv[0], "--public-key-match-cert")) { if (argc<2) { fprintf(stderr, "%s: %s requires a file name parameter\n", progname, argv[0]); return 0; } rval = new_primitive(SSL_CRITERIA_PUBKEY_MATCH_CERT); if (rval==0) return 0; rval->u.primitive.s = argv[1]; if (0==postprocess_parse_PUBKEY_MATCH_CERT(rval)) { free(rval); return 0; } *d_argc = 2; } else if (0==strcmp(argv[0], "--cert-md5-digest")) { if (argc<2) { fprintf(stderr, "%s: %s requires a hex number argument\n", progname, argv[0]); return 0; } rval = new_primitive(SSL_CRITERIA_CERT_DIGEST); if (rval==0) return 0; rval->u.primitive.s = argv[1]; if (0==postprocess_parse_CERT_DIGEST(rval)) { free(rval); return 0; } *d_argc = 2; } else if (0==strcmp(argv[0], "--write-pem-cert")) { if (argc<2) { fprintf(stderr, "%s: %s requires a file name parameter\n", progname, argv[0]); return 0; } rval = new_primitive(SSL_CRITERIA_WRITE_PEM_CERT); if (rval==0) return 0; rval->u.primitive.s = argv[1]; *d_argc = 2; /****************************************************************/ } else { fprintf(stderr, "%s: unrecognized SSL criteria tree term `%s'\n", progname, argv[0]); return 0; } return rval; } /********************************************************************/ static int ssl_criteria_audit_chain_AND(ctx, node, curr_cert) X509_STORE_CTX *ctx; struct ssl_criteria_node *node; X509 *curr_cert; { int i; for (i=0; iu.gamma.nterms; i++) { if (!ssl_criteria_audit_chain(ctx, node->u.gamma.terms[i], curr_cert)) return 0; } return 1; } static int ssl_criteria_audit_chain_OR(ctx, node, curr_cert) X509_STORE_CTX *ctx; struct ssl_criteria_node *node; X509 *curr_cert; { int i; for (i=0; iu.gamma.nterms; i++) { if (ssl_criteria_audit_chain(ctx, node->u.gamma.terms[i], curr_cert)) return 1; } return 0; } /* returns 1 on success, 0 on failure or missing nid */ static int X509_NAME_compare_entry(a, nid, value) X509_NAME *a; int nid; char *value; { int i; int n; int type, num; X509_NAME_ENTRY *ne; unsigned char *q; for (i=0; ientries); i++) { ne=(X509_NAME_ENTRY *)sk_value(a->entries,i); n=OBJ_obj2nid(ne->object); if (n!=nid) continue; /* skip this one */ type = ne->value->type; num = ne->value->length; q = ne->value->data; if (type == V_ASN1_PRINTABLESTRING) { if (0==strncmp(value, q, num)) { return 1; /* match! */ } else { return 0; /* failure */ } } else { fprintf(stderr, "unhandled X509_NAME_ENTRY type: %d\n", type); return -1; /* what the heck am I supposed to do? */ } } return 0; } static int compare_public_keys(cert1, cert2) X509 *cert1, *cert2; { EVP_PKEY *a = X509_PUBKEY_get(cert1->cert_info->key); EVP_PKEY *b = X509_PUBKEY_get(cert2->cert_info->key); if (a==0 || b==0) { fprintf(stderr, "%s: unable to extract public key from certificate (0x%lx, 0x%lx)\n", progname, (long)a, (long)b); abort(); } if (a->type != b->type) return 0; /* not even same key scheme */ switch (a->type) { case EVP_PKEY_RSA: /* BN_print(bio_c_out, a->pkey.rsa->n); BIO_printf(bio_c_out, " == "); BN_print(bio_c_out, b->pkey.rsa->n); BIO_printf(bio_c_out, "\n"); BN_print(bio_c_out, a->pkey.rsa->e); BIO_printf(bio_c_out, " == "); BN_print(bio_c_out, b->pkey.rsa->e); BIO_printf(bio_c_out, "\n"); */ return 0==BN_cmp(a->pkey.rsa->n, b->pkey.rsa->n) && 0==BN_cmp(a->pkey.rsa->e, b->pkey.rsa->e); break; case EVP_PKEY_DSA: return 0==BN_cmp(a->pkey.dsa->p, b->pkey.dsa->p) && 0==BN_cmp(a->pkey.dsa->q, b->pkey.dsa->q) && 0==BN_cmp(a->pkey.dsa->g, b->pkey.dsa->g) && 0==BN_cmp(a->pkey.dsa->pub_key, b->pkey.dsa->pub_key); break; default: fprintf(stderr, "%s: Unhandled/unknown key type %d\n", progname, a->type); abort(); break; } } static void dump_hex_memblock(fp, b, n) FILE *fp; char *b; int n; { int i; for (i=0; ii) byte block stored in prim->s . */ static int compare_cert_digest(cert, prim) X509 *cert; struct ssl_criteria_node_primitive *prim; { int len=0; char digest1[EVP_MAX_MD_SIZE]; char *digest2 = prim->s; if ( ! X509_digest(cert, prim->md_type, digest1, &len) ) { fprintf(stderr, "%s: Unable to compute digest for certificate.\n", progname); return 0; } if (len > sizeof(digest1)) { fprintf(stderr, "%s: digest size was %d, bigger than %d! PANIC!\n", progname, len, sizeof(digest1)); abort(); } if (len != prim->i) { return 0; /* digest was a different size */ } if ( 0 == memcmp(digest1, digest2, len) ) { return 1; } else { fprintf(stderr, "%s: ", progname); dump_hex_memblock(stderr, digest1, len); fprintf(stderr, " != "); dump_hex_memblock(stderr, digest2, len); fprintf(stderr, " .\n"); return 0; } } static int write_pem_cert(cert, fname) X509 *cert; char *fname; { BIO *b; b = BIO_new(BIO_s_file()); if (b==0) { ERR_print_errors(bio_err); return 0; } if ( 0 >= BIO_write_filename(b, fname)) { fprintf(stderr, "%s: unable to open %s for write: ", progname, fname); perror(""); return 0; } PEM_write_bio_X509(b, cert); BIO_free(b); return 1; } /********/ static int ssl_criteria_audit_chain_UNARY(ctx, node, curr_cert) X509_STORE_CTX *ctx; struct ssl_criteria_node *node; X509 *curr_cert; { struct ssl_criteria_node_unary *u = &node->u.unary; switch (u->type) { case SSL_CRITERIA_NOT: { int rval; rval = ssl_criteria_audit_chain(ctx, u->arg, curr_cert); if (rval<0) return rval; else return !rval; break; /* notreached */ } case SSL_CRITERIA_DEPTH: if (u->i < sk_num(ctx->chain)) { curr_cert = (X509*)sk_value(ctx->chain, u->i); return ssl_criteria_audit_chain(ctx, u->arg, curr_cert); } else { fprintf(stderr, "%s: certificate chain has depth %d (--depth %d is too deep)\n", progname, sk_num(ctx->chain), u->i); return -1; } break; /* notreached */ default: /* notreached */ return -1; } } static int ssl_criteria_audit_chain_PRIMITIVE(ctx, node, curr_cert) X509_STORE_CTX *ctx; struct ssl_criteria_node *node; X509 *curr_cert; { struct ssl_criteria_node_primitive *prim = &node->u.primitive; switch (prim->type) { case SSL_CRITERIA_COMMON_NAME: return X509_NAME_compare_entry(X509_get_subject_name(curr_cert), NID_commonName, prim->s); case SSL_CRITERIA_COUNTRY_NAME: return X509_NAME_compare_entry(X509_get_subject_name(curr_cert), NID_countryName, prim->s); case SSL_CRITERIA_STATE_NAME: return X509_NAME_compare_entry(X509_get_subject_name(curr_cert), NID_stateOrProvinceName, prim->s); case SSL_CRITERIA_LOCALITY_NAME: return X509_NAME_compare_entry(X509_get_subject_name(curr_cert), NID_localityName, prim->s); case SSL_CRITERIA_ORGANIZATION_NAME: return X509_NAME_compare_entry(X509_get_subject_name(curr_cert), NID_organizationName, prim->s); case SSL_CRITERIA_DEPARTMENT_NAME: return X509_NAME_compare_entry(X509_get_subject_name(curr_cert), NID_organizationalUnitName, prim->s); case SSL_CRITERIA_PUBKEY_MATCH_CERT: return compare_public_keys(curr_cert, prim->x); case SSL_CRITERIA_CERT_DIGEST: return compare_cert_digest(curr_cert, prim); case SSL_CRITERIA_WRITE_PEM_CERT: return write_pem_cert(curr_cert, prim->s); break; default: fprintf(stderr, "%s: unknown criteria primitive type %d.\n", progname, prim->type); return -1; } } int ssl_criteria_audit_chain(ctx, node, curr_cert) X509_STORE_CTX *ctx; struct ssl_criteria_node *node; X509 *curr_cert; { if (curr_cert==0) { curr_cert = (X509*)sk_value(ctx->chain, 0); } switch (node->type) { case SSL_CRITERIA_AND: return ssl_criteria_audit_chain_AND(ctx, node, curr_cert); case SSL_CRITERIA_OR: return ssl_criteria_audit_chain_OR(ctx, node, curr_cert); case SSL_CRITERIA_UNARY: return ssl_criteria_audit_chain_UNARY(ctx, node, curr_cert); case SSL_CRITERIA_PRIMITIVE: return ssl_criteria_audit_chain_PRIMITIVE(ctx, node, curr_cert); default: fprintf(stderr, "%s: unknown criteria node type %d.\n", progname, node->type); return 0; } } /********************************************************************/ /********************************************************************/ /* debugging aids */ /********************************************************************/ static void n_spaces(fp, n) FILE *fp; int n; { while (n>0) { putc(' ', fp); n--; } } void debug_print_criteria_node(fp, root, indent) FILE *fp; struct ssl_criteria_node *root; int indent; { int i; switch (root->type) { case SSL_CRITERIA_AND: n_spaces(fp, indent); fprintf(fp, "AND (\n"); for (i=0; iu.gamma.nterms; i++) { debug_print_criteria_node(fp, root->u.gamma.terms[i], indent+2); } n_spaces(fp, indent); fprintf(fp, ")\n"); break; case SSL_CRITERIA_OR: n_spaces(fp, indent); fprintf(fp, "OR (\n"); for (i=0; iu.gamma.nterms; i++) { debug_print_criteria_node(fp, root->u.gamma.terms[i], indent+2); } n_spaces(fp, indent); fprintf(fp, ")\n"); break; case SSL_CRITERIA_UNARY: n_spaces(fp, indent); fprintf(fp, "UNARY/ "); switch (root->u.unary.type) { case SSL_CRITERIA_NOT: fprintf(fp, "NOT (\n"); break; case SSL_CRITERIA_DEPTH: fprintf(fp, "DEPTH=%d (\n", root->u.unary.i); break; } debug_print_criteria_node(fp, root->u.unary.arg, indent+2); n_spaces(fp, indent); fprintf(fp, ")\n"); break; case SSL_CRITERIA_PRIMITIVE: n_spaces(fp, indent); fprintf(fp, "PRIMITIVE/ "); switch (root->u.primitive.type) { case SSL_CRITERIA_COMMON_NAME: fprintf(fp, "COMMON_NAME == %s", root->u.primitive.s); break; case SSL_CRITERIA_COUNTRY_NAME: fprintf(fp, "COUNTRY_NAME == %s", root->u.primitive.s); break; case SSL_CRITERIA_STATE_NAME: fprintf(fp, "STATE_NAME == %s", root->u.primitive.s); break; case SSL_CRITERIA_LOCALITY_NAME: fprintf(fp, "LOCALITY_NAME == %s", root->u.primitive.s); break; case SSL_CRITERIA_ORGANIZATION_NAME: fprintf(fp, "ORGANIZATION_NAME == %s", root->u.primitive.s); break; case SSL_CRITERIA_DEPARTMENT_NAME: fprintf(fp, "DEPARTMENT_NAME == %s", root->u.primitive.s); break; case SSL_CRITERIA_PUBKEY_MATCH_CERT: fprintf(fp, "PUBKEY_MATCH_CERT(%s)", root->u.primitive.s); break; case SSL_CRITERIA_CERT_DIGEST: fprintf(fp, "PUBKEY_CERT_DIGEST == "); dump_hex_memblock(fp, root->u.primitive.s, root->u.primitive.i); break; case SSL_CRITERIA_WRITE_PEM_CERT: fprintf(fp, "WRITE_PEM_CERT(%s)", root->u.primitive.s); break; } fprintf(fp, "\n"); break; } }