summaryrefslogtreecommitdiff
path: root/programs/openac
diff options
context:
space:
mode:
Diffstat (limited to 'programs/openac')
-rw-r--r--programs/openac/Makefile154
-rw-r--r--programs/openac/build.c242
-rw-r--r--programs/openac/build.h47
-rw-r--r--programs/openac/loglite.c295
-rw-r--r--programs/openac/openac.8180
-rwxr-xr-xprograms/openac/openac.c438
6 files changed, 1356 insertions, 0 deletions
diff --git a/programs/openac/Makefile b/programs/openac/Makefile
new file mode 100644
index 000000000..7aeacee0a
--- /dev/null
+++ b/programs/openac/Makefile
@@ -0,0 +1,154 @@
+# Makefile for the openac attribute certificate generation tool
+# Copyright (C) 2004 Andreas Steffen
+# Zuercher Hochschule Winterthur
+#
+# 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: Makefile,v 1.16 2006/02/17 19:33:27 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+CONFDIR=$(DESTDIR)/etc/openac
+PLUTODIR=../pluto
+
+PROGRAM=openac
+EXTRA8PROC=${PROGRAM}.8
+
+LIBS=${FREESWANLIB} $(LIBDESLITE) -lgmp
+CFLAGS+= -DDEBUG -DNO_PLUTO
+
+# This compile option activates the leak detective
+ifeq ($(USE_LEAK_DETECTIVE),true)
+ CFLAGS+= -DLEAK_DETECTIVE
+endif
+
+X509_OBJS= ac.o asn1.o ca.o certs.o constants.o crl.o defs.o mp_defs.o fetch.o \
+ id.o keys.o lex.o md2.o md5.o ocsp.o oid.o pem.o pgp.o pkcs1.o \
+ rnd.o sha1.o smartcard.o x509.o
+
+OBJS= build.o loglite.o ${X509_OBJS}
+
+include ../Makefile.program
+
+build.o : build.c build.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+loglite.o : loglite.c $(PLUTODIR)/log.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+# X.509 library
+
+ac.o : $(PLUTODIR)/ac.c $(PLUTODIR)/ac.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+asn1.o : $(PLUTODIR)/asn1.c $(PLUTODIR)/asn1.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+ca.o : $(PLUTODIR)/ca.c $(PLUTODIR)/ca.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+certs.o : $(PLUTODIR)/certs.c $(PLUTODIR)/certs.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+constants.o : $(PLUTODIR)/constants.c $(PLUTODIR)/constants.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+crl.o : $(PLUTODIR)/crl.c $(PLUTODIR)/crl.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+defs.o : $(PLUTODIR)/defs.c $(PLUTODIR)/defs.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+mp_defs.o : $(PLUTODIR)/mp_defs.c $(PLUTODIR)/mp_defs.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+fetch.o : $(PLUTODIR)/fetch.c $(PLUTODIR)/fetch.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+id.o : $(PLUTODIR)/id.c $(PLUTODIR)/id.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+keys.o : $(PLUTODIR)/keys.c $(PLUTODIR)/keys.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+lex.o : $(PLUTODIR)/lex.c $(PLUTODIR)/lex.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+md2.o : $(PLUTODIR)/md2.c $(PLUTODIR)/md2.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+md5.o : $(PLUTODIR)/md5.c $(PLUTODIR)/md5.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+ocsp.o : $(PLUTODIR)/ocsp.c $(PLUTODIR)/ocsp.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+oid.o : $(PLUTODIR)/oid.c $(PLUTODIR)/oid.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+pem.o : $(PLUTODIR)/pem.c $(PLUTODIR)/pem.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+pgp.o : $(PLUTODIR)/pgp.c $(PLUTODIR)/pgp.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+pkcs1.o : $(PLUTODIR)/pkcs1.c $(PLUTODIR)/pkcs1.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+rnd.o : $(PLUTODIR)/rnd.c $(PLUTODIR)/rnd.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+sha1.o : $(PLUTODIR)/sha1.c $(PLUTODIR)/sha1.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+smartcard.o : $(PLUTODIR)/smartcard.c $(PLUTODIR)/smartcard.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+x509.o : $(PLUTODIR)/x509.c $(PLUTODIR)/x509.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+# Stolen from pluto/Makefile
+
+gatherdeps:
+ @ls | grep '\.c$$' | sed -e 's/\(.*\)\.c$$/\1.o: \1.c/'
+ @echo
+ @ls | grep '\.c$$' | xargs grep '^#[ ]*include[ ]*"' | \
+ sed -e 's/\.c:#[ ]*include[ ]*"/.o: /' -e 's/".*//'
+
+# Dependencies generated by "make gatherdeps":
+
+build.o: build.c
+loglite.o: loglite.c
+openac.o: openac.c
+
+build.o: ../pluto/constants.h
+build.o: ../pluto/defs.h
+build.o: ../pluto/oid.h
+build.o: ../pluto/asn1.h
+build.o: ../pluto/x509.h
+build.o: ../pluto/log.h
+build.o: build.h
+loglite.o: ../pluto/constants.h
+loglite.o: ../pluto/defs.h
+loglite.o: ../pluto/log.h
+loglite.o: ../pluto/whack.h
+openac.o: ../pluto/constants.h
+openac.o: ../pluto/defs.h
+openac.o: ../pluto/mp_defs.h
+openac.o: ../pluto/log.h
+openac.o: ../pluto/asn1.h
+openac.o: ../pluto/certs.h
+openac.o: ../pluto/x509.h
+openac.o: ../pluto/crl.h
+openac.o: ../pluto/keys.h
+openac.o: ../pluto/ac.h
+openac.o: build.h
diff --git a/programs/openac/build.c b/programs/openac/build.c
new file mode 100644
index 000000000..bd3df6fee
--- /dev/null
+++ b/programs/openac/build.c
@@ -0,0 +1,242 @@
+/* Build a X.509 attribute certificate
+ * Copyright (C) 2002 Ueli Galizzi, Ariane Seiler
+ * Copyright (C) 2004 Andreas Steffen
+ * Zuercher Hochschule Winterthur, Switzerland
+ *
+ * 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: build.c,v 1.14 2005/09/06 11:47:57 as Exp $
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/oid.h"
+#include "../pluto/asn1.h"
+#include "../pluto/x509.h"
+#include "../pluto/log.h"
+
+#include "build.h"
+
+static u_char ASN1_group_oid_str[] = {
+ 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x0a ,0x04
+};
+
+static const chunk_t ASN1_group_oid = strchunk(ASN1_group_oid_str);
+
+static u_char ASN1_authorityKeyIdentifier_oid_str[] = {
+ 0x06, 0x03,
+ 0x55, 0x1d, 0x23
+};
+
+static const chunk_t ASN1_authorityKeyIdentifier_oid
+ = strchunk(ASN1_authorityKeyIdentifier_oid_str);
+
+static u_char ASN1_noRevAvail_ext_str[] = {
+ 0x30, 0x09,
+ 0x06, 0x03,
+ 0x55, 0x1d, 0x38,
+ 0x04, 0x02,
+ 0x05, 0x00
+};
+
+static const chunk_t ASN1_noRevAvail_ext = strchunk(ASN1_noRevAvail_ext_str);
+
+/*
+ * build directoryName
+ */
+static chunk_t
+build_directoryName(asn1_t tag, chunk_t name)
+{
+ return asn1_wrap(tag, "m"
+ , asn1_simple_object(ASN1_CONTEXT_C_4, name));
+}
+
+/*
+ * build holder
+ */
+static chunk_t
+build_holder(void)
+{
+ return asn1_wrap(ASN1_SEQUENCE, "mm"
+ , asn1_wrap(ASN1_CONTEXT_C_0, "mm"
+ , build_directoryName(ASN1_SEQUENCE, user->issuer)
+ , asn1_simple_object(ASN1_INTEGER, user->serialNumber)
+ )
+ , build_directoryName(ASN1_CONTEXT_C_1, user->subject));
+}
+
+/*
+ * build v2Form
+ */
+static chunk_t
+build_v2_form(void)
+{
+ return asn1_wrap(ASN1_CONTEXT_C_0, "m"
+ , build_directoryName(ASN1_SEQUENCE, signer->subject));
+}
+
+/*
+ * build attrCertValidityPeriod
+ */
+static chunk_t
+build_attr_cert_validity(void)
+{
+ return asn1_wrap(ASN1_SEQUENCE, "mm"
+ , timetoasn1(&notBefore, ASN1_GENERALIZEDTIME)
+ , timetoasn1(&notAfter, ASN1_GENERALIZEDTIME));
+}
+
+/*
+ * build attributes
+ */
+static chunk_t
+build_ietfAttributes(ietfAttrList_t *list)
+{
+ chunk_t ietfAttributes;
+ ietfAttrList_t *item = list;
+ size_t size = 0;
+ u_char *pos;
+
+ /* precalculate the total size of all values */
+ while (item != NULL)
+ {
+ size_t len = item->attr->value.len;
+
+ size += 1 + (len > 0) + (len >= 128) + (len >= 256) + (len >= 65536) + len;
+ item = item->next;
+ }
+ pos = build_asn1_object(&ietfAttributes, ASN1_SEQUENCE, size);
+
+ while (list != NULL)
+ {
+ ietfAttr_t *attr = list->attr;
+ asn1_t type = ASN1_NULL;
+
+ switch (attr->kind)
+ {
+ case IETF_ATTRIBUTE_OCTETS:
+ type = ASN1_OCTET_STRING;
+ break;
+ case IETF_ATTRIBUTE_STRING:
+ type = ASN1_UTF8STRING;
+ break;
+ case IETF_ATTRIBUTE_OID:
+ type = ASN1_OID;
+ break;
+ }
+ mv_chunk(&pos, asn1_simple_object(type, attr->value));
+
+ list = list->next;
+ }
+
+ return asn1_wrap(ASN1_SEQUENCE, "m", ietfAttributes);
+}
+
+/*
+ * build attribute type
+ */
+static chunk_t
+build_attribute_type(const chunk_t type, chunk_t content)
+{
+ return asn1_wrap(ASN1_SEQUENCE, "cm"
+ , type
+ , asn1_wrap(ASN1_SET, "m", content));
+}
+
+/*
+ * build attributes
+ */
+static chunk_t
+build_attributes(void)
+{
+ return asn1_wrap(ASN1_SEQUENCE, "m"
+ , build_attribute_type(ASN1_group_oid
+ , build_ietfAttributes(groups)));
+}
+
+/*
+ * build authorityKeyIdentifier
+ */
+static chunk_t
+build_authorityKeyID(x509cert_t *signer)
+{
+ chunk_t keyIdentifier = (signer->subjectKeyID.ptr == NULL)
+ ? empty_chunk
+ : asn1_simple_object(ASN1_CONTEXT_S_0
+ , signer->subjectKeyID);
+
+ chunk_t authorityCertIssuer = build_directoryName(ASN1_CONTEXT_C_1
+ , signer->issuer);
+
+ chunk_t authorityCertSerialNumber = asn1_simple_object(ASN1_CONTEXT_S_2
+ , signer->serialNumber);
+
+ return asn1_wrap(ASN1_SEQUENCE, "cm"
+ , ASN1_authorityKeyIdentifier_oid
+ , asn1_wrap(ASN1_OCTET_STRING, "m"
+ , asn1_wrap(ASN1_SEQUENCE, "mmm"
+ , keyIdentifier
+ , authorityCertIssuer
+ , authorityCertSerialNumber
+ )
+ )
+ );
+}
+
+/*
+ * build extensions
+ */
+static chunk_t
+build_extensions(void)
+{
+ return asn1_wrap(ASN1_SEQUENCE, "mc"
+ , build_authorityKeyID(signer)
+ , ASN1_noRevAvail_ext);
+}
+
+/*
+ * build attributeCertificateInfo
+ */
+static chunk_t
+build_attr_cert_info(void)
+{
+ return asn1_wrap(ASN1_SEQUENCE, "cmmcmmmm"
+ , ASN1_INTEGER_1
+ , build_holder()
+ , build_v2_form()
+ , ASN1_sha1WithRSA_id
+ , asn1_simple_object(ASN1_INTEGER, serial)
+ , build_attr_cert_validity()
+ , build_attributes()
+ , build_extensions());
+}
+
+/*
+ * build an X.509 attribute certificate
+ */
+chunk_t
+build_attr_cert(void)
+{
+ chunk_t attributeCertificateInfo = build_attr_cert_info();
+ chunk_t signatureValue = pkcs1_build_signature(attributeCertificateInfo
+ , OID_SHA1, signerkey, TRUE);
+
+ return asn1_wrap(ASN1_SEQUENCE, "mcm"
+ , attributeCertificateInfo
+ , ASN1_sha1WithRSA_id
+ , signatureValue);
+}
diff --git a/programs/openac/build.h b/programs/openac/build.h
new file mode 100644
index 000000000..deeddda04
--- /dev/null
+++ b/programs/openac/build.h
@@ -0,0 +1,47 @@
+/* Build a X.509 attribute certificate
+ * Copyright (C) 2002 Ueli Galizzi, Ariane Seiler
+ * Copyright (C) 2004 Andreas Steffen
+ * Zuercher Hochschule Winterthur, Switzerland
+ *
+ * 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: build.h,v 1.4 2004/11/03 14:28:52 as Exp $
+ */
+
+#ifndef _BUILD_H
+#define _BUILD_H
+
+#include <time.h>
+
+#include "../pluto/x509.h"
+#include "../pluto/keys.h"
+#include "../pluto/ac.h"
+
+/*
+ * global variables accessible by both main() and build.c
+ */
+extern x509cert_t *user;
+extern x509cert_t *signer;
+
+extern ietfAttrList_t *groups;
+extern struct RSA_private_key *signerkey;
+
+extern time_t notBefore;
+extern time_t notAfter;
+
+extern chunk_t serial;
+
+/*
+ * exported functions
+ */
+extern chunk_t build_attr_cert(void);
+
+#endif /* _BUILD_H */
diff --git a/programs/openac/loglite.c b/programs/openac/loglite.c
new file mode 100644
index 000000000..b1763cc9f
--- /dev/null
+++ b/programs/openac/loglite.c
@@ -0,0 +1,295 @@
+/* error logging functions
+ * Copyright (C) 1997 Angelos D. Keromytis.
+ * Copyright (C) 1998-2001 D. Hugh Redelmeier.
+ *
+ * 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: loglite.c,v 1.2 2005/07/11 18:38:16 as Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h> /* used only if MSG_NOSIGNAL not defined */
+#include <libgen.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/log.h"
+#include "../pluto/whack.h"
+
+bool
+ log_to_stderr = FALSE, /* should log go to stderr? */
+ log_to_syslog = TRUE; /* should log go to syslog? */
+
+void
+init_log(const char *program)
+{
+ if (log_to_stderr)
+ setbuf(stderr, NULL);
+ if (log_to_syslog)
+ openlog(program, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_AUTHPRIV);
+}
+
+void
+close_log(void)
+{
+ if (log_to_syslog)
+ closelog();
+}
+
+void
+plog(const char *message, ...)
+{
+ va_list args;
+ char m[LOG_WIDTH]; /* longer messages will be truncated */
+
+ va_start(args, message);
+ vsnprintf(m, sizeof(m), message, args);
+ va_end(args);
+
+ if (log_to_stderr)
+ fprintf(stderr, "%s\n", m);
+ if (log_to_syslog)
+ syslog(LOG_WARNING, "%s", m);
+}
+
+void
+loglog(int mess_no, const char *message, ...)
+{
+ va_list args;
+ char m[LOG_WIDTH]; /* longer messages will be truncated */
+
+ va_start(args, message);
+ vsnprintf(m, sizeof(m), message, args);
+ va_end(args);
+
+ if (log_to_stderr)
+ fprintf(stderr, "%s\n", m);
+ if (log_to_syslog)
+ syslog(LOG_WARNING, "%s", m);
+}
+
+void
+log_errno_routine(int e, const char *message, ...)
+{
+ va_list args;
+ char m[LOG_WIDTH]; /* longer messages will be truncated */
+
+ va_start(args, message);
+ vsnprintf(m, sizeof(m), message, args);
+ va_end(args);
+
+ if (log_to_stderr)
+ fprintf(stderr, "ERROR: %s. Errno %d: %s\n", m, e, strerror(e));
+ if (log_to_syslog)
+ syslog(LOG_ERR, "ERROR: %s. Errno %d: %s", m, e, strerror(e));
+}
+
+void
+exit_log(const char *message, ...)
+{
+ va_list args;
+ char m[LOG_WIDTH]; /* longer messages will be truncated */
+
+ va_start(args, message);
+ vsnprintf(m, sizeof(m), message, args);
+ va_end(args);
+
+ if (log_to_stderr)
+ fprintf(stderr, "FATAL ERROR: %s\n", m);
+ if (log_to_syslog)
+ syslog(LOG_ERR, "FATAL ERROR: %s", m);
+ exit(1);
+}
+
+void
+exit_log_errno_routine(int e, const char *message, ...)
+{
+ va_list args;
+ char m[LOG_WIDTH]; /* longer messages will be truncated */
+
+ va_start(args, message);
+ vsnprintf(m, sizeof(m), message, args);
+ va_end(args);
+
+ if (log_to_stderr)
+ fprintf(stderr, "FATAL ERROR: %s. Errno %d: %s\n", m, e, strerror(e));
+ if (log_to_syslog)
+ syslog(LOG_ERR, "FATAL ERROR: %s. Errno %d: %s", m, e, strerror(e));
+ exit(1);
+}
+
+void
+whack_log(int mess_no, const char *message, ...)
+{
+ va_list args;
+ char m[LOG_WIDTH]; /* longer messages will be truncated */
+
+ va_start(args, message);
+ vsnprintf(m, sizeof(m), message, args);
+ va_end(args);
+
+ fprintf(stderr, "%s\n", m);
+}
+
+/* Build up a diagnostic in a static buffer.
+ * Although this would be a generally useful function, it is very
+ * hard to come up with a discipline that prevents different uses
+ * from interfering. It is intended that by limiting it to building
+ * diagnostics, we will avoid this problem.
+ * Juggling is performed to allow an argument to be a previous
+ * result: the new string may safely depend on the old one. This
+ * restriction is not checked in any way: violators will produce
+ * confusing results (without crashing!).
+ */
+char diag_space[sizeof(diag_space)];
+
+err_t
+builddiag(const char *fmt, ...)
+{
+ static char diag_space[LOG_WIDTH]; /* longer messages will be truncated */
+ char t[sizeof(diag_space)]; /* build result here first */
+ va_list args;
+
+ va_start(args, fmt);
+ t[0] = '\0'; /* in case nothing terminates string */
+ vsnprintf(t, sizeof(t), fmt, args);
+ va_end(args);
+ strcpy(diag_space, t);
+ return diag_space;
+}
+
+/* Debugging message support */
+
+#ifdef DEBUG
+
+void
+switch_fail(int n, const char *file_str, unsigned long line_no)
+{
+ char buf[30];
+
+ snprintf(buf, sizeof(buf), "case %d unexpected", n);
+ passert_fail(buf, file_str, line_no);
+}
+
+void
+passert_fail(const char *pred_str, const char *file_str, unsigned long line_no)
+{
+ /* we will get a possibly unplanned prefix. Hope it works */
+ loglog(RC_LOG_SERIOUS, "ASSERTION FAILED at %s:%lu: %s", file_str, line_no, pred_str);
+ abort(); /* exiting correctly doesn't always work */
+}
+
+lset_t
+ base_debugging = DBG_NONE, /* default to reporting nothing */
+ cur_debugging = DBG_NONE;
+
+void
+pexpect_log(const char *pred_str, const char *file_str, unsigned long line_no)
+{
+ /* we will get a possibly unplanned prefix. Hope it works */
+ loglog(RC_LOG_SERIOUS, "EXPECTATION FAILED at %s:%lu: %s", file_str, line_no, pred_str);
+}
+
+/* log a debugging message (prefixed by "| ") */
+
+void
+DBG_log(const char *message, ...)
+{
+ va_list args;
+ char m[LOG_WIDTH]; /* longer messages will be truncated */
+
+ va_start(args, message);
+ vsnprintf(m, sizeof(m), message, args);
+ va_end(args);
+
+ if (log_to_stderr)
+ fprintf(stderr, "| %s\n", m);
+ if (log_to_syslog)
+ syslog(LOG_DEBUG, "| %s", m);
+}
+
+/* dump raw bytes in hex to stderr (for lack of any better destination) */
+
+void
+DBG_dump(const char *label, const void *p, size_t len)
+{
+# define DUMP_LABEL_WIDTH 20 /* arbitrary modest boundary */
+# define DUMP_WIDTH (4 * (1 + 4 * 3) + 1)
+ char buf[DUMP_LABEL_WIDTH + DUMP_WIDTH];
+ char *bp;
+ const unsigned char *cp = p;
+
+ bp = buf;
+
+ if (label != NULL && label[0] != '\0')
+ {
+ /* Handle the label. Care must be taken to avoid buffer overrun. */
+ size_t llen = strlen(label);
+
+ if (llen + 1 > sizeof(buf))
+ {
+ DBG_log("%s", label);
+ }
+ else
+ {
+ strcpy(buf, label);
+ if (buf[llen-1] == '\n')
+ {
+ buf[llen-1] = '\0'; /* get rid of newline */
+ DBG_log("%s", buf);
+ }
+ else if (llen < DUMP_LABEL_WIDTH)
+ {
+ bp = buf + llen;
+ }
+ else
+ {
+ DBG_log("%s", buf);
+ }
+ }
+ }
+
+ do {
+ int i, j;
+
+ for (i = 0; len!=0 && i!=4; i++)
+ {
+ *bp++ = ' ';
+ for (j = 0; len!=0 && j!=4; len--, j++)
+ {
+ static const char hexdig[] = "0123456789abcdef";
+
+ *bp++ = ' ';
+ *bp++ = hexdig[(*cp >> 4) & 0xF];
+ *bp++ = hexdig[*cp & 0xF];
+ cp++;
+ }
+ }
+ *bp = '\0';
+ DBG_log("%s", buf);
+ bp = buf;
+ } while (len != 0);
+# undef DUMP_LABEL_WIDTH
+# undef DUMP_WIDTH
+}
+
+#endif /* DEBUG */
diff --git a/programs/openac/openac.8 b/programs/openac/openac.8
new file mode 100644
index 000000000..8e609a1b1
--- /dev/null
+++ b/programs/openac/openac.8
@@ -0,0 +1,180 @@
+.TH IPSEC_OPENAC 8 "29 September 2005"
+.SH NAME
+ipsec openac \- Generation of X.509 attribute certificates
+.SH SYNOPSIS
+.B ipsec
+.B openac
+[
+.B \-\-help
+] [
+.B \-\-version
+] [
+.B \-\-optionsfrom
+\fIfilename\fP
+] [
+.B \-\-quiet
+]
+.br
+\ \ \ [
+.B \-\-debug\(hyall
+] [
+.B \-\-debug\(hyparsing
+] [
+.B \-\-debug\(hyraw
+] [
+.B \-\-debug\(hyprivate
+]
+.br
+\ \ \ [
+.B \-\-days
+\fIdays\fP
+] [
+.B \-\-hours
+\fIhours\fP
+]
+.br
+\ \ \ [
+.B \-\-startdate
+\fIYYYYMMDDHHMMSSZ\fP
+] [
+.B \-\-stopdate
+\fIYYYYMMDDHHMMSSZ\fP
+]
+.br
+.B \ \ \ \-\-cert
+\fIcertfile\fP
+.B \-\-key
+\fIkeyfile\fP
+[
+.B \-\-password
+\fIpassword\fP
+]
+.br
+.B \ \ \ \-\-usercert
+\fIcertfile\fP
+.B \-\-groups
+\fIattr1,attr2,...\fP
+.B \-\-out
+\fIfilename\fP
+.SH DESCRIPTION
+.BR openac
+is intended to be used by an Authorization Authority (AA) to generate and sign
+X.509 attribute certificates. Currently only the inclusion of one ore several group
+attributes is supported. An attribute certificate is linked to a holder by
+including the issuer and serial number of the holder's X.509 certificate.
+.SH OPTIONS
+.TP
+\fB\-\-help\fP
+display the usage message.
+.TP
+\fB\-\-version\fP
+display the version of \fBopenac\fP.
+.TP
+\fB\-\-optionsfrom\fP\ \fIfilename\fP
+adds the contents of the file to the argument list.
+If \fIfilename\fP is a relative path then the file is searched in the directory
+\fI/etc/openac\fP.
+.TP
+\fB\-\-quiet\fP
+By default \fBopenac\fP logs all control output both to syslog and stderr.
+With the \fB\-\-quiet\fP option no output is written to stderr.
+.TP
+\fB\-\-days\fP\ \fIdays\fP
+Validity of the X.509 attribute certificate in days. If neiter the \fB\-\-days\fP\ nor
+the \fB\-\-hours\fP\ option is specified then a default validity interval of 1 day is assumed.
+The \fB\-\-days\fP\ option can be combined with the \fB\-\-hours\fP\ option.
+.TP
+\fB\-\-hours\fP\ \fIhours\fP
+Validity of the X.509 attribute certificate in hours. If neiter the \fB\-\-hours\fP\ nor
+the \fB\-\-days\fP\ option is specified then a default validity interval of 24 hours is assumed.
+The \fB\-\-hours\fP\ option can be combined with the \fB\-\-days\fP\ option.
+.TP
+\fB\-\-startdate\fP\ \fIYYYYMMDDHHMMSSZ\fP
+defines the \fBnotBefore\fP date when the X.509 attribute certificate becomes valid.
+The date \fIYYYYMMDDHHMMSS\fP must be specified in UTC (\fIZ\fPulu time).
+If the \fB\-\-startdate\fP option is not specified then the current date is taken as a default.
+
+.TP
+\fB\-\-stopdate\fP\ \fIYYYYMMDDHHMMSSZ\fP
+defines the \fBnotAfter\fP date when the X.509 attribute certificate will expire.
+The date \fIYYYYMMDDHHMMSS\fP must be specified in UTC (\fIZ\fPulu time).
+If the \fB\-\-stopdate\fP option is not specified then the default \fBnotAfter\fP value is computed
+by adding the validity interval specified by the \fB\-\-days\fP\ and/or \fB\-\-days\fP\ options
+to the \fBnotBefore\fP date.
+.TP
+\fB\-\-cert\fP\ \fIcertfile\fP
+specifies the file containing the X.509 certificate of the Authorization Authority.
+The certificate is stored either in PEM or DER format.
+.TP
+\fB\-\-key\fP\ \fIkeyfile\fP
+specifies the encrypted file containing the private RSA key of the Authoritzation
+Authority. The private key is stored in PKCS#1 format.
+.TP
+\fB\-\-password\fP\ \fIpassword\fP
+specifies the password with which the private RSA keyfile defined by the
+\fB\-\-key\fP option has been protected. If the option is missing then the
+password is prompted for on the command line.
+.TP
+\fB\-\-usercert\fP\ \fIcertfile\fP
+specifies file containing the X.509 certificate of the user to which the generated attribute
+certificate will apply. The certificate file is stored either in PEM or DER format.
+.TP
+\fB\-\-groups\fP\ \fIattr1,attr2\fP
+specifies a comma-separated list of group attributes that will go into the
+X.509 attribute certificate.
+.TP
+\fB\-\-out\fP\ \fIfilename\fP
+specifies the file where the generated X.509 attribute certificate will be stored to.
+.SS Debugging
+.LP
+\fBopenac\fP produces a prodigious amount of debugging information. To do so,
+it must be compiled with \-DDEBUG. There are several classes of debugging output,
+and \fBopenac\fP may be directed to produce a selection of them. All lines of
+debugging output are prefixed with ``|\ '' to distinguish them from error messages.
+.LP
+When \fBopenac\fP is invoked, it may be given arguments to specify
+which classes to output. The current options are:
+.TP
+\fB\-\-debug-raw\fP
+show the raw bytes of the parsed user and authorization authority certificates
+as well as of the generated X.509 attribute certificate.
+.TP
+\fB\-\-debug-parsing\fP
+show the parsed structure of user and authorization authority certificats
+as well as of the generated X.509 attribute certificate.
+.TP
+\fB\-\-debug-all\fP
+all of the above.
+.TP
+\fB\-\-debug-private\fP
+enables debugging output of the authorization authority's private key.
+.SH EXIT STATUS
+.LP
+The execution of \fBopenac\fP terminates with one of the following two exit codes:
+.TP
+0
+means that the attribute certificate was successfully generated and stored.
+.TP
+1
+means that something went wrong.
+.SH FILES
+\fI/etc/openac/serial\fP\ \ \ serial number of latest attribute certificate
+.SH SEE ALSO
+.LP
+The X.509 attribute certificates generated with \fBopenac\fP can be used to
+enforce group policies defined by \fIipsec.conf\fP(5). Use \fIipsec_auto\fP(8)
+to load and list X.509 attribute certificates.
+.LP
+For more information on X.509 attribute certificates, refer to the following
+IETF RFC:
+.IP
+RFC 3281 An Internet Attribute Certificate Profile for Authorization
+.SH HISTORY
+The \fBopenac\fP program was originally written by Ariane Seiler and Ueli Galizzi.
+The software was recoded by Andreas Steffen using strongSwan's X.509 library and
+the ASN.1 code synthesis functions written by Christoph Gysin and Christoph Zwahlen.
+All authors were with the Zurich University of Applied Sciences in Winterthur,
+Switzerland.
+.LP
+.SH BUGS
+Bugs should be reported to the <users@lists.strongswan.org> mailing list.
diff --git a/programs/openac/openac.c b/programs/openac/openac.c
new file mode 100755
index 000000000..524a302d7
--- /dev/null
+++ b/programs/openac/openac.c
@@ -0,0 +1,438 @@
+/* Generation of X.509 attribute certificates
+ * Copyright (C) 2002 Ueli Galizzi, Ariane Seiler
+ * Copyright (C) 2004 Andreas Steffen
+ * Zuercher Hochschule Winterthur, Switzerland
+ *
+ * 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: openac.c,v 1.18 2006/01/04 21:12:33 as Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <time.h>
+#include <gmp.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/mp_defs.h"
+#include "../pluto/log.h"
+#include "../pluto/asn1.h"
+#include "../pluto/certs.h"
+#include "../pluto/x509.h"
+#include "../pluto/crl.h"
+#include "../pluto/keys.h"
+#include "../pluto/ac.h"
+
+#include "build.h"
+
+#define OPENAC_PATH "/etc/openac"
+#define OPENAC_SERIAL "/etc/openac/serial"
+
+const char openac_version[] = "openac 0.3";
+
+/* by default the CRL policy is lenient */
+bool strict_crl_policy = FALSE;
+
+/* by default pluto does not check crls dynamically */
+long crl_check_interval = 0;
+
+/* by default pluto logs out after every smartcard use */
+bool pkcs11_keep_state = FALSE;
+
+static void
+usage(const char *mess)
+{
+ if (mess != NULL && *mess != '\0')
+ fprintf(stderr, "%s\n", mess);
+ fprintf(stderr
+ , "Usage: openac"
+ " [--help]"
+ " [--version]"
+ " [--optionsfrom <filename>]"
+ " [--quiet]"
+#ifdef DEBUG
+ " \\\n\t"
+ " [--debug-all]"
+ " [--debug-parsing]"
+ " [--debug-raw]"
+ " [--debug-private]"
+#endif
+ " \\\n\t"
+ " [--days <days>]"
+ " [--hours <hours>]"
+ " \\\n\t"
+ " [--startdate <YYYYMMDDHHMMSSZ>]"
+ " [--enddate <YYYYMMDDHHMMSSZ>]"
+ " \\\n\t"
+ " --cert <certfile>"
+ " --key <keyfile>"
+ " [--password <password>]"
+ " \\\n\t"
+ " --usercert <certfile>"
+ " --groups <attr1,attr2,..>"
+ " --out <filename>"
+ "\n"
+ );
+ exit(mess == NULL? 0 : 1);
+}
+
+/*
+ * read the last serial number from file
+ */
+static chunk_t
+read_serial(void)
+{
+ MP_INT number;
+
+ char buf[BUF_LEN];
+ char bytes[BUF_LEN];
+
+ FILE *fd = fopen(OPENAC_SERIAL, "r");
+
+ /* serial number defaults to 0 */
+ size_t len = 1;
+ bytes[0] = 0x00;
+
+ if (fd)
+ {
+ if (fscanf(fd, "%s", buf))
+ {
+ err_t ugh = ttodata(buf, 0, 16, bytes, BUF_LEN, &len);
+
+ if (ugh != NULL)
+ plog(" error reading serial number from %s: %s"
+ , OPENAC_SERIAL, ugh);
+ }
+ fclose(fd);
+ }
+ else
+ plog(" file '%s' does not exist yet - serial number set to 01"
+ , OPENAC_SERIAL);
+
+ /* conversion of read serial number to a multiprecision integer
+ * and incrementing it by one
+ * and representing it as a two's complement octet string
+ */
+ n_to_mpz(&number, bytes, len);
+ mpz_add_ui(&number, &number, 0x01);
+ serial = mpz_to_n(&number, 1 + mpz_sizeinbase(&number, 2)/BITS_PER_BYTE);
+ mpz_clear(&number);
+
+ return serial;
+}
+
+/*
+ * write back the last serial number to file
+ */
+static void
+write_serial(chunk_t serial)
+{
+ char buf[BUF_LEN];
+
+ FILE *fd = fopen(OPENAC_SERIAL, "w");
+
+ if (fd)
+ {
+ datatot(serial.ptr, serial.len, 16, buf, BUF_LEN);
+ plog(" serial number is %s", buf);
+ fprintf(fd, "%s\n", buf);
+ fclose(fd);
+ }
+ else
+ plog(" could not open file '%s' for writing", OPENAC_SERIAL);
+}
+
+/*
+ * global variables accessible by both main() and build.c
+ */
+x509cert_t *user = NULL;
+x509cert_t *signer = NULL;
+
+ietfAttrList_t *groups = NULL;
+struct RSA_private_key *signerkey = NULL;
+
+time_t notBefore = 0;
+time_t notAfter = 0;
+
+chunk_t serial;
+
+
+int
+main(int argc, char **argv)
+{
+ char *keyfile = NULL;
+ char *certfile = NULL;
+ char *usercertfile = NULL;
+ char *outfile = NULL;
+
+ cert_t signercert = empty_cert;
+ cert_t usercert = empty_cert;
+
+ chunk_t attr_cert = empty_chunk;
+ x509acert_t *ac = NULL;
+
+ const time_t default_validity = 24*3600; /* 24 hours */
+ time_t validity = 0;
+
+ prompt_pass_t pass;
+
+ pass.secret[0] = '\0';
+ pass.prompt = TRUE;
+ pass.fd = STDIN_FILENO;
+
+ log_to_stderr = TRUE;
+
+ /* handle arguments */
+ for (;;)
+ {
+# define DBG_OFFSET 256
+ static const struct option long_opts[] = {
+ /* name, has_arg, flag, val */
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { "optionsfrom", required_argument, NULL, '+' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "cert", required_argument, NULL, 'c' },
+ { "key", required_argument, NULL, 'k' },
+ { "password", required_argument, NULL, 'p' },
+ { "usercert", required_argument, NULL, 'u' },
+ { "groups", required_argument, NULL, 'g' },
+ { "days", required_argument, NULL, 'D' },
+ { "hours", required_argument, NULL, 'H' },
+ { "startdate", required_argument, NULL, 'S' },
+ { "enddate", required_argument, NULL, 'E' },
+ { "out", required_argument, NULL, 'o' },
+#ifdef DEBUG
+ { "debug-all", no_argument, NULL, 'A' },
+ { "debug-raw", no_argument, NULL, DBG_RAW + DBG_OFFSET },
+ { "debug-parsing", no_argument, NULL, DBG_PARSING + DBG_OFFSET },
+ { "debug-private", no_argument, NULL, DBG_PRIVATE + DBG_OFFSET },
+#endif
+ { 0,0,0,0 }
+ };
+
+ int c = getopt_long(argc, argv, "hv+:qc:k:p;u:g:D:H:S:E:o:", long_opts, NULL);
+
+ /* Note: "breaking" from case terminates loop */
+ switch (c)
+ {
+ case EOF: /* end of flags */
+ break;
+
+ case 0: /* long option already handled */
+ continue;
+
+ case ':': /* diagnostic already printed by getopt_long */
+ case '?': /* diagnostic already printed by getopt_long */
+ usage(NULL);
+ break; /* not actually reached */
+
+ case 'h': /* --help */
+ usage(NULL);
+ break; /* not actually reached */
+
+ case 'v': /* --version */
+ printf("%s\n", openac_version);
+ exit(0);
+ break; /* not actually reached */
+
+ case '+': /* --optionsfrom <filename> */
+ {
+ char path[BUF_LEN];
+
+ if (*optarg == '/') /* absolute pathname */
+ strncpy(path, optarg, BUF_LEN);
+ else /* relative pathname */
+ snprintf(path, BUF_LEN, "%s/%s", OPENAC_PATH, optarg);
+ optionsfrom(path, &argc, &argv, optind, stderr);
+ /* does not return on error */
+ }
+ continue;
+
+ case 'q': /* --quiet */
+ log_to_stderr = TRUE;
+ continue;
+
+ case 'c': /* --cert */
+ certfile = optarg;
+ continue;
+
+ case 'k': /* --key */
+ keyfile = optarg;
+ continue;
+
+ case 'p': /* --key */
+ pass.prompt = FALSE;
+ strncpy(pass.secret, optarg, sizeof(pass.secret));
+ continue;
+
+ case 'u': /* --usercert */
+ usercertfile = optarg;
+ continue;
+
+ case 'g': /* --groups */
+ decode_groups(optarg, &groups);
+ continue;
+
+ case 'D': /* --days */
+ if (optarg == NULL || !isdigit(optarg[0]))
+ usage("missing number of days");
+ {
+ char *endptr;
+ long days = strtol(optarg, &endptr, 0);
+
+ if (*endptr != '\0' || endptr == optarg
+ || days <= 0)
+ usage("<days> must be a positive number");
+ validity += 24*3600*days;
+ }
+ continue;
+
+ case 'H': /* --hours */
+ if (optarg == NULL || !isdigit(optarg[0]))
+ usage("missing number of hours");
+ {
+ char *endptr;
+ long hours = strtol(optarg, &endptr, 0);
+
+ if (*endptr != '\0' || endptr == optarg
+ || hours <= 0)
+ usage("<hours> must be a positive number");
+ validity += 3600*hours;
+ }
+ continue;
+
+ case 'S': /* --startdate */
+ if (optarg == NULL || strlen(optarg) != 15 || optarg[14] != 'Z')
+ usage("date format must be YYYYMMDDHHMMSSZ");
+ {
+ chunk_t date = { optarg, 15 };
+ notBefore = asn1totime(&date, ASN1_GENERALIZEDTIME);
+ }
+ continue;
+
+ case 'E': /* --enddate */
+ if (optarg == NULL || strlen(optarg) != 15 || optarg[14] != 'Z')
+ usage("date format must be YYYYMMDDHHMMSSZ");
+ {
+ chunk_t date = { optarg, 15 };
+ notAfter = asn1totime(&date, ASN1_GENERALIZEDTIME);
+ }
+ continue;
+
+ case 'o': /* --outt */
+ outfile = optarg;
+ continue ;
+
+#ifdef DEBUG
+ case 'A': /* --debug-all */
+ base_debugging = DBG_ALL;
+ continue;
+#endif
+ default:
+#ifdef DEBUG
+ if (c >= DBG_OFFSET)
+ {
+ base_debugging |= c - DBG_OFFSET;
+ continue;
+ }
+#undef DBG_OFFSET
+#endif
+ bad_case(c);
+ }
+ break;
+ }
+
+ init_log("openac");
+ cur_debugging = base_debugging;
+
+ if (optind != argc)
+ usage("unexpected argument");
+
+ /* load the signer's RSA private key */
+ if (keyfile != NULL)
+ {
+ err_t ugh = NULL;
+
+ signerkey = alloc_thing(RSA_private_key_t, "RSA private key");
+ ugh = load_rsa_private_key(keyfile, &pass, signerkey);
+
+ if (ugh != NULL)
+ {
+ free_RSA_private_content(signerkey);
+ pfree(signerkey);
+ plog("%s", ugh);
+ exit(1);
+ }
+ }
+
+ /* load the signer's X.509 certificate */
+ if (certfile != NULL)
+ {
+ if (!load_cert(certfile, "signer cert", &signercert))
+ exit(1);
+ signer = signercert.u.x509;
+ }
+
+ /* load the users's X.509 certificate */
+ if (usercertfile != NULL)
+ {
+ if (!load_cert(usercertfile, "user cert", &usercert))
+ exit(1);
+ user = usercert.u.x509;
+ }
+
+ /* compute validity interval */
+ validity = (validity)? validity : default_validity;
+ notBefore = (notBefore) ? notBefore : time(NULL);
+ notAfter = (notAfter) ? notAfter : notBefore + validity;
+
+ /* build and parse attribute certificate */
+ if (user != NULL && signer != NULL && signerkey != NULL)
+ {
+ /* read the serial number and increment it by one */
+ serial = read_serial();
+
+ attr_cert = build_attr_cert();
+ ac = alloc_thing(x509acert_t, "x509acert");
+ *ac = empty_ac;
+ parse_ac(attr_cert, ac);
+
+ /* write the attribute certificate to file */
+ if (write_chunk(outfile, "attribute cert", attr_cert, 0022, TRUE))
+ write_serial(serial);
+ }
+
+ /* delete all dynamic objects */
+ if (signerkey != NULL)
+ {
+ free_RSA_private_content(signerkey);
+ pfree(signerkey);
+ }
+ free_x509cert(signercert.u.x509);
+ free_x509cert(usercert.u.x509);
+ free_ietfAttrList(groups);
+ free_acert(ac);
+ pfree(serial.ptr);
+
+#ifdef LEAK_DETECTIVE
+ report_leaks();
+#endif /* LEAK_DETECTIVE */
+ close_log();
+ exit(0);
+}