strongswan/src/pluto/ac.c

1008 lines
25 KiB
C

/* 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 <http://www.fsf.org/copyleft/gpl.txt>.
*
* 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.
*
* RCSID $Id$
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#include <sys/types.h>
#include <freeswan.h>
#include "constants.h"
#include "defs.h"
#include "asn1.h"
#include <asn1/oid.h>
#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 */
};
#define IETF_ATTR_OCTETS 4
#define IETF_ATTR_OID 6
#define IETF_ATTR_STRING 8
#define IETF_ATTR_ROOF 11
/* 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 */
};
#define ROLE_ROOF 4
/* 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 */
};
#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
#define AC_OBJ_ROOF 55
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_ctx_t ctx;
chunk_t object;
u_int level;
int objectID = 0;
ietfAttrList_t *list = NULL;
asn1_init(&ctx, blob, level0, FALSE, DBG_RAW);
while (objectID < IETF_ATTR_ROOF)
{
if (!extract_object(ietfAttrSyntaxObjects, &objectID, &object, &level, &ctx))
return NULL;
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;
}
objectID++;
}
return list;
}
/*
* parses roleSyntax
*/
static void
parse_roleSyntax(chunk_t blob, int level0)
{
asn1_ctx_t ctx;
chunk_t object;
u_int level;
int objectID = 0;
asn1_init(&ctx, blob, level0, FALSE, DBG_RAW);
while (objectID < ROLE_ROOF)
{
if (!extract_object(roleSyntaxObjects, &objectID, &object, &level, &ctx))
return;
switch (objectID) {
default:
break;
}
objectID++;
}
}
/*
* Parses an X.509 attribute certificate
*/
bool
parse_ac(chunk_t blob, x509acert_t *ac)
{
asn1_ctx_t ctx;
bool critical;
chunk_t object;
u_int level;
int objectID = 0;
int type = OID_UNKNOWN;
int extn_oid = OID_UNKNOWN;
asn1_init(&ctx, blob, 0, FALSE, DBG_RAW);
while (objectID < AC_OBJ_ROOF) {
if (!extract_object(acObjects, &objectID, &object, &level, &ctx))
return FALSE;
/* those objects which will parsed further need the next higher level */
level++;
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);
return FALSE;
}
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 = parse_algorithmIdentifier(object, level, NULL);
break;
case AC_OBJ_SERIAL_NUMBER:
ac->serialNumber = object;
break;
case AC_OBJ_NOT_BEFORE:
ac->notBefore = asn1totime(&object, ASN1_GENERALIZEDTIME);
break;
case AC_OBJ_NOT_AFTER:
ac->notAfter = asn1totime(&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 = parse_algorithmIdentifier(object, level, NULL);
break;
case AC_OBJ_SIGNATURE:
ac->signature = object;
break;
default:
break;
}
objectID++;
}
time(&ac->installed);
return TRUE;
}
/*
* 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(&current_time);
DBG(DBG_CONTROL | DBG_PARSING,
DBG_log(" not before : %s", timetoa(&ac->notBefore, TRUE));
DBG_log(" current time: %s", timetoa(&current_time, TRUE));
DBG_log(" not after : %s", timetoa(&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 (!check_signature(ac->certificateInfo, ac->signature
, ac->algorithm, 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, "%s",timetoa(&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 %s %s",
timetoa(&ac->notBefore, utc),
(ac->notBefore < now)?"ok":"fatal (not valid yet)");
whack_log(RC_COMMENT, " not after %s %s",
timetoa(&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, "%s, count: %d", timetoa(&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;
}
}