[rt-commit] r154 - in /redwax-tool/trunk: ChangeLog Makefile.am config.h.in configure.ac redwax-tool.c redwax-tool.h redwax_libical.c redwax_libical.h redwax_openssl.c redwax_util.h

rt-commit at redwax.eu rt-commit at redwax.eu
Sun Oct 15 13:09:28 CEST 2023


Author: minfrin at redwax.eu
Date: Sun Oct 15 13:09:27 2023
New Revision: 154

Log:
Add the --calendar-out and --reminder-out options
to publish certificate expiry to a calendar.

Added:
    redwax-tool/trunk/redwax_libical.c
    redwax-tool/trunk/redwax_libical.h
Modified:
    redwax-tool/trunk/ChangeLog
    redwax-tool/trunk/Makefile.am
    redwax-tool/trunk/config.h.in
    redwax-tool/trunk/configure.ac
    redwax-tool/trunk/redwax-tool.c
    redwax-tool/trunk/redwax-tool.h
    redwax-tool/trunk/redwax_openssl.c
    redwax-tool/trunk/redwax_util.h

Modified: redwax-tool/trunk/ChangeLog
==============================================================================
--- redwax-tool/trunk/ChangeLog	(original)
+++ redwax-tool/trunk/ChangeLog	Sun Oct 15 13:09:27 2023
@@ -1,5 +1,9 @@
 
 Changes with v0.9.3
+
+ *) Add the --calendar-out and --reminder-out options
+    to publish certificate expiry to a calendar.
+    [Graham Leggett]
 
  *) Add the --filter-expiry option to allow acceptance
     of expired leaf and chain certificates. [Graham

Modified: redwax-tool/trunk/Makefile.am
==============================================================================
--- redwax-tool/trunk/Makefile.am	(original)
+++ redwax-tool/trunk/Makefile.am	Sun Oct 15 13:09:27 2023
@@ -8,7 +8,7 @@
 endif
 
 bin_PROGRAMS = redwax-tool
-redwax_tool_SOURCES = redwax-tool.c redwax-tool.h redwax_openssl.c redwax_openssl.h redwax_nss.c redwax_nss.h redwax_p11kit.c redwax_p11kit.h redwax_util.c redwax_util.h
+redwax_tool_SOURCES = redwax-tool.c redwax-tool.h redwax_openssl.c redwax_openssl.h redwax_nss.c redwax_nss.h redwax_p11kit.c redwax_p11kit.h redwax_libical.c redwax_libical.h redwax_util.c redwax_util.h
 
 EXTRA_DIST = redwax-tool.spec
 dist_man_MANS = redwax-tool.1

Modified: redwax-tool/trunk/config.h.in
==============================================================================
--- redwax-tool/trunk/config.h.in	(original)
+++ redwax-tool/trunk/config.h.in	Sun Oct 15 13:09:27 2023
@@ -26,6 +26,9 @@
 
 /* Define to 1 if you have the <libgen.h> header file. */
 #undef HAVE_LIBGEN_H
+
+/* Define to 1 if you have the <libical/ical.h> header file. */
+#undef HAVE_LIBICAL_ICAL_H
 
 /* Define to 1 if your system has a GNU libc compatible `malloc' function, and
    to 0 otherwise. */

Modified: redwax-tool/trunk/configure.ac
==============================================================================
--- redwax-tool/trunk/configure.ac	(original)
+++ redwax-tool/trunk/configure.ac	Sun Oct 15 13:09:27 2023
@@ -57,6 +57,21 @@
     fi
   ])
 
+AC_ARG_WITH(libical,[  --with-libical  use libical library],
+  [
+    if test "$with_libical" != "no"; then
+      PKG_CHECK_MODULES(libical, [libical >= 0.40])
+
+      CFLAGS="$CFLAGS $libical_CFLAGS"
+      CPPFLAGS="$CPPFLAGS $libical_CPPFLAGS"
+      LDFLAGS="$LDFLAGS $libical_LDFLAGS"
+      LIBS="$LIBS $libical_LIBS"
+
+      AC_CHECK_HEADERS(libical/ical.h)
+
+    fi
+  ])
+
 AC_ARG_WITH([bash-completion-dir],
     AS_HELP_STRING([--with-bash-completion-dir[=PATH]],
         [Install the bash auto-completion script in this directory. @<:@default=yes@:>@]),

Modified: redwax-tool/trunk/redwax-tool.c
==============================================================================
--- redwax-tool/trunk/redwax-tool.c	(original)
+++ redwax-tool/trunk/redwax-tool.c	Sun Oct 15 13:09:27 2023
@@ -42,6 +42,7 @@
 #include "redwax_nss.h"
 #include "redwax_openssl.h"
 #include "redwax_p11kit.h"
+#include "redwax_libical.h"
 
 #if HAVE_LIBGEN_H
 #include <libgen.h>
@@ -75,12 +76,15 @@
         APR_HOOK_LINK(complete_pkcs11_module_out);
         APR_HOOK_LINK(process_pkcs11_module_out);
         APR_HOOK_LINK(process_metadata_out);
+        APR_HOOK_LINK(process_calendar_out);
+        APR_HOOK_LINK(process_reminder_out);
         APR_HOOK_LINK(process_ssh_public_out);
         APR_HOOK_LINK(complete_format_out);
         APR_HOOK_LINK(process_jwks_out);
         APR_HOOK_LINK(set_format_out);
         APR_HOOK_LINK(complete_order_out);
         APR_HOOK_LINK(set_order_out);
+        APR_HOOK_LINK(set_calendar_alarm);
         APR_HOOK_LINK(search_chain);
         APR_HOOK_LINK(search_key);
         APR_HOOK_LINK(compare_certificate);
@@ -135,6 +139,10 @@
         (redwax_tool_t * r, const char *mod, redwax_token_quoted_e quoted), (r, mod, quoted), DECLINED);
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_metadata_out,
         (redwax_tool_t * r, const char *arg), (r, arg), DECLINED);
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_calendar_out,
+        (redwax_tool_t * r, const char *arg), (r, arg), DECLINED);
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_reminder_out,
+        (redwax_tool_t * r, const char *arg), (r, arg), DECLINED);
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_ssh_public_out,
         (redwax_tool_t * r, const char *arg, const char *secret), (r, arg, secret), DECLINED);
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, set_format_out,
@@ -145,6 +153,8 @@
         (redwax_tool_t * r, const char *arg), (r, arg), DECLINED);
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, complete_order_out,
         (redwax_tool_t * r, apr_hash_t *orders), (r, orders), DECLINED);
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, set_calendar_alarm,
+        (redwax_tool_t * r, const char *arg), (r, arg), DECLINED);
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_jwks_out,
         (redwax_tool_t * r, const char *arg), (r, arg), DECLINED);
 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, apr_status_t, search_chain,
