krb5 commit: Add regexp support on Windows using std::regex

ghudson at mit.edu ghudson at mit.edu
Tue Dec 24 14:32:05 EST 2024


https://github.com/krb5/krb5/commit/04b22c1c2dfbf7954b6cb114527a50a95ebccad3
commit 04b22c1c2dfbf7954b6cb114527a50a95ebccad3
Author: Ken Hornstein <kenh at cmf.nrl.navy.mil>
Date:   Fri Nov 29 20:34:53 2024 -0500

    Add regexp support on Windows using std::regex
    
    Provide glue code in libkrb5support to emulate the POSIX regex(3)
    interface by calling into the C++ std::regex classes.
    
    Eliminate compatibility code for pre-POSIX regex functions.
    
    [ghudson at mit.edu: additional cleanup of regexp callers; minor style
    adjustments; removed regerrlen field; edited commit message]

 src/aclocal.m4                   |  48 ------------
 src/build-tools/krb5-config.in   |   4 +-
 src/config/pre.in                |   5 +-
 src/configure.ac                 |  11 +--
 src/include/k5-platform.h        |   8 ++
 src/include/k5-regex.h           | 118 ++++++++++++++++++++++++++++++
 src/kadmin/dbutil/dump.c         |  47 +-----------
 src/kadmin/dbutil/kdb5_mkey.c    |  63 ++--------------
 src/lib/kadm5/srv/Makefile.in    |   2 +-
 src/lib/kadm5/srv/svr_iters.c    |  53 ++------------
 src/lib/krb5/Makefile.in         |   2 +-
 src/lib/krb5/os/localauth_rule.c |  15 +---
 src/util/support/Makefile.in     |  17 ++++-
 src/util/support/regex.cpp       | 153 +++++++++++++++++++++++++++++++++++++++
 14 files changed, 311 insertions(+), 235 deletions(-)

