/* Support of X.509 attribute certificates * Copyright (C) 2002 Ueli Galizzi, Ariane Seiler * Copyright (C) 2003 Martin Berner, Lukas Suter * * 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. See . * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ac.h" #include "x509.h" #include "crl.h" #include "ca.h" #include "certs.h" #include "log.h" #include "whack.h" #include "fetch.h" /** * Chained list of X.509 attribute certificates */ static x509acert_t *x509acerts = NULL; /** * Chained list of ietfAttributes */ static ietfAttrList_t *ietfAttributes = NULL; /** * ASN.1 definition of ietfAttrSyntax */ static const asn1Object_t ietfAttrSyntaxObjects[] = { { 0, "ietfAttrSyntax", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ { 1, "policyAuthority", ASN1_CONTEXT_C_0, ASN1_OPT | ASN1_BODY }, /* 1 */ { 1, "end opt", ASN1_EOC, ASN1_END }, /* 2 */ { 1, "values", ASN1_SEQUENCE, ASN1_LOOP }, /* 3 */ { 2, "octets", ASN1_OCTET_STRING, ASN1_OPT | ASN1_BODY }, /* 4 */ { 2, "end choice", ASN1_EOC, ASN1_END }, /* 5 */ { 2, "oid", ASN1_OID, ASN1_OPT | ASN1_BODY }, /* 6 */ { 2, "end choice", ASN1_EOC, ASN1_END }, /* 7 */ { 2, "string", ASN1_UTF8STRING, ASN1_OPT | ASN1_BODY }, /* 8 */ { 2, "end choice", ASN1_EOC, ASN1_END }, /* 9 */ { 1, "end loop", ASN1_EOC, ASN1_END }, /* 10 */ { 0, "exit", ASN1_EOC, ASN1_EXIT } }; #define IETF_ATTR_OCTETS 4 #define IETF_ATTR_OID 6 #define IETF_ATTR_STRING 8 /** * ASN.1 definition of roleSyntax */ static const asn1Object_t roleSyntaxObjects[] = { { 0, "roleSyntax", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ { 1, "roleAuthority", ASN1_CONTEXT_C_0, ASN1_OPT | ASN1_OBJ }, /* 1 */ { 1, "end opt", ASN1_EOC, ASN1_END }, /* 2 */ { 1, "roleName", ASN1_CONTEXT_C_1, ASN1_OBJ }, /* 3 */ { 0, "exit", ASN1_EOC, ASN1_EXIT } }; /** * ASN.1 definition of an X509 attribute certificate */ static const asn1Object_t acObjects[] = { { 0, "AttributeCertificate", ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */ { 1, "AttributeCertificateInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 1 */ { 2, "version", ASN1_INTEGER, ASN1_DEF | ASN1_BODY }, /* 2 */ { 2, "holder", ASN1_SEQUENCE, ASN1_NONE }, /* 3 */ { 3, "baseCertificateID", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 4 */ { 4, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 5 */ { 4, "serial", ASN1_INTEGER, ASN1_BODY }, /* 6 */ { 4, "issuerUID", ASN1_BIT_STRING, ASN1_OPT | ASN1_BODY }, /* 7 */ { 4, "end opt", ASN1_EOC, ASN1_END }, /* 8 */ { 3, "end opt", ASN1_EOC, ASN1_END }, /* 9 */ { 3, "entityName", ASN1_CONTEXT_C_1, ASN1_OPT | ASN1_OBJ }, /* 10 */ { 3, "end opt", ASN1_EOC, ASN1_END }, /* 11 */ { 3, "objectDigestInfo", ASN1_CONTEXT_C_2, ASN1_OPT }, /* 12 */ { 4, "digestedObjectType", ASN1_ENUMERATED, ASN1_BODY }, /* 13 */ { 4, "otherObjectTypeID", ASN1_OID, ASN1_OPT | ASN1_BODY }, /* 14 */ { 4, "end opt", ASN1_EOC, ASN1_END }, /* 15 */ { 4, "digestAlgorithm", ASN1_EOC, ASN1_RAW }, /* 16 */ { 3, "end opt", ASN1_EOC, ASN1_END }, /* 17 */ { 2, "v2Form", ASN1_CONTEXT_C_0, ASN1_NONE }, /* 18 */ { 3, "issuerName", ASN1_SEQUENCE, ASN1_OPT | ASN1_OBJ }, /* 19 */ { 3, "end opt", ASN1_EOC, ASN1_END }, /* 20 */ { 3, "baseCertificateID", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 21 */ { 4, "issuerSerial", ASN1_SEQUENCE, ASN1_NONE }, /* 22 */ { 5, "issuer", ASN1_SEQUENCE, ASN1_OBJ }, /* 23 */ { 5, "serial", ASN1_INTEGER, ASN1_BODY }, /* 24 */ { 5, "issuerUID", ASN1_BIT_STRING, ASN1_OPT | ASN1_BODY }, /* 25 */ { 5, "end opt", ASN1_EOC, ASN1_END }, /* 26 */ { 3, "end opt", ASN1_EOC, ASN1_END }, /* 27 */ { 3, "objectDigestInfo", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 28 */ { 4, "digestInfo", ASN1_SEQUENCE, ASN1_OBJ }, /* 29 */ { 5, "digestedObjectType", ASN1_ENUMERATED, ASN1_BODY }, /* 30 */ { 5, "otherObjectTypeID", ASN1_OID, ASN1_OPT | ASN1_BODY }, /* 31 */ { 5, "end opt", ASN1_EOC, ASN1_END }, /* 32 */ { 5, "digestAlgorithm", ASN1_EOC, ASN1_RAW }, /* 33 */ { 3, "end opt", ASN1_EOC, ASN1_END }, /* 34 */ { 2, "signature", ASN1_EOC, ASN1_RAW }, /* 35 */ { 2, "serialNumber", ASN1_INTEGER, ASN1_BODY }, /* 36 */ { 2, "attrCertValidityPeriod", ASN1_SEQUENCE, ASN1_NONE }, /* 37 */ { 3, "notBeforeTime", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 38 */ { 3, "notAfterTime", ASN1_GENERALIZEDTIME, ASN1_BODY }, /* 39 */ { 2, "attributes", ASN1_SEQUENCE, ASN1_LOOP }, /* 40 */ { 3, "attribute", ASN1_SEQUENCE, ASN1_NONE }, /* 41 */ { 4, "type", ASN1_OID, ASN1_BODY }, /* 42 */ { 4, "values", ASN1_SET, ASN1_LOOP }, /* 43 */ { 5, "value", ASN1_EOC, ASN1_RAW }, /* 44 */ { 4, "end loop", ASN1_EOC, ASN1_END }, /* 45 */ { 2, "end loop", ASN1_EOC, ASN1_END }, /* 46 */ { 2, "extensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 47 */ { 3, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 48 */ { 4, "extnID", ASN1_OID, ASN1_BODY }, /* 49 */ { 4, "critical", ASN1_BOOLEAN, ASN1_DEF | ASN1_BODY }, /* 50 */ { 4, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 51 */ { 2, "end loop", ASN1_EOC, ASN1_END }, /* 52 */ { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 53 */ { 1, "signatureValue", ASN1_BIT_STRING, ASN1_BODY }, /* 54 */ { 0, "exit", ASN1_EOC, ASN1_EXIT } }; #define AC_OBJ_CERTIFICATE 0 #define AC_OBJ_CERTIFICATE_INFO 1 #define AC_OBJ_VERSION 2 #define AC_OBJ_HOLDER_ISSUER 5 #define AC_OBJ_HOLDER_SERIAL 6 #define AC_OBJ_ENTITY_NAME 10 #define AC_OBJ_ISSUER_NAME 19 #define AC_OBJ_ISSUER 23 #define AC_OBJ_SIG_ALG 35 #define AC_OBJ_SERIAL_NUMBER 36 #define AC_OBJ_NOT_BEFORE 38 #define AC_OBJ_NOT_AFTER 39 #define AC_OBJ_ATTRIBUTE_TYPE 42 #define AC_OBJ_ATTRIBUTE_VALUE 44 #define AC_OBJ_EXTN_ID 49 #define AC_OBJ_CRITICAL 50 #define AC_OBJ_EXTN_VALUE 51 #define AC_OBJ_ALGORITHM 53 #define AC_OBJ_SIGNATURE 54 const x509acert_t empty_ac = { NULL , /* *next */ 0 , /* installed */ { NULL, 0 }, /* certificate */ { NULL, 0 }, /* certificateInfo */ 1 , /* version */ /* holder */ /* baseCertificateID */ { NULL, 0 }, /* holderIssuer */ { NULL, 0 }, /* holderSerial */ /* entityName */ { NULL, 0 }, /* generalNames */ /* v2Form */ { NULL, 0 }, /* issuerName */ /* signature */ OID_UNKNOWN, /* sigAlg */ { NULL, 0 }, /* serialNumber */ /* attrCertValidityPeriod */ 0 , /* notBefore */ 0 , /* notAfter */ /* attributes */ NULL , /* charging */ NULL , /* groups */ /* extensions */ { NULL, 0 }, /* authKeyID */ { NULL, 0 }, /* authKeySerialNumber */ FALSE , /* noRevAvail */ /* signatureAlgorithm */ OID_UNKNOWN, /* algorithm */ { NULL, 0 }, /* signature */ }; /** * compare two ietfAttributes, returns zero if a equals b * negative/positive if a is earlier/later in the alphabet than b */ static int cmp_ietfAttr(ietfAttr_t *a,ietfAttr_t *b) { int cmp_len, len, cmp_value; /* cannot compare OID with STRING or OCTETS attributes */ if (a->kind == IETF_ATTRIBUTE_OID && b->kind != IETF_ATTRIBUTE_OID) return 1; cmp_len = a->value.len - b->value.len; len = (cmp_len < 0)? a->value.len : b->value.len; cmp_value = memcmp(a->value.ptr, b->value.ptr, len); return (cmp_value == 0)? cmp_len : cmp_value; } /** * add an ietfAttribute to the chained list */ static ietfAttr_t* add_ietfAttr(ietfAttr_t *attr) { ietfAttrList_t **listp = &ietfAttributes; ietfAttrList_t *list = *listp; int cmp = -1; while (list != NULL) { cmp = cmp_ietfAttr(attr, list->attr); if (cmp <= 0) break; listp = &list->next; list = *listp; } if (cmp == 0) { /* attribute already exists, increase count */ free(attr); list->attr->count++; return list->attr; } else { ietfAttrList_t *el = malloc_thing(ietfAttrList_t); /* new attribute, unshare value */ attr->value = chunk_clone(attr->value); attr->count = 1; time(&attr->installed); el->attr = attr; el->next = list; *listp = el; return attr; } } /** * decodes a comma separated list of group attributes */ void decode_groups(char *groups, ietfAttrList_t **listp) { if (groups == NULL) return; while (strlen(groups) > 0) { char *end; char *next = strchr(groups, ','); if (next == NULL) end = next = groups + strlen(groups); else end = next++; /* eat preceeding whitespace */ while (groups < end && *groups == ' ') groups++; /* eat trailing whitespace */ while (end > groups && *(end-1) == ' ') end--; if (groups < end) { ietfAttr_t *attr = malloc_thing(ietfAttr_t); ietfAttrList_t *el = malloc_thing(ietfAttrList_t); attr->kind = IETF_ATTRIBUTE_STRING; attr->value.ptr = groups; attr->value.len = end - groups; attr->count = 0; el->attr = add_ietfAttr(attr); el->next = *listp; *listp = el; } groups = next; } } static bool same_attribute(const ietfAttr_t *a, const ietfAttr_t *b) { return (a->kind == b->kind && a->value.len == b->value.len && memeq(a->value.ptr, b->value.ptr, b->value.len)); } bool group_membership(const ietfAttrList_t *peer_list , const char *conn , const ietfAttrList_t *conn_list) { if (conn_list == NULL) return TRUE; while (peer_list != NULL) { const ietfAttr_t *peer_attr = peer_list->attr; const ietfAttrList_t *list = conn_list; while (list != NULL) { ietfAttr_t *conn_attr = list->attr; if (same_attribute(conn_attr, peer_attr)) { DBG(DBG_CONTROL, DBG_log("%s: peer matches group '%.*s'" , conn , (int)peer_attr->value.len, peer_attr->value.ptr) ) return TRUE; } list = list->next; } peer_list = peer_list->next; } DBG(DBG_CONTROL, DBG_log("%s: peer doesn't match any group", conn) ) return FALSE; } void unshare_ietfAttrList(ietfAttrList_t **listp) { ietfAttrList_t *list = *listp; while (list != NULL) { ietfAttrList_t *el = malloc_thing(ietfAttrList_t); el->attr = list->attr; el->attr->count++; el->next = NULL; *listp = el; listp = &el->next; list = list->next; } } /** * Parses ietfAttrSyntax */ static ietfAttrList_t* parse_ietfAttrSyntax(chunk_t blob, int level0) { asn1_parser_t *parser; chunk_t object; int objectID; ietfAttrList_t *list = NULL; parser = asn1_parser_create(ietfAttrSyntaxObjects, blob); parser->set_top_level(parser, level0); while (parser->iterate(parser, &objectID, &object)) { switch (objectID) { case IETF_ATTR_OCTETS: case IETF_ATTR_OID: case IETF_ATTR_STRING: { ietfAttr_t *attr = malloc_thing(ietfAttr_t); ietfAttrList_t *el = malloc_thing(ietfAttrList_t); attr->kind = (objectID - IETF_ATTR_OCTETS) / 2; attr->value = object; attr->count = 0; el->attr = add_ietfAttr(attr); el->next = list; list = el; } break; default: break; } } parser->destroy(parser); return list; } /** * Parses roleSyntax */ static void parse_roleSyntax(chunk_t blob, int level0) { asn1_parser_t *parser; chunk_t object; int objectID; parser = asn1_parser_create(roleSyntaxObjects, blob); parser->set_top_level(parser, level0); while (parser->iterate(parser, &objectID, &object)) { switch (objectID) { default: break; } } parser->destroy(parser); } /** * Parses an X.509 attribute certificate */ bool parse_ac(chunk_t blob, x509acert_t *ac) { asn1_parser_t *parser; chunk_t object; int objectID; int type = OID_UNKNOWN; int extn_oid = OID_UNKNOWN; bool success = FALSE; bool critical; parser = asn1_parser_create(acObjects, blob); while (parser->iterate(parser, &objectID, &object)) { u_int level = parser->get_level(parser)+1; switch (objectID) { case AC_OBJ_CERTIFICATE: ac->certificate = object; break; case AC_OBJ_CERTIFICATE_INFO: ac->certificateInfo = object; break; case AC_OBJ_VERSION: ac->version = (object.len) ? (1 + (u_int)*object.ptr) : 1; DBG(DBG_PARSING, DBG_log(" v%d", ac->version); ) if (ac->version != 2) { plog("v%d attribute certificates are not supported" , ac->version); goto end; } break; case AC_OBJ_HOLDER_ISSUER: ac->holderIssuer = get_directoryName(object, level, FALSE); break; case AC_OBJ_HOLDER_SERIAL: ac->holderSerial = object; break; case AC_OBJ_ENTITY_NAME: ac->entityName = get_directoryName(object, level, TRUE); break; case AC_OBJ_ISSUER_NAME: ac->issuerName = get_directoryName(object, level, FALSE); break; case AC_OBJ_SIG_ALG: ac->sigAlg = asn1_parse_algorithmIdentifier(object, level, NULL); break; case AC_OBJ_SERIAL_NUMBER: ac->serialNumber = object; break; case AC_OBJ_NOT_BEFORE: ac->notBefore = asn1_to_time(&object, ASN1_GENERALIZEDTIME); break; case AC_OBJ_NOT_AFTER: ac->notAfter = asn1_to_time(&object, ASN1_GENERALIZEDTIME); break; case AC_OBJ_ATTRIBUTE_TYPE: type = asn1_known_oid(object); break; case AC_OBJ_ATTRIBUTE_VALUE: { switch (type) { case OID_AUTHENTICATION_INFO: DBG(DBG_PARSING, DBG_log(" need to parse authenticationInfo") ) break; case OID_ACCESS_IDENTITY: DBG(DBG_PARSING, DBG_log(" need to parse accessIdentity") ) break; case OID_CHARGING_IDENTITY: ac->charging = parse_ietfAttrSyntax(object, level); break; case OID_GROUP: ac->groups = parse_ietfAttrSyntax(object, level); break; case OID_ROLE: parse_roleSyntax(object, level); break; default: break; } } break; case AC_OBJ_EXTN_ID: extn_oid = asn1_known_oid(object); break; case AC_OBJ_CRITICAL: critical = object.len && *object.ptr; DBG(DBG_PARSING, DBG_log(" %s",(critical)?"TRUE":"FALSE"); ) break; case AC_OBJ_EXTN_VALUE: { switch (extn_oid) { case OID_CRL_DISTRIBUTION_POINTS: DBG(DBG_PARSING, DBG_log(" need to parse crlDistributionPoints") ) break; case OID_AUTHORITY_KEY_ID: parse_authorityKeyIdentifier(object, level , &ac->authKeyID, &ac->authKeySerialNumber); break; case OID_TARGET_INFORMATION: DBG(DBG_PARSING, DBG_log(" need to parse targetInformation") ) break; case OID_NO_REV_AVAIL: ac->noRevAvail = TRUE; break; default: break; } } break; case AC_OBJ_ALGORITHM: ac->algorithm = asn1_parse_algorithmIdentifier(object, level, NULL); break; case AC_OBJ_SIGNATURE: ac->signature = object; break; default: break; } } success = parser->success(parser); time(&ac->installed); end: parser->destroy(parser); return success; } /** * Release an ietfAttribute, free it if count reaches zero */ static void release_ietfAttr(ietfAttr_t* attr) { if (--attr->count == 0) { ietfAttrList_t **plist = &ietfAttributes; ietfAttrList_t *list = *plist; while (list->attr != attr) { plist = &list->next; list = *plist; } *plist = list->next; free(attr->value.ptr); free(attr); free(list); } } /** * Free an ietfAttrList */ void free_ietfAttrList(ietfAttrList_t* list) { while (list != NULL) { ietfAttrList_t *el = list; release_ietfAttr(el->attr); list = list->next; free(el); } } /** * Free a X.509 attribute certificate */ void free_acert(x509acert_t *ac) { if (ac != NULL) { free_ietfAttrList(ac->charging); free_ietfAttrList(ac->groups); free(ac->certificate.ptr); free(ac); } } /** * Free first X.509 attribute certificate in the chained list */ static void free_first_acert(void) { x509acert_t *first = x509acerts; x509acerts = first->next; free_acert(first); } /** * Free all attribute certificates in the chained list */ void free_acerts(void) { while (x509acerts != NULL) free_first_acert(); } /** * Get a X.509 attribute certificate for a given holder */ x509acert_t* get_x509acert(chunk_t issuer, chunk_t serial) { x509acert_t *ac = x509acerts; x509acert_t *prev_ac = NULL; while (ac != NULL) { if (same_dn(issuer, ac->holderIssuer) && same_serial(serial, ac->holderSerial)) { if (ac!= x509acerts) { /* bring the certificate up front */ prev_ac->next = ac->next; ac->next = x509acerts; x509acerts = ac; } return ac; } prev_ac = ac; ac = ac->next; } return NULL; } /** * Add a X.509 attribute certificate to the chained list */ static void add_acert(x509acert_t *ac) { x509acert_t *old_ac = get_x509acert(ac->holderIssuer, ac->holderSerial); if (old_ac != NULL) { if (ac->notBefore >old_ac->notBefore) { /* delete the old attribute cert */ free_first_acert(); DBG(DBG_CONTROL, DBG_log("attribute cert is newer - existing cert deleted") ) } else { DBG(DBG_CONTROL, DBG_log("attribute cert is not newer - existing cert kept"); ) free_acert(ac); return; } } plog("attribute cert added"); /* insert new attribute cert at the root of the chain */ ac->next = x509acerts; x509acerts = ac; } /** * Verify the validity of an attribute certificate by * checking the notBefore and notAfter dates */ static err_t check_ac_validity(const x509acert_t *ac) { time_t current_time; time(¤t_time); DBG(DBG_CONTROL | DBG_PARSING, DBG_log(" not before : %T", &ac->notBefore, TRUE); DBG_log(" current time: %T", ¤t_time, TRUE); DBG_log(" not after : %T", &ac->notAfter, TRUE); ) if (current_time < ac->notBefore) return "attribute certificate is not valid yet"; if (current_time > ac->notAfter) return "attribute certificate has expired"; else return NULL; } /** * verifies a X.509 attribute certificate */ bool verify_x509acert(x509acert_t *ac, bool strict) { u_char buf[BUF_LEN]; x509cert_t *aacert; err_t ugh = NULL; time_t valid_until = ac->notAfter; DBG(DBG_CONTROL, dntoa(buf, BUF_LEN, ac->entityName); DBG_log("holder: '%s'",buf); dntoa(buf, BUF_LEN, ac->issuerName); DBG_log("issuer: '%s'",buf); ) ugh = check_ac_validity(ac); if (ugh != NULL) { plog("%s", ugh); return FALSE; } DBG(DBG_CONTROL, DBG_log("attribute certificate is valid") ) lock_authcert_list("verify_x509acert"); aacert = get_authcert(ac->issuerName, ac->authKeySerialNumber , ac->authKeyID, AUTH_AA); unlock_authcert_list("verify_x509acert"); if (aacert == NULL) { plog("issuer aacert not found"); return FALSE; } DBG(DBG_CONTROL, DBG_log("issuer aacert found") ) if (!x509_check_signature(ac->certificateInfo, ac->signature, ac->algorithm, aacert)) { plog("attribute certificate signature is invalid"); return FALSE; } DBG(DBG_CONTROL, DBG_log("attribute certificate signature is valid"); ) return verify_x509cert(aacert, strict, &valid_until); } /** * Loads X.509 attribute certificates */ void load_acerts(void) { u_char buf[BUF_LEN]; /* change directory to specified path */ u_char *save_dir = getcwd(buf, BUF_LEN); if (!chdir(A_CERT_PATH)) { struct dirent **filelist; int n; plog("Changing to directory '%s'",A_CERT_PATH); n = scandir(A_CERT_PATH, &filelist, file_select, alphasort); if (n > 0) { while (n--) { chunk_t blob = chunk_empty; bool pgp = FALSE; if (load_coded_file(filelist[n]->d_name, NULL, "acert", &blob, &pgp)) { x509acert_t *ac = malloc_thing(x509acert_t); *ac = empty_ac; if (parse_ac(blob, ac) && verify_x509acert(ac, FALSE)) add_acert(ac); else free_acert(ac); } free(filelist[n]); } free(filelist); } } /* restore directory path */ ignore_result(chdir(save_dir)); } /** * lists group attributes separated by commas on a single line */ void format_groups(const ietfAttrList_t *list, char *buf, int len) { bool first_group = TRUE; while (list != NULL && len > 0) { ietfAttr_t *attr = list->attr; if (attr->kind == IETF_ATTRIBUTE_OCTETS || attr->kind == IETF_ATTRIBUTE_STRING) { int written = snprintf(buf, len, "%s%.*s" , (first_group)? "" : ", " , (int)attr->value.len, attr->value.ptr); first_group = FALSE; /* return value of snprintf() up to glibc 2.0.6 */ if (written < 0) break; buf += written; len -= written; } list = list->next; } } /** * list all X.509 attribute certificates in the chained list */ void list_acerts(bool utc) { x509acert_t *ac = x509acerts; time_t now; /* determine the current time */ time(&now); if (ac != NULL) { whack_log(RC_COMMENT, " "); whack_log(RC_COMMENT, "List of X.509 Attribute Certificates:"); whack_log(RC_COMMENT, " "); } while (ac != NULL) { u_char buf[BUF_LEN]; whack_log(RC_COMMENT, "%T", &ac->installed, utc); if (ac->entityName.ptr != NULL) { dntoa(buf, BUF_LEN, ac->entityName); whack_log(RC_COMMENT, " holder: '%s'", buf); } if (ac->holderIssuer.ptr != NULL) { dntoa(buf, BUF_LEN, ac->holderIssuer); whack_log(RC_COMMENT, " hissuer: '%s'", buf); } if (ac->holderSerial.ptr != NULL) { datatot(ac->holderSerial.ptr, ac->holderSerial.len, ':' , buf, BUF_LEN); whack_log(RC_COMMENT, " hserial: %s", buf); } if (ac->groups != NULL) { format_groups(ac->groups, buf, BUF_LEN); whack_log(RC_COMMENT, " groups: %s", buf); } dntoa(buf, BUF_LEN, ac->issuerName); whack_log(RC_COMMENT, " issuer: '%s'", buf); datatot(ac->serialNumber.ptr, ac->serialNumber.len, ':' , buf, BUF_LEN); whack_log(RC_COMMENT, " serial: %s", buf); whack_log(RC_COMMENT, " validity: not before %T %s", &ac->notBefore, utc, (ac->notBefore < now)?"ok":"fatal (not valid yet)"); whack_log(RC_COMMENT, " not after %T %s", &ac->notAfter, utc, check_expiry(ac->notAfter, ACERT_WARNING_INTERVAL, TRUE)); if (ac->authKeyID.ptr != NULL) { datatot(ac->authKeyID.ptr, ac->authKeyID.len, ':' , buf, BUF_LEN); whack_log(RC_COMMENT, " authkey: %s", buf); } if (ac->authKeySerialNumber.ptr != NULL) { datatot(ac->authKeySerialNumber.ptr, ac->authKeySerialNumber.len, ':' , buf, BUF_LEN); whack_log(RC_COMMENT, " aserial: %s", buf); } ac = ac->next; } } /** * list all group attributes in alphabetical order */ void list_groups(bool utc) { ietfAttrList_t *list = ietfAttributes; if (list != NULL) { whack_log(RC_COMMENT, " "); whack_log(RC_COMMENT, "List of Group Attributes:"); whack_log(RC_COMMENT, " "); } while (list != NULL) { ietfAttr_t *attr = list->attr; whack_log(RC_COMMENT, "%T, count: %d", &attr->installed, utc, attr->count); switch (attr->kind) { case IETF_ATTRIBUTE_OCTETS: case IETF_ATTRIBUTE_STRING: whack_log(RC_COMMENT, " %.*s", (int)attr->value.len, attr->value.ptr); break; case IETF_ATTRIBUTE_OID: whack_log(RC_COMMENT, " OID"); break; default: break; } list = list->next; } }