@@ -211,19 +221,22 @@
 #define REDWAX_TOOL_METADATA_OUT 298
 #define REDWAX_TOOL_METADATA_THRESHOLD 299
 #define REDWAX_TOOL_FORMAT_OUT 300
-#define REDWAX_TOOL_JWKS_OUT 301
-#define REDWAX_TOOL_TEXT_OUT 302
-#define REDWAX_TOOL_NO_TEXT_OUT 303
-#define REDWAX_TOOL_SSH_PRIVATE_OUT 304
-#define REDWAX_TOOL_SSH_PUBLIC_OUT 305
-#define REDWAX_TOOL_SMIMEA_OUT 306
-#define REDWAX_TOOL_SSHFP_OUT 307
-#define REDWAX_TOOL_TLSA_OUT 308
-#define REDWAX_TOOL_USER_IN 309
-#define REDWAX_TOOL_USER_OUT 310
-#define REDWAX_TOOL_GROUP_IN 311
-#define REDWAX_TOOL_GROUP_OUT 312
-#define REDWAX_TOOL_ORDER_OUT 313
+#define REDWAX_TOOL_CALENDAR_OUT 301
+#define REDWAX_TOOL_CALENDAR_ALARM 302
+#define REDWAX_TOOL_REMINDER_OUT 303
+#define REDWAX_TOOL_JWKS_OUT 304
+#define REDWAX_TOOL_TEXT_OUT 305
+#define REDWAX_TOOL_NO_TEXT_OUT 306
+#define REDWAX_TOOL_SSH_PRIVATE_OUT 307
+#define REDWAX_TOOL_SSH_PUBLIC_OUT 308
+#define REDWAX_TOOL_SMIMEA_OUT 309
+#define REDWAX_TOOL_SSHFP_OUT 310
+#define REDWAX_TOOL_TLSA_OUT 311
+#define REDWAX_TOOL_USER_IN 312
+#define REDWAX_TOOL_USER_OUT 313
+#define REDWAX_TOOL_GROUP_IN 314
+#define REDWAX_TOOL_GROUP_OUT 315
+#define REDWAX_TOOL_ORDER_OUT 316
 
 #define REDWAX_EXIT_OK 0
 #define REDWAX_EXIT_INIT 1
@@ -314,6 +327,9 @@
     { "metadata-out", REDWAX_TOOL_METADATA_OUT, 1, "  --metadata-out=file\t\tWrite metadata of each certificate and key to the\n\t\t\t\tgiven file in the format given by the format\n\t\t\t\tparameter." },
     { "metadata-threshold", REDWAX_TOOL_METADATA_THRESHOLD, 1, "  --metadata-threshold=days\tSet the threshold in days below which an expiry\n\t\t\t\tbecomes a warning. If unset, defaults to no\n\t\t\t\twarning." },
     { "format-out", REDWAX_TOOL_FORMAT_OUT, 1, "  --format-out=xml|json|yaml\tFormat of output metadata." },
+    { "calendar-out", REDWAX_TOOL_CALENDAR_OUT, 1, "  --calendar-out=file\t\tWrite a calendar containing entries until the expiry\n\t\t\t\tdate of each certificate to the given file or\n\t\t\t\tdirectory. If a directory is specified, entries will\n\t\t\t\tbe created in discrete ICS files." },
+    { "reminder-out", REDWAX_TOOL_REMINDER_OUT, 1, "  --reminder-out=file\t\tWrite a calendar containing reminders at the expiry\n\t\t\t\tdate of each certificate to the given file or\n\t\t\t\tdirectory. If a directory is specified, entries will\n\t\t\t\tbe created in discrete ICS files." },
+    { "calendar-alarm", REDWAX_TOOL_CALENDAR_ALARM, 1, "  --calendar-alarm=duration\tIf specified, add an alarm to each calendar entry if\n\t\t\t\tnot already present. The alarm format is a RFC5545\n\t\t\t\tDURATION as described in section 3.3.6. Example:\n\t\t\t\t-P1W is one week prior to expiry." },
     { "user-in", REDWAX_TOOL_USER_IN, 1, "  --user-in=user\t\tUse the privileges of this user when reading\n\t\t\t\tcertificates and keys." },
     { "user-out", REDWAX_TOOL_USER_OUT, 1, "  --user-out=user\t\tUse the privileges of this user when writing\n\t\t\t\tcertificates and keys." },
     { "group-in", REDWAX_TOOL_GROUP_IN, 1, "  --group-in=group\t\tUse the privileges of this group when reading\n\t\t\t\tcertificates and keys. If you have set a user\n\t\t\t\tbefore setting a group, you may no longer have\n\t\t\t\tpermission to set the group. It is recommended\n\t\t\t\tthat if user and group are set, the group is set\n\t\t\t\tfirst." },