diff --git a/src/aclocal.m4 b/src/aclocal.m4
index 4b57d3bd0..7397bdcc2 100644
--- a/src/aclocal.m4
+++ b/src/aclocal.m4
@@ -714,53 +714,6 @@ KRB5_NEED_PROTO([#ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif],daemon,1)])dnl
 
-dnl
-dnl KRB5_AC_NEED_LIBGEN --- check if libgen needs to be linked in for
-dnl 				compile/step	
-dnl
-dnl
-AC_DEFUN(KRB5_AC_NEED_LIBGEN,[
-AC_REQUIRE([AC_PROG_CC])dnl
-dnl
-dnl regcomp is present but non-functional on Solaris 2.4
-dnl
-AC_MSG_CHECKING(for working regcomp)
-AC_CACHE_VAL(ac_cv_func_regcomp,
-[AC_RUN_IFELSE(
-  [AC_LANG_PROGRAM(
-    [[#include <sys/types.h>
-      #include <regex.h>
-      regex_t x; regmatch_t m;]],
-    [[return regcomp(&x,"pat.*",0) || regexec(&x,"pattern",1,&m,0);]])],
-  [ac_cv_func_regcomp=yes], [ac_cv_func_regcomp=no],
-  [AC_MSG_ERROR([Cannot test regcomp when cross compiling])])])
-AC_MSG_RESULT($ac_cv_func_regcomp)
-test $ac_cv_func_regcomp = yes && AC_DEFINE(HAVE_REGCOMP,1,[Define if regcomp exists and functions])
-dnl
-dnl Check for the compile and step functions - only if regcomp is not available
-dnl
-if test $ac_cv_func_regcomp = no; then
- save_LIBS="$LIBS"
- LIBS=-lgen
-dnl this will fail if there's no compile/step in -lgen, or if there's
-dnl no -lgen.  This is fine.
- AC_CHECK_FUNCS(compile step)
- LIBS="$save_LIBS"
-dnl
-dnl Set GEN_LIB if necessary 
-dnl
- AC_CHECK_LIB(gen, compile, GEN_LIB=-lgen, GEN_LIB=)
- AC_SUBST(GEN_LIB)
-fi
-])
-dnl
-dnl KRB5_AC_REGEX_FUNCS --- check for different regular expression 
-dnl				support functions
-dnl
-AC_DEFUN(KRB5_AC_REGEX_FUNCS,[
-AC_CHECK_FUNCS(re_comp re_exec regexec)
-AC_REQUIRE([KRB5_AC_NEED_LIBGEN])dnl
-])dnl
 dnl
 dnl WITH_HESIOD
 dnl
@@ -850,7 +803,6 @@ dnl Set variables to build a program.
 
 AC_DEFUN(KRB5_BUILD_PROGRAM,
 [AC_REQUIRE([KRB5_LIB_AUX])dnl
-AC_REQUIRE([KRB5_AC_NEED_LIBGEN])dnl
 AC_SUBST(CC_LINK)
 AC_SUBST(CXX_LINK)
 AC_SUBST(RPATH_FLAG)
diff --git a/src/build-tools/krb5-config.in b/src/build-tools/krb5-config.in
index 8e6eb8660..2cb439887 100755
--- a/src/build-tools/krb5-config.in
+++ b/src/build-tools/krb5-config.in
@@ -42,7 +42,6 @@ DEFKTNAME='@DEFKTNAME@'
 DEFCKTNAME='@DEFCKTNAME@'
 
 LIBS='@LIBS@'
-GEN_LIB=@GEN_LIB@
 
 # Defaults for program
 library=krb5
@@ -253,8 +252,7 @@ if test -n "$do_libs"; then
     fi
 
     # If we ever support a flag to generate output suitable for static
-    # linking, we would output "-lkrb5support $GEN_LIB $LIBS $DL_LIB"
-    # here.
+    # linking, we would output "-lkrb5support $LIBS $DL_LIB" here.
 
     echo $lib_flags
 fi
diff --git a/src/config/pre.in b/src/config/pre.in
index 39c417e43..1197c1ffd 100644
--- a/src/config/pre.in
+++ b/src/config/pre.in
@@ -348,9 +348,6 @@ VERTO_DEPS-k5	= $(BUILDTOP)/include/verto.h
 
 # LIBS gets substituted in... e.g. -lnsl -lsocket
 
-# GEN_LIB is -lgen if needed for regexp
-GEN_LIB		= @GEN_LIB@
-
 # Editline or readline flags and libraries.
 RL_CFLAGS	= @RL_CFLAGS@
 RL_LIBS		= @RL_LIBS@
@@ -380,7 +377,7 @@ SUPPORT_LIB			= -l$(SUPPORT_LIBNAME)
 # HESIOD_LIBS is -lhesiod...
 HESIOD_LIBS	= @HESIOD_LIBS@
 
-KRB5_BASE_LIBS	= $(KRB5_LIB) $(K5CRYPTO_LIB) $(COM_ERR_LIB) $(SUPPORT_LIB) $(GEN_LIB) $(LIBS) $(DL_LIB)
+KRB5_BASE_LIBS	= $(KRB5_LIB) $(K5CRYPTO_LIB) $(COM_ERR_LIB) $(SUPPORT_LIB) $(LIBS) $(DL_LIB)
 KDB5_LIBS	= $(KDB5_LIB) $(GSSRPC_LIBS)
 GSS_LIBS	= $(GSS_KRB5_LIB)
 # needs fixing if ever used on macOS!
diff --git a/src/configure.ac b/src/configure.ac
index 9d4e08a70..4325fae99 100644
--- a/src/configure.ac
+++ b/src/configure.ac
@@ -490,18 +490,9 @@ AC_SUBST(GETTIMEOFDAY_ST_OBJ)
 AC_SUBST(EXTRA_SUPPORT_SYMS)
 
 DECLARE_SYS_ERRLIST
-AC_CHECK_HEADERS(unistd.h paths.h regex.h regexpr.h fcntl.h memory.h ifaddrs.h sys/filio.h byteswap.h machine/endian.h machine/byte_order.h sys/bswap.h endian.h pwd.h arpa/inet.h alloca.h dlfcn.h limits.h sys/random.h)
-AC_CHECK_HEADER(regexp.h, [], [],
-[#define INIT char *sp = instring;
-#define GETC() (*sp++)
-#define PEEKC() (*sp)
-#define UNGETC(c) (--sp)
-#define RETURN(c) return(c)
-#define ERROR(c)
-])
+AC_CHECK_HEADERS(unistd.h paths.h fcntl.h memory.h ifaddrs.h sys/filio.h byteswap.h machine/endian.h machine/byte_order.h sys/bswap.h endian.h pwd.h arpa/inet.h alloca.h dlfcn.h limits.h sys/random.h)
 AC_CHECK_MEMBERS([struct stat.st_mtimensec,struct stat.st_mtimespec.tv_nsec,struct stat.st_mtim.tv_nsec],,,[#include <sys/types.h>
 #include <sys/stat.h>])
-KRB5_AC_REGEX_FUNCS
 AC_TYPE_OFF_T
 
 # Fancy caching of perror result...
diff --git a/src/include/k5-platform.h b/src/include/k5-platform.h
index 77bd6e18a..0a19b3668 100644
--- a/src/include/k5-platform.h
+++ b/src/include/k5-platform.h
@@ -70,6 +70,10 @@
 #include <unistd.h>
 #endif
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #ifdef _WIN32
 #define CAN_COPY_VA_LIST
 #endif
@@ -1151,4 +1155,8 @@ extern char *k5_secure_getenv(const char *name);
 int k5_dir_filenames(const char *dirname, char ***fnames_out);
 void k5_free_filenames(char **fnames);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* K5_PLATFORM_H */
diff --git a/src/include/k5-regex.h b/src/include/k5-regex.h
new file mode 100644
index 000000000..e43f65c71
--- /dev/null
+++ b/src/include/k5-regex.h
@@ -0,0 +1,118 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* include/k5-regex.h - Compatibility glue for std::regex on Windows */
+
+/*
+ * Copyright (C) 2024 United States Government as represented by the
+ * Secretary of the Navy.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * On POSIX platforms we can use the standardized regcomp()/regexec() function
+ * calls.  Windows does not provide POSIX regex functions, but does provide a
+ * C++ interface (std::regex) that has the same functionality.
+ */
+
+#ifndef _K5_REGEX_H_
+#define _K5_REGEX_H_
+
+#ifndef _WIN32
+
+/* On POSIX platforms, just include regex.h. */
+#include <regex.h>
+
+#else /* _WIN32 */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* On Windows, emulate the POSIX interface using functions from
+ * libkrb5support. */
+
+typedef struct {
+    size_t re_nsub;             /* Number of subexpressions */
+    void *regex;                /* Pointer to std::basic_regex */
+    char errmsg[128];           /* Regular expression error message */
+} regex_t;
+
+typedef ssize_t regoff_t;
+
+typedef struct {
+    regoff_t rm_so;
+    regoff_t rm_eo;
+} regmatch_t;
+
+/*
+ * Flags to k5_regcomp()
+ */
+
+#define REG_BASIC       0x00    /* Basic regular expressions */
+#define REG_EXTENDED    0x01    /* Extended regular expressions */
+#define REG_ICASE       0x02    /* Case-insensitive match */
+#define REG_NOSUB       0x04    /* Do not do submatching */
+
+/*
+ * Flags to k5_regexec()
+ */
+
+#define REG_NOTBOL      0x01    /* First character not at beginning of line */
+#define REG_NOTEOL      0x02    /* Last character not at end of line */
+
+/*
+ * Error return codes for k5_regcomp()/k5_regexec()
+ *
+ * We only define REG_NOMATCH and REG_BADPAT, since no Kerberos code looks
+ * for anything other than success and REG_NOMATCH.
+ */
+
+#define REG_NOMATCH     1
+#define REG_BADPAT      2
+
+/*
+ * Note that we don't follow the POSIX API exactly because k5_regexec()
+ * doesn't declare regex_t as const; that's so we can store an error
+ * string.
+ */
+int k5_regcomp(regex_t *preg, const char *pattern, int flags);
+int k5_regexec(regex_t *preg, const char *string, size_t,
+               regmatch_t pmatch[], int flags);
+size_t k5_regerror(int code, const regex_t *preg, char *errmsg,
+                   size_t errmsg_size);
+void k5_regfree(regex_t *preg);
+
+#define regcomp k5_regcomp
+#define regexec k5_regexec
+#define regerror k5_regerror
+#define regfree k5_regfree
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* _WIN32 */
+#endif /* _K5_REGEX_H_ */
diff --git a/src/kadmin/dbutil/dump.c b/src/kadmin/dbutil/dump.c
index f964e5ca9..e45551a20 100644
--- a/src/kadmin/dbutil/dump.c
+++ b/src/kadmin/dbutil/dump.c
@@ -34,9 +34,7 @@
 #include <kdb.h>
 #include <com_err.h>
 #include "kdb5_util.h"
-#if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
-#include <regex.h>
-#endif  /* HAVE_REGEX_H */
+#include "k5-regex.h"
 
 /* Needed for master key conversion. */
 static krb5_boolean mkey_convert;
@@ -47,18 +45,6 @@ krb5_kvno new_mkvno;
 #define K5Q(x) K5Q1(x)
 #define K5CONST_WIDTH_SCANF_STR(x) "%" K5Q(x) "s"
 
-/* Use compile(3) if no regcomp present. */
-#if !defined(HAVE_REGCOMP) && defined(HAVE_REGEXP_H)
-#define INIT char *sp = instring;
-#define GETC() (*sp++)
-#define PEEKC() (*sp)
-#define UNGETC(c) (--sp)
-#define RETURN(c) return(c)
-#define ERROR(c)
-#define RE_BUF_SIZE 1024
-#include <regexp.h>
-#endif /* !HAVE_REGCOMP && HAVE_REGEXP_H */
-
 typedef krb5_error_code (*dump_func)(krb5_context context,
                                      krb5_db_entry *entry, const char *name,
                                      FILE *fp, krb5_boolean verbose,
@@ -236,23 +222,15 @@ update_ok_file(krb5_context context, int fd)
 static int
 name_matches(char *name, struct dump_args *args)
 {
-#if HAVE_REGCOMP
     regex_t reg;
     regmatch_t rmatch;
     int st;
     char errmsg[BUFSIZ];
-#elif   HAVE_REGEXP_H
-    char regexp_buffer[RE_BUF_SIZE];
-#elif   HAVE_RE_COMP
-    extern char *re_comp();
-    char *re_result;
-#endif  /* HAVE_RE_COMP */
     int i, match;
 
     /* Check each regular expression in args. */
     match = args->nnames ? 0 : 1;
     for (i = 0; i < args->nnames && !match; i++) {
-#if HAVE_REGCOMP
         /* Compile the regular expression. */
         st = regcomp(&reg, args->names[i], REG_EXTENDED);
         if (st) {
@@ -274,29 +252,6 @@ name_matches(char *name, struct dump_args *args)
             break;
         }
         regfree(&reg);
-#elif HAVE_REGEXP_H
-        /* Compile the regular expression. */
-        compile(args->names[i], regexp_buffer, &regexp_buffer[RE_BUF_SIZE],
-                '\0');
-        if (step(name, regexp_buffer)) {
-            if (loc1 == name && loc2 == &name[strlen(name)])
-                match = 1;
-        }
-#elif HAVE_RE_COMP
-        /* Compile the regular expression. */
-        re_result = re_comp(args->names[i]);
-        if (re_result) {
-            fprintf(stderr, _("%s: regular expression error: %s\n"), progname,
-                    re_result);
-            break;
-        }
-        if (re_exec(name))
-            match = 1;
-#else /* HAVE_RE_COMP */
-        /* If no regular expression support, then just compare the strings. */
-        if (!strcmp(args->names[i], name))
-            match = 1;
-#endif /* HAVE_REGCOMP */
     }
     return match;
 }
diff --git a/src/kadmin/dbutil/kdb5_mkey.c b/src/kadmin/dbutil/kdb5_mkey.c
index aceb0a9b8..0088c8eaf 100644
--- a/src/kadmin/dbutil/kdb5_mkey.c
+++ b/src/kadmin/dbutil/kdb5_mkey.c
@@ -11,22 +11,7 @@
 #include <adm_proto.h>
 #include "kdb5_util.h"
 #include <time.h>
-
-#if defined(HAVE_COMPILE) && defined(HAVE_STEP)
-#define SOLARIS_REGEXPS
-#elif defined(HAVE_REGCOMP) && defined(HAVE_REGEXEC)
-#define POSIX_REGEXPS
-#elif defined(HAVE_RE_COMP) && defined(HAVE_RE_EXEC)
-#define BSD_REGEXPS
-#else
-#error I cannot find any regexp functions
-#endif
-#ifdef SOLARIS_REGEXPS
-#include        <regexpr.h>
-#endif
-#ifdef POSIX_REGEXPS
-#include        <regex.h>
-#endif
+#include "k5-regex.h"
 
 extern krb5_keyblock master_keyblock; /* current mkey */
 extern krb5_kvno   master_kvno;
@@ -644,15 +629,7 @@ struct update_enc_mkvno {
     unsigned int updated;
     unsigned int dry_run : 1;
     unsigned int verbose : 1;
-#ifdef SOLARIS_REGEXPS
-    char *expbuf;
-#endif
-#ifdef POSIX_REGEXPS
     regex_t preg;
-#endif
-#if !defined(SOLARIS_REGEXPS) && !defined(POSIX_REGEXPS)
-    unsigned char placeholder;
-#endif
 };
 
 /* XXX Duplicated in libkadm5srv! */
@@ -667,10 +644,10 @@ struct update_enc_mkvno {
  *
  * Effects:
  *
- * regexp is filled in with allocated memory contained a regular
- * expression to be used with re_comp/compile that matches what the
- * shell-style glob would match.  If glob does not contain an "@"
- * character and realm is not NULL, "@*" is appended to the regexp.
+ * regexp is filled in with allocated memory containing a regular
+ * expression that matches what the shell-style glob would match.
+ * If glob does not contain an "@" character and realm is not
+ * NULL, "@*" is appended to the regexp.
  *
  * Conversion algorithm:
  *
@@ -745,7 +722,6 @@ update_princ_encryption_1(void *cb, krb5_db_entry *ent)
     struct update_enc_mkvno *p = cb;
     char *pname = 0;
     krb5_error_code retval;
-    int match;
     krb5_timestamp now;
     int result;
     krb5_kvno old_mkvno;
@@ -761,18 +737,8 @@ update_princ_encryption_1(void *cb, krb5_db_entry *ent)
         goto skip;
     }
 
-#ifdef SOLARIS_REGEXPS
-    match = (step(pname, p->expbuf) != 0);
-#endif
-#ifdef POSIX_REGEXPS
-    match = (regexec(&p->preg, pname, 0, NULL, 0) == 0);
-#endif
-#ifdef BSD_REGEXPS
-    match = (re_exec(pname) != 0);
-#endif
-    if (!match) {
+    if (regexec(&p->preg, pname, 0, NULL, 0) != 0)
         goto skip;
-    }
     p->re_match_count++;
     retval = krb5_dbe_get_mkvno(util_context, ent, &old_mkvno);
     if (retval) {
@@ -868,9 +834,6 @@ kdb5_update_princ_encryption(int argc, char *argv[])
     krb5_error_code retval;
     krb5_actkvno_node *actkvno_list = 0;
     krb5_db_entry *master_entry = NULL;
-#ifdef BSD_REGEXPS
-    char *msg;
-#endif
     char *regexp = NULL;
     krb5_keyblock *act_mkey;
     krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context);
@@ -917,17 +880,7 @@ kdb5_update_princ_encryption(int argc, char *argv[])
         goto cleanup;
     }
 
-    if (
-#ifdef SOLARIS_REGEXPS
-        ((data.expbuf = compile(regexp, NULL, NULL)) == NULL)
-#endif
-#ifdef POSIX_REGEXPS
-        ((regcomp(&data.preg, regexp, REG_NOSUB)) != 0)
-#endif
-#ifdef BSD_REGEXPS
-        ((msg = (char *) re_comp(regexp)) != NULL)
-#endif
-    ) {
+    if (regcomp(&data.preg, regexp, REG_NOSUB) != 0) {
         /* XXX syslog msg or regerr(regerrno) */
         com_err(progname, 0, _("error compiling converted regexp '%s'"),
                 regexp);
@@ -1004,9 +957,7 @@ kdb5_update_princ_encryption(int argc, char *argv[])
 cleanup:
     krb5_db_free_principal(util_context, master_entry);
     free(regexp);
-#ifdef POSIX_REGEXPS
     regfree(&data.preg);
-#endif
     memset(&new_master_keyblock, 0, sizeof(new_master_keyblock));
     krb5_dbe_free_actkvno_list(util_context, actkvno_list);
 }
diff --git a/src/lib/kadm5/srv/Makefile.in b/src/lib/kadm5/srv/Makefile.in
index d218ea487..9e173b048 100644
--- a/src/lib/kadm5/srv/Makefile.in
+++ b/src/lib/kadm5/srv/Makefile.in
@@ -21,7 +21,7 @@ SHLIB_EXPDEPS=\
 	$(TOPLIBD)/libk5crypto$(SHLIBEXT) \
 	$(COM_ERR_DEPLIB) $(SUPPORT_LIBDEP)
 SHLIB_EXPLIBS =	-lgssrpc -lgssapi_krb5 -lkdb5 $(KDB5_DB_LIB) -lkrb5 \
-		-lk5crypto $(SUPPORT_LIB) $(COM_ERR_LIB) @GEN_LIB@ $(LIBS)
+		-lk5crypto $(SUPPORT_LIB) $(COM_ERR_LIB) $(LIBS)
 RELDIR=kadm5/srv
 
 SRCS =	$(srcdir)/pwqual.c \
diff --git a/src/lib/kadm5/srv/svr_iters.c b/src/lib/kadm5/srv/svr_iters.c
index d5a99dea0..4b26bc939 100644
--- a/src/lib/kadm5/srv/svr_iters.c
+++ b/src/lib/kadm5/srv/svr_iters.c
@@ -6,25 +6,11 @@
  */
 
 #include "autoconf.h"
-#if defined(HAVE_COMPILE) && defined(HAVE_STEP)
-#define SOLARIS_REGEXPS
-#elif defined(HAVE_REGCOMP) && defined(HAVE_REGEXEC)
-#define POSIX_REGEXPS
-#elif defined(HAVE_RE_COMP) && defined(HAVE_RE_EXEC)
-#define BSD_REGEXPS
-#else
-#error I cannot find any regexp functions
-#endif
 
 #include        <sys/types.h>
 #include        <string.h>
 #include        <kadm5/admin.h>
-#ifdef SOLARIS_REGEXPS
-#include        <regexpr.h>
-#endif
-#ifdef POSIX_REGEXPS
-#include        <regex.h>
-#endif
+#include        "k5-regex.h"
 #include <stdlib.h>
 
 #include        "server_internal.h"
@@ -35,12 +21,7 @@ struct iter_data {
     int n_names, sz_names;
     unsigned int malloc_failed;
     char *exp;
-#ifdef SOLARIS_REGEXPS
-    char *expbuf;
-#endif
-#ifdef POSIX_REGEXPS
     regex_t preg;
-#endif
 };
 
 /* XXX Duplicated in kdb5_util!  */
@@ -56,9 +37,9 @@ struct iter_data {
  * Effects:
  *
  * regexp is filled in with allocated memory contained a regular
- * expression to be used with re_comp/compile that matches what the
- * shell-style glob would match.  If glob does not contain an "@"
- * character and realm is not NULL, "@*" is appended to the regexp.
+ * expression that matches what the shell-style glob would match.
+ * If glob does not contain an "@" character and realm is not
+ * NULL, "@*" is appended to the regexp.
  *
  * Conversion algorithm:
  *
@@ -130,15 +111,7 @@ static kadm5_ret_t glob_to_regexp(char *glob, char *realm, char **regexp)
 static void get_either_iter(struct iter_data *data, char *name)
 {
     int match;
-#ifdef SOLARIS_REGEXPS
-    match = (step(name, data->expbuf) != 0);
-#endif
-#ifdef POSIX_REGEXPS
     match = (regexec(&data->preg, name, 0, NULL, 0) == 0);
-#endif
-#ifdef BSD_REGEXPS
-    match = (re_exec(name) != 0);
-#endif
     if (match) {
         if (data->n_names == data->sz_names) {
             int new_sz = data->sz_names * 2;
@@ -184,9 +157,6 @@ static kadm5_ret_t kadm5_get_either(int princ,
                                     int *count)
 {
     struct iter_data data;
-#ifdef BSD_REGEXPS
-    char *msg;
-#endif
     char *regexp = NULL;
     int i, ret;
     kadm5_server_handle_t handle = server_handle;
@@ -202,18 +172,7 @@ static kadm5_ret_t kadm5_get_either(int princ,
                               &regexp)) != KADM5_OK)
         return ret;
 
-    if (
-#ifdef SOLARIS_REGEXPS
-        ((data.expbuf = compile(regexp, NULL, NULL)) == NULL)
-#endif
-#ifdef POSIX_REGEXPS
-        ((regcomp(&data.preg, regexp, REG_NOSUB)) != 0)
-#endif
-#ifdef BSD_REGEXPS
-        ((msg = (char *) re_comp(regexp)) != NULL)
-#endif
-    )
-    {
+    if (regcomp(&data.preg, regexp, REG_NOSUB) != 0) {
         /* XXX syslog msg or regerr(regerrno) */
         free(regexp);
         return EINVAL;
@@ -236,9 +195,7 @@ static kadm5_ret_t kadm5_get_either(int princ,
     }
 
     free(regexp);
-#ifdef POSIX_REGEXPS
     regfree(&data.preg);
-#endif
     if ( !ret && data.malloc_failed)
         ret = ENOMEM;
     if ( ret ) {
diff --git a/src/lib/krb5/Makefile.in b/src/lib/krb5/Makefile.in
index 67fd7c009..ece308460 100644
--- a/src/lib/krb5/Makefile.in
+++ b/src/lib/krb5/Makefile.in
@@ -45,7 +45,7 @@ RELDIR=krb5
 SHLIB_EXPDEPS = \
 	$(TOPLIBD)/libk5crypto$(SHLIBEXT) \
 	$(COM_ERR_DEPLIB) $(SUPPORT_DEPLIB)
-SHLIB_EXPLIBS=-lk5crypto $(COM_ERR_LIB) $(SUPPORT_LIB) @GEN_LIB@ \
+SHLIB_EXPLIBS=-lk5crypto $(COM_ERR_LIB) $(SUPPORT_LIB) \
 	@MACOS_FRAMEWORK@ $(LIBS)
 
 all-unix: all-liblinks
diff --git a/src/lib/krb5/os/localauth_rule.c b/src/lib/krb5/os/localauth_rule.c
index 056857633..860a1e40e 100644
--- a/src/lib/krb5/os/localauth_rule.c
+++ b/src/lib/krb5/os/localauth_rule.c
@@ -68,9 +68,7 @@
 #include "os-proto.h"
 #include <krb5/localauth_plugin.h>
 #include <ctype.h>
-
-#ifdef HAVE_REGEX_H
-#include <regex.h>
+#include "k5-regex.h"
 
 /* Process the match portion of a rule and update *contextp.  Return
  * KRB5_LNAME_NOTRANS if selstring doesn't match the regexp. */
@@ -303,17 +301,6 @@ cleanup:
     return ret;
 }
 
-#else /* HAVE_REGEX_H */
-
-static krb5_error_code
-an2ln_rule(krb5_context context, krb5_localauth_moddata data, const char *type,
-           const char *rule, krb5_const_principal aname, char **lname_out)
-{
-    return KRB5_LNAME_NOTRANS;
-}
-
-#endif
-
 static void
 freestr(krb5_context context, krb5_localauth_moddata data, char *str)
 {
diff --git a/src/util/support/Makefile.in b/src/util/support/Makefile.in
index 86d5a950a..f63e44a4d 100644
--- a/src/util/support/Makefile.in
+++ b/src/util/support/Makefile.in
@@ -58,6 +58,11 @@ SECURE_GETENV_OBJ= @SECURE_GETENV_OBJ@
 ##DOS##SECURE_GETENV_ST_OBJ=
 ##DOS##SECURE_GETENV_OBJ=
 
+K5_REGEX_ST_OBJ=
+K5_REGEX_OBJ=
+##DOS##K5_REGEX_ST_OBJ= regex.o
+##DOS##K5_REGEX_OBJ= $(OUTPRE)regex.$(OBJEXT)
+
 IPC_ST_OBJ=
 IPC_OBJ=
 ##DOS##IPC_ST_OBJ= ipc_stream.o
@@ -99,7 +104,8 @@ STLIBOBJS= \
 	$(MKSTEMP_ST_OBJ) \
 	$(GETOPT_ST_OBJ) \
 	$(GETOPT_LONG_ST_OBJ) \
-	$(SECURE_GETENV_OBJ)
+	$(SECURE_GETENV_OBJ) \
+	$(K5_REGEX_ST_OBJ)
 
 LIBOBJS= \
 	$(OUTPRE)threads.$(OBJEXT) \
@@ -128,7 +134,8 @@ LIBOBJS= \
 	$(MKSTEMP_OBJ) \
 	$(GETOPT_OBJ) \
 	$(GETOPT_LONG_OBJ) \
-	$(SECURE_GETENV_OBJ)
+	$(SECURE_GETENV_OBJ) \
+	$(K5_REGEX_OBJ)
 
 SRCS=\
 	$(srcdir)/threads.c \
@@ -164,7 +171,8 @@ SRCS=\
 	$(srcdir)/t_utf16.c \
 	$(srcdir)/getopt.c \
 	$(srcdir)/getopt_long.c \
-	$(srcdir)/secure_getenv.c
+	$(srcdir)/secure_getenv.c \
+	$(srcdir)/regex.c
 
 SHLIB_EXPDEPS =
 # Add -lm if dumping thread stats, for sqrt.
@@ -193,7 +201,8 @@ SHLIB_EXPORT_FILE=libkrb5support.exports
 EXTRA_SUPPORT_SYMS= @EXTRA_SUPPORT_SYMS@
 ##DOS##EXTRA_SUPPORT_SYMS= krb5int_mkstemp krb5int_strlcpy krb5int_strlcat \
 ##DOS##		k5_getopt k5_getopt_long \
-##DOS##		krb5int_vasprintf krb5int_asprintf krb5int_gettimeofday $(IPC_SYMS)
+##DOS##		krb5int_vasprintf krb5int_asprintf krb5int_gettimeofday $(IPC_SYMS) \
+##DOS##		k5_regcomp k5_regexec k5_regerror k5_regfree
 ##DOS##DATA_SUPPORT_SYMS= k5_opterr k5_optind k5_optopt k5_optarg
 
 ##DOS##!if 0
diff --git a/src/util/support/regex.cpp b/src/util/support/regex.cpp
new file mode 100644
index 000000000..5b6ef0344
--- /dev/null
+++ b/src/util/support/regex.cpp
@@ -0,0 +1,153 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* regex.cpp - Glue routines to std::regex functions */
+
+/*
+ * Copyright (C) 2024 United States Government as represented by the
+ * Secretary of the Navy.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * These functions provide a mostly-complete POSIX regex(3)
+ * implementation using C++ std::regex.  Deficiencies are noted below.
+ */
+
+#include "k5-platform.h"
+#include "k5-regex.h"
+
+#include <regex>
+
+/*
+ * Our implementation of regcomp() which calls into std::regex.  We implement
+ * the standard flags, but not the non-portable extensions present on some
+ * platforms.
+ */
+extern "C" int
+k5_regcomp(regex_t *preg, const char *pattern, int cflags)
+{
+    std::regex *r;
+    std::regex_constants::syntax_option_type flags;
+
+    memset(preg, 0, sizeof(*preg));
+
+    flags = (cflags & REG_EXTENDED) ? std::regex::extended : std::regex::basic;
+    if (cflags & REG_ICASE)
+        flags |= std::regex::icase;
+    if (cflags & REG_NOSUB)
+        flags |= std::regex::nosubs;
+
+    try {
+        r = new std::regex(pattern, flags);
+        preg->regex = r;
+        preg->re_nsub = r->mark_count();
+    } catch (std::regex_error& e) {
+        /* Save the error message in errmsg.  We don't actually use the
+         * error code for anything; return REG_BADPAT for everything. */
+        strlcpy(preg->errmsg, e.what(), sizeof(preg->errmsg));
+        return REG_BADPAT;
+    }
+
+    return 0;
+}
+
+extern "C" int
+k5_regexec(regex_t *preg, const char *string, size_t nmatch,
+           regmatch_t pmatch[], int eflags)
+{
+    size_t i;
+    std::cmatch cm;
+    std::regex_constants::match_flag_type flags;
+    std::regex *r = static_cast<std::regex *>(preg->regex);
+
+    flags = std::regex_constants::match_default;
+    if (eflags & REG_NOTBOL)
+        flags |= std::regex_constants::match_not_bol;
+    if (eflags & REG_NOTEOL)
+        flags |= std::regex_constants::match_not_eol;
+
+    try {
+        if (!std::regex_search(string, cm, *r, flags))
+            return REG_NOMATCH;
+
+        /*
+         * If given, fill in pmatch with the full match string and any
+         * sub-matches.  If we set nosub previously we shouldn't have any
+         * submatches, but should still have the first element which refers to
+         * the whole match string.
+         */
+
+        for (i = 0; i < nmatch; i++) {
+            /*
+             * If we're past the end of the match list (cm.size()) or
+             * this sub-match didn't match (!cm[i].matched()) then
+             * return -1 for those array members.
+             */
+            if (i >= cm.size() || !cm[i].matched) {
+                pmatch[i].rm_so = pmatch[i].rm_eo = -1;
+            } else {
+                pmatch[i].rm_so = cm.position(i);
+                pmatch[i].rm_eo = cm.position(i) + cm.length(i);
+            }
+        }
+    } catch (std::regex_error& e) {
+        /* See above. */
+        strlcpy(preg->errmsg, e.what(), sizeof(preg->errmsg));
+        return REG_BADPAT;
+    }
+
+    return 0;
+}
+
+/*
+ * Report back an error string.  We don't use the errcode for anything, just
+ * the error string stored in regex_t.  If we don't have an error string,
+ * return an "unknown error" message.
+ */
+extern "C" size_t
+k5_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
+{
+    const char *err;
+    size_t errlen;
+
+    err = preg->errmsg;
+    if (*err == '\0')
+        err = "Unknown regular expression error";
+
+    if (errbuf != NULL && errbuf_size > 0)
+        strlcpy(errbuf, err, errbuf_size);
+    return strlen(err);
+}
+
+extern "C" void
+k5_regfree(regex_t *preg)
+{
+    if (preg->regex == NULL)
+        return;
+    delete static_cast<std::regex *>(preg->regex);
+    memset(preg, 0, sizeof(*preg));
+}


More information about the cvs-krb5 mailing list