@@ -2366,6 +2382,32 @@
     return status;
 }
 
+static apr_status_t redwax_calendar_out(redwax_tool_t *r, const char *arg)
+{
+    arg = redwax_home(r, arg);
+
+    apr_status_t status = rt_run_process_calendar_out(r, arg);
+
+    return status;
+}
+
+static apr_status_t redwax_calendar_alarm(redwax_tool_t *r, const char *arg)
+{
+
+    apr_status_t status = rt_run_set_calendar_alarm(r, arg);
+
+    return status;
+}
+
+static apr_status_t redwax_reminder_out(redwax_tool_t *r, const char *arg)
+{
+    arg = redwax_home(r, arg);
+
+    apr_status_t status = rt_run_process_reminder_out(r, arg);
+
+    return status;
+}
+
 static apr_status_t redwax_jwks_out(redwax_tool_t *r, const char *arg)
 {
     arg = redwax_home(r, arg);
@@ -2691,6 +2733,18 @@
             if (redwax_set_threshold(r, optarg)) {
                 return REDWAX_EXIT_OPTIONS;
             }
+            break;
+        }
+        case REDWAX_TOOL_CALENDAR_OUT: {
+            redwax_calendar_out(r, optarg);
+            break;
+        }
+        case REDWAX_TOOL_CALENDAR_ALARM: {
+            redwax_calendar_alarm(r, optarg);
+            break;
+        }
+        case REDWAX_TOOL_REMINDER_OUT: {
+            redwax_reminder_out(r, optarg);
             break;
         }
         case REDWAX_TOOL_SSH_PUBLIC_OUT: {
@@ -3176,6 +3230,9 @@
 #if HAVE_P11_KIT_MODULES_LOAD_AND_INITIALIZE
     redwax_add_default_p11kit_hooks();
 #endif
+#if HAVE_LIBICAL_ICAL_H
+    redwax_add_default_libical_hooks();
+#endif
     redwax_add_default_hooks();
 
     apr_hook_sort_all();

Modified: redwax-tool/trunk/redwax-tool.h
==============================================================================
--- redwax-tool/trunk/redwax-tool.h	(original)
+++ redwax-tool/trunk/redwax-tool.h	Sun Oct 15 13:09:27 2023
@@ -54,16 +54,16 @@
 } redwax_format_e;
 
 typedef enum redwax_order_e {
-	REDWAX_ORDER_ALL = 0,
-	REDWAX_ORDER_KEY_FIRST,
-	REDWAX_ORDER_KEY_LAST,
+    REDWAX_ORDER_ALL = 0,
+    REDWAX_ORDER_KEY_FIRST,
+    REDWAX_ORDER_KEY_LAST,
 } redwax_order_e;
 
 typedef enum redwax_expiry_e {
-	REDWAX_EXPIRY_CHECK = 0,
-	REDWAX_EXPIRY_IGNORE,
-	REDWAX_EXPIRY_IGNORE_LEAF,
-	REDWAX_EXPIRY_IGNORE_CHAIN
+    REDWAX_EXPIRY_CHECK = 0,
+    REDWAX_EXPIRY_IGNORE,
+    REDWAX_EXPIRY_IGNORE_LEAF,
+    REDWAX_EXPIRY_IGNORE_CHAIN
 } redwax_expiry_e;
 
 typedef struct redwax_tool_t {
@@ -106,6 +106,7 @@
     const char *user_out;
     const char *group_in;
     const char *group_out;
+    const char *calendar_alarm;
     redwax_filter_t filter;
     redwax_nss_t nss_out;
     redwax_pkcs11_t pkcs11_in;
@@ -180,6 +181,11 @@
     apr_size_t issuer_len;
     const unsigned char *serial_der;
     apr_size_t serial_len;
+    apr_time_t *before;
+    apr_time_t *after;
+    const char *text;
+    const char *compact;
+    const char *pem;
 } redwax_certificate_x509_t;
 
 typedef struct redwax_certificate_t {
@@ -548,6 +554,22 @@
         (redwax_tool_t *r, const char *arg));
 
 /**
+ * Hook to handle the output of calendar data.
+ *
+ * @param r The redwax-tool context.
+ */
+APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, process_calendar_out,
+        (redwax_tool_t *r, const char *arg));
+
+/**
+ * Hook to handle the output of calendar (reminder) data.
+ *
+ * @param r The redwax-tool context.
+ */
+APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, process_reminder_out,
+        (redwax_tool_t *r, const char *arg));
+
+/**
  * Hook to write SSH public keys.
  *
  * @param r The redwax-tool context.
@@ -585,6 +607,14 @@
  * @param r The redwax-tool context.
  */
 APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, set_order_out,
+        (redwax_tool_t *r, const char *arg));
+
+/**
+ * Hook to set the calendar alarm.
+ *
+ * @param r The redwax-tool context.
+ */
+APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, set_calendar_alarm,
         (redwax_tool_t *r, const char *arg));
 
 /**

Added: redwax-tool/trunk/redwax_libical.c
==============================================================================
--- redwax-tool/trunk/redwax_libical.c	(added)
+++ redwax-tool/trunk/redwax_libical.c	Sun Oct 15 13:09:27 2023
@@ -0,0 +1,701 @@
+/**
+ *    Copyright (C) 2022 Graham Leggett <minfrin at sharp.fm>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/*
+ * redwax_libical - libical routines for generating calendars
+ *
+ */
+
+#include <apr_strings.h>
+#include <apr_uuid.h>
+
+#include "config.h"
+#include "redwax-tool.h"
+
+#include "redwax_util.h"
+
+#if HAVE_LIBICAL_ICAL_H
+
+#include <libical/ical.h>
+
+/**
+ * Keep track of calendar.
+ */
+typedef struct redwax_libical_vcalendar_t {
+    const char *path;
+    const char *file;
+    apr_file_t *out;
+    icalcomponent *cal;
+} redwax_libical_vcalendar_t;
+
+static apr_status_t redwax_libical_initialise(redwax_tool_t *r)
+{
+
+    return OK;
+}
+
+static void redwax_libical_make_vcalendar(redwax_libical_vcalendar_t *vcal)
+{
+    vcal->cal = icalcomponent_vanew(ICAL_VCALENDAR_COMPONENT,
+                       icalproperty_new_version("2.0"),
+                       icalproperty_new_prodid("-//Redwax Project//redwax-tool//EN"),
+                       NULL);
+//    vcal->cal = icalcomponent_new_vcalendar();
+}
+
+static apr_status_t redwax_libical_open_vcalendar(redwax_tool_t *r, const char *path, redwax_libical_vcalendar_t **pvcal)
+{
+    apr_finfo_t finfo;
+
+    apr_file_t *out;
+
+    redwax_libical_vcalendar_t *vcal = apr_pcalloc(r->pool,
+            sizeof(redwax_libical_vcalendar_t));
+
+    apr_status_t status;
+
+    if (!strcmp(path, "-")) {
+        out = r->out;
+        redwax_libical_make_vcalendar(vcal);
+    }
+    else if (APR_SUCCESS == apr_stat(&finfo, path, APR_FINFO_TYPE, r->pool) && finfo.filetype == APR_DIR) {
+        out = NULL;
+    }
+    else {
+        status = apr_file_open(&out, path, APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE,
+                APR_FPROT_OS_DEFAULT, r->pool);
+        if (APR_SUCCESS != status) {
+            redwax_print_error(r,
+                    "Could not open '%s': %pm\n", path, &status);
+            return status;
+        }
+        redwax_libical_make_vcalendar(vcal);
+    }
+
+    vcal->path = path;
+    vcal->out = out;
+
+    *pvcal = vcal;
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_libical_load_vcomponent(redwax_tool_t *r,
+        redwax_libical_vcalendar_t *vcal, icalcomponent_kind kind,
+        const char *uid, icalcomponent **comp)
+{
+    if (vcal->out) {
+        *comp = icalcomponent_new(kind);
+
+        icalcomponent_add_component(vcal->cal, *comp);
+
+        icalcomponent_set_uid(*comp, uid);
+
+    }
+    else {
+        char *calname, *calpath, *buffer;
+
+        apr_file_t *in;
+
+        apr_off_t end = 0, start = 0;
+        apr_size_t bytes_read;
+
+        apr_status_t status;
+
+        calname = apr_pstrcat(r->pool, uid, ".ics", NULL);
+        if (APR_SUCCESS
+                != (status = apr_filepath_merge(&calpath, vcal->path,
+                        calname, APR_FILEPATH_NATIVE, r->pool))) {
+            redwax_print_error(r,
+                    "Could not merge '%s' and '%s': %pm\n", vcal->path, calname, &status);
+            return status;
+        }
+        else {
+            vcal->file = calpath;
+        }
+
+        status = apr_file_open(&in, vcal->file, APR_FOPEN_READ,
+                APR_FPROT_OS_DEFAULT, r->pool);
+        if (APR_ENOENT == status) {
+
+            redwax_libical_make_vcalendar(vcal);
+
+            *comp = icalcomponent_new(kind);
+
+            icalcomponent_set_uid(*comp, uid);
+
+            icalcomponent_add_component(vcal->cal, *comp);
+
+            return APR_SUCCESS;
+        }
+        else if (APR_SUCCESS != status) {
+            redwax_print_error(r,
+                    "Could not read '%s': %pm\n", vcal->file, &status);
+            return status;
+        }
+
+        /* how long is the key? */
+        status = apr_file_seek(in, APR_END, &end);
+        if (APR_SUCCESS != status) {
+            redwax_print_error(r,
+                    "Could not seek '%s': %pm\n", vcal->file, &status);
+            return status;
+        }
+
+        /* back to the beginning */
+        status = apr_file_seek(in, APR_SET, &start);
+        if (APR_SUCCESS != status) {
+            redwax_print_error(r,
+                    "Could not seek '%s': %pm\n", vcal->file, &status);
+            return status;
+        }
+
+        buffer = apr_palloc(r->pool, end + 1);
+        buffer[end] = 0;
+
+        status = apr_file_read_full(in, buffer, end, &bytes_read);
+        if (APR_SUCCESS != status) {
+            redwax_print_error(r,
+                    "Could not get '%s': %pm\n", vcal->file, &status);
+            return status;
+        }
+
+        vcal->cal = icalparser_parse_string(buffer);
+        if(icalerrno != ICAL_NO_ERROR) {
+            redwax_print_error(r,
+                    "Could not parse '%s': %s\n", vcal->file, icalerror_perror());
+            return APR_EINVAL;
+        }
+
+        apr_file_close(in);
+
+        *comp = icalcomponent_get_first_component(vcal->cal, kind);
+
+        while (*comp) {
+            const char *u = icalcomponent_get_uid(*comp);
+
+            if (u && !strcmp(uid, u)) {
+                return APR_SUCCESS;
+            }
+
+            *comp = icalcomponent_get_next_component(*comp, kind);
+        }
+
+        *comp = icalcomponent_new(kind);
+
+        icalcomponent_set_uid(*comp, uid);
+
+        icalcomponent_add_component(vcal->cal, *comp);
+
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_libical_save_vcomponent(redwax_tool_t *r, redwax_libical_vcalendar_t *vcal)
+{
+    if (!vcal->out && vcal->cal) {
+
+        apr_file_t *out;
+
+        apr_status_t status;
+
+        status = apr_file_open(&out, vcal->file, APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE,
+                APR_FPROT_OS_DEFAULT, r->pool);
+        if (APR_SUCCESS != status) {
+            redwax_print_error(r,
+                    "Could not open '%s': %pm\n", vcal->file, &status);
+            return status;
+        }
+
+        status = apr_file_puts(icalcomponent_as_ical_string(vcal->cal), out);
+        if (APR_SUCCESS != status) {
+            redwax_print_error(r,
+                    "Could not write '%s': %pm\n", vcal->file, &status);
+            return status;
+        }
+
+        icalcomponent_free(vcal->cal);
+        vcal->cal = NULL;
+
+        apr_file_close(out);
+        vcal->file = NULL;
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_libical_close_vcalendar(redwax_tool_t *r, redwax_libical_vcalendar_t *vcal)
+{
+    if (vcal->out && vcal->cal) {
+        apr_status_t status;
+
+        status = apr_file_puts(icalcomponent_as_ical_string(vcal->cal), vcal->out);
+
+        icalcomponent_free(vcal->cal);
+
+        return status;
+    }
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t redwax_libical_add_event(redwax_tool_t *r,
+        redwax_libical_vcalendar_t *vcal, const redwax_certificate_t *cert)
+{
+    icalcomponent *event, *alarm;
+
+    apr_status_t status;
+
+    if (cert->x509) {
+
+        const char *uid;
+
+        if (cert->x509->skid_der) {
+
+            uid = redwax_pencode_base16_binary(r->pool,
+                    cert->x509->skid_der, cert->x509->skid_len,
+                    REDWAX_ENCODE_NONE, NULL);
+
+        }
+        else {
+            /* skip */
+            return APR_SUCCESS;
+        }
+
+        status = redwax_libical_load_vcomponent(r, vcal,
+                ICAL_VEVENT_COMPONENT, uid, &event);
+        if (APR_SUCCESS != status) {
+            return status;
+        }
+
+        if (cert->common.subject) {
+
+            icalcomponent_set_summary(event,
+                    apr_psprintf(r->pool, "%s", cert->common.subject));
+
+        }
+
+        if (cert->x509->before) {
+
+            icalcomponent_set_dtstamp(event,
+                    icaltime_from_timet_with_zone(*cert->x509->before,
+                            0, icaltimezone_get_utc_timezone()));
+
+        }
+
+        if (cert->x509->after) {
+
+            struct icaltimetype after = icaltime_from_timet_with_zone(*cert->x509->after,
+                    0, icaltimezone_get_utc_timezone());
+
+            after.is_date = 1;
+
+            icalcomponent_set_dtstart(event, after);
+
+            icaltime_adjust(&after, 1, 0, 0, 0);
+
+            icalcomponent_set_dtend(event, after);
+
+        }
+
+        if (cert->x509->compact && cert->x509->pem) {
+
+            icalcomponent_set_description(event,
+                    apr_pstrcat(r->pool, cert->x509->compact, cert->x509->pem, NULL));
+
+        }
+        else if (cert->x509->compact) {
+
+            icalcomponent_set_description(event, cert->x509->compact);
+
+        }
+        else if (cert->x509->pem) {
+
+            icalcomponent_set_description(event, cert->x509->pem);
+
+        }
+
+        alarm = icalcomponent_get_first_component(event, ICAL_VALARM_COMPONENT);
+
+        if (!alarm && r->calendar_alarm) {
+
+            apr_uuid_t uuid;
+            char ubuf[APR_UUID_FORMATTED_LENGTH + 1];
+
+            struct icaltriggertype trigger;
+
+            trigger = icaltriggertype_from_string(r->calendar_alarm);
+
+            alarm = icalcomponent_new_valarm();
+
+            apr_uuid_get(&uuid);
+            apr_uuid_format(ubuf, &uuid);
+
+            icalcomponent_set_uid(alarm, ubuf);
+
+            icalcomponent_add_property(alarm, icalproperty_new_action(ICAL_ACTION_DISPLAY));
+
+            icalcomponent_set_description(alarm, "Certificate renewal");
+
+            icalcomponent_add_property(alarm, icalproperty_new_trigger(trigger));
+
+            icalcomponent_add_component(event, alarm);
+
+        }
+
+        if (!icalcomponent_get_first_property(event, ICAL_TRANSP_PROPERTY)) {
+
+            icalcomponent_add_property(event,
+                    icalproperty_new_transp(ICAL_TRANSP_TRANSPARENT));
+
+        }
+
+    }
+
+    return redwax_libical_save_vcomponent(r, vcal);
+}
+
+static apr_status_t redwax_libical_add_todo(redwax_tool_t *r,
+        redwax_libical_vcalendar_t *vcal, const redwax_certificate_t *cert)
+{
+    icalcomponent *todo, *alarm;
+
+    apr_status_t status;
+
+    if (cert->x509) {
+
+        const char *uid;
+
+        if (cert->x509->skid_der) {
+
+            uid = redwax_pencode_base16_binary(r->pool,
+                    cert->x509->skid_der, cert->x509->skid_len,
+                    REDWAX_ENCODE_NONE, NULL);
+
+        }
+        else {
+            /* skip */
+            return APR_SUCCESS;
+        }
+
+        status = redwax_libical_load_vcomponent(r, vcal,
+                ICAL_VTODO_COMPONENT, uid, &todo);
+        if (APR_SUCCESS != status) {
+            return status;
+        }
+
+        if (cert->common.subject) {
+
+            icalcomponent_set_summary(todo,
+                    apr_psprintf(r->pool, "%s", cert->common.subject));
+
+        }
+
+        if (cert->x509->before) {
+
+            icalcomponent_set_dtstamp(todo,
+                    icaltime_from_timet_with_zone(*cert->x509->before,
+                            0, icaltimezone_get_utc_timezone()));
+
+            icalcomponent_set_dtstart(todo,
+                    icaltime_from_timet_with_zone(*cert->x509->before,
+                            0, icaltimezone_get_utc_timezone()));
+
+        }
+
+        if (cert->x509->after) {
+
+            icalcomponent_set_due(todo,
+                    icaltime_from_timet_with_zone(*cert->x509->after,
+                            0, icaltimezone_get_utc_timezone()));
+
+        }
+
+        if (cert->x509->compact && cert->x509->pem) {
+
+            icalcomponent_set_description(todo,
+                    apr_pstrcat(r->pool, cert->x509->compact, cert->x509->pem, NULL));
+
+        }
+        else if (cert->x509->compact) {
+
+            icalcomponent_set_description(todo, cert->x509->compact);
+
+        }
+        else if (cert->x509->pem) {
+
+            icalcomponent_set_description(todo, cert->x509->pem);
+
+        }
+
+        alarm = icalcomponent_get_first_component(todo, ICAL_VALARM_COMPONENT);
+
+        if (!alarm && r->calendar_alarm) {
+
+            apr_uuid_t uuid;
+            char ubuf[APR_UUID_FORMATTED_LENGTH + 1];
+
+            struct icaltriggertype trigger;
+
+            trigger = icaltriggertype_from_string(r->calendar_alarm);
+
+            alarm = icalcomponent_new_valarm();
+
+            apr_uuid_get(&uuid);
+            apr_uuid_format(ubuf, &uuid);
+
+            icalcomponent_set_uid(alarm, ubuf);
+
+            icalcomponent_add_property(alarm, icalproperty_new_action(ICAL_ACTION_DISPLAY));
+
+            icalcomponent_set_description(alarm, "Certificate renewal");
+
+            icalcomponent_add_property(alarm, icalproperty_new_trigger(trigger));
+
+            icalcomponent_add_component(todo, alarm);
+
+        }
+    }
+
+    return redwax_libical_save_vcomponent(r, vcal);
+}
+
+static apr_status_t redwax_libical_process_calendar_out(redwax_tool_t *r,
+        const char *file)
+{
+    redwax_libical_vcalendar_t *vcal;
+
+    int i;
+    apr_status_t status;
+
+    if (r->cert_out) {
+
+        for (i = 0; i < r->certs_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->certs_out,
+                    i, const redwax_certificate_t);
+
+            redwax_print_error(r, "calendar-out: certificate: %s\n",
+                    cert->common.subject);
+        }
+    }
+
+    if (r->chain_out) {
+
+        for (i = 0; i < r->intermediates_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->intermediates_out,
+                    i, const redwax_certificate_t);
+
+            redwax_print_error(r, "calendar-out: intermediate: %s\n",
+                    cert->common.subject);
+        }
+    }
+
+    if (r->trust_out) {
+        for (i = 0; i < r->trusted_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->trusted_out, i, const redwax_certificate_t);
+
+            redwax_print_error(r, "calendar-out: trusted: %s\n",
+                    cert->common.subject);
+        }
+    }
+
+    status = redwax_libical_open_vcalendar(r, file, &vcal);
+    if (APR_SUCCESS != status) {
+        return status;
+    }
+
+    if (r->cert_out) {
+
+        for (i = 0; i < r->certs_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->certs_out,
+                    i, const redwax_certificate_t);
+
+            status = redwax_libical_add_event(r, vcal, cert);
+            if (APR_SUCCESS != status) {
+                return status;
+            }
+        }
+
+    }
+
+    if (r->chain_out) {
+
+        for (i = 0; i < r->intermediates_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->intermediates_out,
+                    i, const redwax_certificate_t);
+
+            status = redwax_libical_add_event(r, vcal, cert);
+            if (APR_SUCCESS != status) {
+                return status;
+            }
+        }
+
+    }
+
+    if (r->trust_out) {
+
+        for (i = 0; i < r->trusted_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->trusted_out, i, const redwax_certificate_t);
+
+            status = redwax_libical_add_event(r, vcal, cert);
+            if (APR_SUCCESS != status) {
+                return status;
+            }
+        }
+
+    }
+
+    return redwax_libical_close_vcalendar(r, vcal);
+
+}
+
+static apr_status_t redwax_libical_process_reminder_out(redwax_tool_t *r,
+        const char *file)
+{
+    redwax_libical_vcalendar_t *vcal;
+
+    int i;
+    apr_status_t status;
+
+    if (r->cert_out) {
+
+        for (i = 0; i < r->certs_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->certs_out,
+                    i, const redwax_certificate_t);
+
+            redwax_print_error(r, "reminder-out: certificate: %s\n",
+                    cert->common.subject);
+        }
+    }
+
+    if (r->chain_out) {
+
+        for (i = 0; i < r->intermediates_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->intermediates_out,
+                    i, const redwax_certificate_t);
+
+            redwax_print_error(r, "reminder-out: intermediate: %s\n",
+                    cert->common.subject);
+        }
+    }
+
+    if (r->trust_out) {
+        for (i = 0; i < r->trusted_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->trusted_out, i, const redwax_certificate_t);
+
+            redwax_print_error(r, "reminder-out: trusted: %s\n",
+                    cert->common.subject);
+        }
+    }
+
+    status = redwax_libical_open_vcalendar(r, file, &vcal);
+    if (APR_SUCCESS != status) {
+        return status;
+    }
+
+    if (r->cert_out) {
+
+        for (i = 0; i < r->certs_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->certs_out,
+                    i, const redwax_certificate_t);
+
+            status = redwax_libical_add_todo(r, vcal, cert);
+            if (APR_SUCCESS != status) {
+                return status;
+            }
+        }
+
+    }
+
+    if (r->chain_out) {
+
+        for (i = 0; i < r->intermediates_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->intermediates_out,
+                    i, const redwax_certificate_t);
+
+            status = redwax_libical_add_todo(r, vcal, cert);
+            if (APR_SUCCESS != status) {
+                return status;
+            }
+        }
+
+    }
+
+    if (r->trust_out) {
+
+        for (i = 0; i < r->trusted_out->nelts; i++)
+        {
+            const redwax_certificate_t *cert = &APR_ARRAY_IDX(r->trusted_out, i, const redwax_certificate_t);
+
+            status = redwax_libical_add_todo(r, vcal, cert);
+            if (APR_SUCCESS != status) {
+                return status;
+            }
+        }
+
+    }
+
+    return redwax_libical_close_vcalendar(r, vcal);
+
+}
+
+static apr_status_t redwax_libical_set_calendar_alarm(redwax_tool_t *r,
+        const char *alarm)
+{
+    struct icaltriggertype trigger;
+
+    trigger = icaltriggertype_from_string(alarm);
+
+    if (icaltriggertype_is_null_trigger(trigger) ||
+            icaltriggertype_is_bad_trigger(trigger)) {
+
+        redwax_print_error(r, "calendar-alarm: trigger is bad: %s\n",
+                alarm);
+
+        return APR_EINVAL;
+
+    }
+    else {
+
+        r->calendar_alarm = alarm;
+
+    }
+
+    return APR_SUCCESS;
+}
+
+void redwax_add_default_libical_hooks()
+{
+    rt_hook_initialise(redwax_libical_initialise, NULL, NULL, APR_HOOK_MIDDLE);
+    rt_hook_process_calendar_out(redwax_libical_process_calendar_out, NULL, NULL, APR_HOOK_MIDDLE);
+    rt_hook_process_reminder_out(redwax_libical_process_reminder_out, NULL, NULL, APR_HOOK_MIDDLE);
+    rt_hook_set_calendar_alarm(redwax_libical_set_calendar_alarm, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+#endif

Added: redwax-tool/trunk/redwax_libical.h
==============================================================================
--- redwax-tool/trunk/redwax_libical.h	(added)
+++ redwax-tool/trunk/redwax_libical.h	Sun Oct 15 13:09:27 2023
@@ -0,0 +1,33 @@
+/**
+ *    Copyright (C) 2022 Graham Leggett <minfrin at sharp.fm>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/*
+ * redwax_libical - libical routines for generating calendars
+ *
+ */
+
+#ifndef REDWAX_LIBICAL_H_
+#define REDWAX_LIBICAL_H_
+
+#include "config.h"
+
+#if HAVE_LIBICAL_ICAL_H
+
+void redwax_add_default_libical_hooks();
+
+#endif
+#endif /* REDWAX_LIBICAL_H_ */

Modified: redwax-tool/trunk/redwax_openssl.c
==============================================================================
--- redwax-tool/trunk/redwax_openssl.c	(original)
+++ redwax-tool/trunk/redwax_openssl.c	Sun Oct 15 13:09:27 2023
@@ -818,6 +818,50 @@
     }
 }
 
+static const char *redwax_openssl_x509_text(apr_pool_t *p, X509 *x, unsigned long flags)
+{
+    BIO *bio;
+    char *buf = NULL;
+    int len = 0;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL) {
+        return NULL;
+    }
+
+    if (x) {
+        X509_print_ex(bio, x, XN_FLAG_RFC2253, flags);
+        len = BIO_get_mem_data(bio, &buf);
+    }
+
+    buf = apr_pstrndup(p, buf, len);
+
+    BIO_free(bio);
+
+    return buf;
+}
+
+static const char *redwax_openssl_x509_pem(apr_pool_t *p, X509 *x)
+{
+    BIO *bio;
+    char *buf = NULL;
+    int len = 0;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL) {
+        return NULL;
+    }
+
+    if (x) {
+        PEM_write_bio_X509(bio, x);
+        len = BIO_get_mem_data(bio, &buf);
+    }
+
+    buf = apr_pstrndup(p, buf, len);
+
+    BIO_free(bio);
+
+    return buf;
+}
+
 static const char *redwax_openssl_name(apr_pool_t *p, X509_NAME *name)
 {
     BIO *bio;
@@ -1608,16 +1652,16 @@
         len = BIO_get_mem_data(bio, &buf);
     }
 
-	if ((r->expiry == REDWAX_EXPIRY_IGNORE
-			|| (r->expiry == REDWAX_EXPIRY_IGNORE_LEAF && depth == 0)
-			|| (r->expiry == REDWAX_EXPIRY_IGNORE_CHAIN && depth > 0))
-			&& X509_STORE_CTX_get_error(ctx) == X509_V_ERR_CERT_HAS_EXPIRED) {
-		X509_STORE_CTX_set_error(ctx, X509_V_OK);
-		ok = 1;
-		redwax_print_error(r,
-				"verify-filter: %d: %.*s: certificate expired, accepting anyway\n",
-				X509_STORE_CTX_get_error_depth(ctx), len, buf);
-	}
+    if ((r->expiry == REDWAX_EXPIRY_IGNORE
+            || (r->expiry == REDWAX_EXPIRY_IGNORE_LEAF && depth == 0)
+            || (r->expiry == REDWAX_EXPIRY_IGNORE_CHAIN && depth > 0))
+            && X509_STORE_CTX_get_error(ctx) == X509_V_ERR_CERT_HAS_EXPIRED) {
+        X509_STORE_CTX_set_error(ctx, X509_V_OK);
+        ok = 1;
+        redwax_print_error(r,
+                "verify-filter: %d: %.*s: certificate expired, accepting anyway\n",
+                X509_STORE_CTX_get_error_depth(ctx), len, buf);
+    }
     else if (!ok) {
         redwax_print_error(r,
                 "verify-filter: %d: %.*s: verify failed: %s\n",
@@ -4690,8 +4734,10 @@
     if (r->key_out) {
         for (i = 0; i < r->keys_out->nelts; i++)
         {
+#if 0
             const redwax_key_t
                 *key = &APR_ARRAY_IDX(r->keys_out, i, const redwax_key_t);
+#endif
 
             redwax_print_error(r, "metadata-out: key\n");
         }
@@ -5461,6 +5507,7 @@
         X509_PUBKEY *pub;
         ASN1_INTEGER *integer;
         ASN1_OCTET_STRING *skid;
+        const ASN1_TIME *before, *after;
 
         const unsigned char *der;
 
@@ -5690,6 +5737,26 @@
             x509->serial_der = der = apr_palloc(r->pool, x509->serial_len);
             i2d_ASN1_INTEGER(integer, &der);
         }
+
+        before = X509_get0_notBefore(x);
+        if (before) {
+            struct tm stime = { 0 };
+            ASN1_TIME_to_tm(before, &stime);
+            x509->before = apr_palloc(r->pool, sizeof(apr_time_t));
+            *x509->before = timegm(&stime);
+        }
+
+        after = X509_get0_notAfter(x);
+        if (after) {
+            struct tm stime = { 0 };
+            ASN1_TIME_to_tm(after, &stime);
+            x509->after = apr_palloc(r->pool, sizeof(apr_time_t));
+            *x509->after = timegm(&stime);
+        }
+
+        x509->text = redwax_openssl_x509_text(r->pool, x, 0);
+        x509->compact = redwax_openssl_x509_text(r->pool, x, X509_FLAG_NO_VERSION | X509_FLAG_NO_PUBKEY | X509_FLAG_NO_SIGDUMP);
+        x509->pem = redwax_openssl_x509_pem(r->pool, x);
 
         X509_free(x);
 

Modified: redwax-tool/trunk/redwax_util.h
==============================================================================
--- redwax-tool/trunk/redwax_util.h	(original)
+++ redwax-tool/trunk/redwax_util.h	Sun Oct 15 13:09:27 2023
@@ -91,11 +91,11 @@
 typedef struct redwax_metadata_level_t {
     void *k;
     int klen;
-    int root:1;
-    int object:1;
-    int array:1;
-    int empty:1;
-    int next:1;
+    unsigned int root:1;
+    unsigned int object:1;
+    unsigned int array:1;
+    unsigned int empty:1;
+    unsigned int next:1;
 } redwax_metadata_level_t;
 
 /**



More information about the rt-commit mailing list