Compare commits
11 Commits
kadmin-get
...
systemd-su
| Author | SHA1 | Date | |
|---|---|---|---|
|
3a17ecb3e5
|
|||
|
19156b61f1
|
|||
|
63ad2a7931
|
|||
|
|
8ec31e4926 | ||
|
|
0a9f1c3f11 | ||
|
|
4fe0ad8e2c | ||
|
|
c999e5a33a | ||
|
|
ce85c64b84 | ||
|
|
c76a096a28 | ||
|
|
8a22366b55 | ||
|
|
1a7e3c4a5c |
1
.github/workflows/coverage.yml
vendored
1
.github/workflows/coverage.yml
vendored
@@ -36,6 +36,7 @@ jobs:
|
||||
../configure --srcdir=`dirname "$PWD"` --enable-maintainer-mode --enable-developer --with-ldap --prefix=$HOME/inst CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS"
|
||||
make -j4
|
||||
- name: Test
|
||||
timeout-minutes: 40
|
||||
run: |
|
||||
cd build
|
||||
sudo sysctl kernel.core_pattern=core.%p || true
|
||||
|
||||
1
.github/workflows/linux-interop.yml
vendored
1
.github/workflows/linux-interop.yml
vendored
@@ -78,6 +78,7 @@ jobs:
|
||||
../configure --srcdir=`dirname "$PWD"` --enable-maintainer-mode --enable-developer --with-ldap $CONFIGURE_OPTS --prefix=$HOME/inst CFLAGS="${{ matrix.cflags }} -Wno-error=shadow -Wno-error=bad-function-cast -Wno-error=unused-function -Wno-error=unused-result -Wno-error=deprecated-declarations" --with-mitkrb5=$HOME/mitkrb5 --with-older-heimdal=$HOME/oh5l
|
||||
make -j4
|
||||
- name: Test
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
MAKEVARS: ${{ matrix.makevars }}
|
||||
|
||||
22
.github/workflows/linux.yml
vendored
22
.github/workflows/linux.yml
vendored
@@ -90,6 +90,7 @@ jobs:
|
||||
../configure --srcdir=`dirname "$PWD"` --enable-maintainer-mode --enable-developer --with-ldap $CONFIGURE_OPTS --prefix=$HOME/inst CFLAGS="${{ matrix.cflags }} -Wno-error=shadow -Wno-error=bad-function-cast -Wno-error=unused-function -Wno-error=unused-result -Wno-error=deprecated-declarations"
|
||||
make -j4
|
||||
- name: Test
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
MAKEVARS: ${{ matrix.makevars }}
|
||||
@@ -107,6 +108,27 @@ jobs:
|
||||
make DESTDIR=/tmp/h5l install
|
||||
cd /tmp/h5l
|
||||
tar czf $HOME/heimdal-install-linux-${{ matrix.compiler }}.tgz .
|
||||
- name: Interrupted/timed out test diagnostics
|
||||
if: ${{ cancelled() || failure() }}
|
||||
run: |
|
||||
set -x
|
||||
# Show any test-related processes still running
|
||||
ps auxww | grep -E 'kdc|test_context|kinit|kadmin|check-' | grep -v grep || true
|
||||
# Show who's on the test port
|
||||
ss -tlnp sport = :49188 || true
|
||||
# Show the newest .log file (the one likely interrupted)
|
||||
newest=$(find build/tests -name '*.log' -newer build/tests/gss/Makefile -printf '%T@ %p\n' 2>/dev/null | sort -rn | head -1 | cut -d' ' -f2-)
|
||||
if [ -n "$newest" ]; then
|
||||
echo "=== Newest test log: $newest ==="
|
||||
cat "$newest"
|
||||
fi
|
||||
# Also show KDC messages.log if any exist
|
||||
for f in build/tests/gss/messages.log build/tests/kdc/messages.log; do
|
||||
if [ -f "$f" ]; then
|
||||
echo "=== $f ==="
|
||||
tail -100 "$f"
|
||||
fi
|
||||
done
|
||||
- name: Core dump stacks
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
|
||||
23
.github/workflows/osx.yml
vendored
23
.github/workflows/osx.yml
vendored
@@ -93,6 +93,7 @@ jobs:
|
||||
# with:
|
||||
# limit-access-to-actor: true
|
||||
- name: Test
|
||||
timeout-minutes: 25
|
||||
shell: bash
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
@@ -105,6 +106,27 @@ jobs:
|
||||
sudo lsof -nP -i:49188 || true
|
||||
cd build
|
||||
make check
|
||||
- name: Interrupted/timed out test diagnostics
|
||||
if: ${{ cancelled() || failure() }}
|
||||
run: |
|
||||
set -x
|
||||
# Show any test-related processes still running
|
||||
ps auxww | grep -E 'kdc|test_context|kinit|kadmin|check-' | grep -v grep || true
|
||||
# Show who's on the test port
|
||||
sudo lsof -nP -i:49188 || true
|
||||
# Show the newest .log file (the one likely interrupted)
|
||||
newest=$(find build/tests -name '*.log' -newer build/tests/gss/Makefile -print0 2>/dev/null | xargs -0 stat -f '%m %N' 2>/dev/null | sort -rn | head -1 | cut -d' ' -f2-)
|
||||
if [ -n "$newest" ]; then
|
||||
echo "=== Newest test log: $newest ==="
|
||||
cat "$newest"
|
||||
fi
|
||||
# Also show KDC messages.log if any exist
|
||||
for f in build/tests/gss/messages.log build/tests/kdc/messages.log; do
|
||||
if [ -f "$f" ]; then
|
||||
echo "=== $f ==="
|
||||
tail -100 "$f"
|
||||
fi
|
||||
done
|
||||
- name: Install
|
||||
run: |
|
||||
cd build || true
|
||||
@@ -112,6 +134,7 @@ jobs:
|
||||
cd /tmp/h5l
|
||||
tar czf $HOME/heimdal-install-osx.tgz .
|
||||
- name: Test logs
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
find build -depth -name \*.trs|xargs grep -lw FAIL|sed -e 's/trs$/log/' | cpio -o > $HOME/logs-osx.cpio
|
||||
find build -name \*.trs|xargs grep -lw FAIL|sed -e 's/trs$/log/'|xargs cat
|
||||
|
||||
1
.github/workflows/scanbuild.yml
vendored
1
.github/workflows/scanbuild.yml
vendored
@@ -49,6 +49,7 @@ jobs:
|
||||
(cd lib/sqlite && make -j4)
|
||||
scan-build --keep-going make -j4
|
||||
- name: Test
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
MAKEVARS: ${{ matrix.makevars }}
|
||||
|
||||
1
.github/workflows/ubsan.yml
vendored
1
.github/workflows/ubsan.yml
vendored
@@ -90,6 +90,7 @@ jobs:
|
||||
../configure --srcdir=`dirname "$PWD"` --enable-maintainer-mode --enable-developer --enable-dynamic --disable-static --with-ldap $CONFIGURE_OPTS --prefix=$HOME/inst CFLAGS="${{ matrix.cflags }} -Wno-error=shadow -Wno-error=bad-function-cast -Wno-error=unused-function -Wno-error=unused-result -Wno-error=deprecated-declarations" LDFLAGS="${{ matrix.ldflags }}"
|
||||
make -j4
|
||||
- name: Test
|
||||
timeout-minutes: 80
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
MAKEVARS: ${{ matrix.makevars }}
|
||||
|
||||
1
.github/workflows/valgrind.yml
vendored
1
.github/workflows/valgrind.yml
vendored
@@ -46,6 +46,7 @@ jobs:
|
||||
../configure --srcdir=`dirname "$PWD"` --enable-maintainer-mode --enable-developer --with-ldap $CONFIGURE_OPTS --prefix=$HOME/inst CFLAGS="-gdwarf-4 -g -ggdb3 -O0 -Wno-error=shadow -Wno-error=bad-function-cast -Wno-error=unused-function -Wno-error=unused-result -Wno-error=deprecated-declarations"
|
||||
make -j4
|
||||
- name: Test
|
||||
timeout-minutes: 120
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
MAKEVARS: ${{ matrix.makevars }}
|
||||
|
||||
1
.github/workflows/windows.yml
vendored
1
.github/workflows/windows.yml
vendored
@@ -119,6 +119,7 @@ jobs:
|
||||
)
|
||||
|
||||
- name: Test
|
||||
timeout-minutes: 20
|
||||
shell: cmd
|
||||
run: |
|
||||
set "PATH=%PATH%;C:\msys64\usr\bin"
|
||||
|
||||
8
.zed/settings.json
Normal file
8
.zed/settings.json
Normal file
@@ -0,0 +1,8 @@
|
||||
// Folder-specific settings
|
||||
//
|
||||
// For a full list of overridable settings, and general information on folder-specific settings,
|
||||
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
|
||||
{
|
||||
"tab_size": 8,
|
||||
"hard_tabs": false
|
||||
}
|
||||
@@ -360,6 +360,7 @@ AC_BROKEN([ \
|
||||
mergesort \
|
||||
mergesort_r \
|
||||
mkstemp \
|
||||
pread \
|
||||
putenv \
|
||||
rcmd \
|
||||
readv \
|
||||
|
||||
15
configure.ac
15
configure.ac
@@ -230,6 +230,21 @@ AM_CONDITIONAL([HAVE_MICROHTTPD], [test "$with_microhttpd" != "no"])
|
||||
AC_SUBST([MICROHTTPD_CFLAGS])
|
||||
AC_SUBST([MICROHTTPD_LIBS])
|
||||
|
||||
dnl systemd sd_notify support
|
||||
AC_ARG_WITH([systemd],
|
||||
AC_HELP_STRING([--with-systemd], [use systemd sd_notify for readiness @<:@default=check@:>@]),
|
||||
[],
|
||||
[with_systemd=check])
|
||||
if test "$with_systemd" != "no"; then
|
||||
PKG_CHECK_MODULES([SYSTEMD], [libsystemd], [with_systemd=yes],[with_systemd=no])
|
||||
fi
|
||||
if test "$with_systemd" = "yes"; then
|
||||
AC_DEFINE_UNQUOTED([HAVE_SYSTEMD], 1, [Define if libsystemd sd_notify is available])
|
||||
fi
|
||||
AM_CONDITIONAL([HAVE_SYSTEMD], [test "$with_systemd" != "no"])
|
||||
AC_SUBST([SYSTEMD_CFLAGS])
|
||||
AC_SUBST([SYSTEMD_LIBS])
|
||||
|
||||
dnl mitkrb5
|
||||
AC_ARG_WITH([mitkrb5],
|
||||
AC_HELP_STRING([--with-mitkrb5=PATH], [Path to MIT Kerberos for interop testing]),
|
||||
|
||||
27
flake.lock
generated
Normal file
27
flake.lock
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1769740369,
|
||||
"narHash": "sha256-xKPyJoMoXfXpDM5DFDZDsi9PHArf2k5BJjvReYXoFpM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "6308c3b21396534d8aaeac46179c14c439a89b8a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
48
flake.nix
Normal file
48
flake.nix
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
|
||||
outputs = { self, nixpkgs }: let
|
||||
inherit (nixpkgs) lib;
|
||||
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
];
|
||||
|
||||
forAllSystems = f: lib.genAttrs systems (system: let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [
|
||||
self.overlays.${system}.default
|
||||
];
|
||||
};
|
||||
in f system pkgs);
|
||||
in {
|
||||
devShells = forAllSystems (system: pkgs: {
|
||||
default = pkgs.callPackage ./nix/shell.nix { };
|
||||
});
|
||||
|
||||
packages = forAllSystems (system: pkgs: with pkgs; {
|
||||
default = self.packages.${system}.heimdal;
|
||||
|
||||
heimdal = pkgs.callPackage ./nix/heimdal {
|
||||
src = lib.cleanSource ./.;
|
||||
inherit (pkgs.darwin.apple_sdk.frameworks) CoreFoundation Security SystemConfiguration;
|
||||
autoreconfHook = pkgs.buildPackages.autoreconfHook269;
|
||||
};
|
||||
|
||||
nixosTest = pkgs.testers.runNixOSTest (import ./nix/nixosTest.nix { inherit nixpkgs; });
|
||||
});
|
||||
|
||||
overlays = forAllSystems (system: pkgs: {
|
||||
default = final: prev: {
|
||||
heimdal = self.packages.${system}.heimdal;
|
||||
};
|
||||
});
|
||||
|
||||
nixosModules = {
|
||||
default = self.nixosModules.heimdal;
|
||||
heimdal = ./nix/module;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -35,6 +35,9 @@
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYSTEMD
|
||||
#include <systemd/sd-daemon.h>
|
||||
#endif
|
||||
|
||||
extern int daemon_child;
|
||||
|
||||
@@ -304,6 +307,10 @@ start_server(krb5_context contextp, const char *port_str)
|
||||
krb5_errx(contextp, 1, "no sockets to listen to - exiting");
|
||||
|
||||
roken_detach_finish(NULL, daemon_child);
|
||||
#ifdef HAVE_SYSTEMD
|
||||
if (getenv("NOTIFY_SOCKET") != NULL)
|
||||
sd_notify(0, "READY=1");
|
||||
#endif
|
||||
|
||||
wait_for_connection(contextp, socks, num_socks);
|
||||
free(socks);
|
||||
|
||||
@@ -4,7 +4,7 @@ include $(top_srcdir)/Makefile.am.common
|
||||
|
||||
WFLAGS += $(WFLAGS_ENUM_CONV)
|
||||
|
||||
AM_CPPFLAGS += $(INCLUDE_libintl) $(INCLUDE_openssl_crypto) -I$(srcdir)/../lib/krb5
|
||||
AM_CPPFLAGS += $(INCLUDE_libintl) $(INCLUDE_openssl_crypto) -I$(srcdir)/../lib/krb5 $(SYSTEMD_CFLAGS)
|
||||
|
||||
lib_LTLIBRARIES = ipc_csr_authorizer.la \
|
||||
libkdc.la
|
||||
@@ -174,7 +174,7 @@ hpropd_LDADD = \
|
||||
$(LIB_kdb) \
|
||||
$(top_builddir)/lib/asn1/libasn1.la \
|
||||
$(LIB_roken) \
|
||||
$(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB)
|
||||
$(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB) $(SYSTEMD_LIBS)
|
||||
|
||||
if PKINIT
|
||||
LIB_pkinit = $(top_builddir)/lib/hx509/libhx509.la
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
*/
|
||||
|
||||
#include "kdc_locl.h"
|
||||
#ifdef HAVE_SYSTEMD
|
||||
#include <systemd/sd-daemon.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* a tuple describing on what to listen
|
||||
@@ -1208,6 +1211,12 @@ start_kdc(krb5_context context,
|
||||
#endif
|
||||
|
||||
roken_detach_finish(NULL, daemon_child);
|
||||
#ifdef HAVE_SYSTEMD
|
||||
/* If built with systemd support and running under systemd, notify readiness.
|
||||
We check NOTIFY_SOCKET to avoid unnecessary calls when not under systemd. */
|
||||
if (getenv("NOTIFY_SOCKET") != NULL)
|
||||
sd_notify(0, "READY=1");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FORK
|
||||
if (!testing_flag) {
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
*/
|
||||
|
||||
#include "hprop.h"
|
||||
#ifdef HAVE_SYSTEMD
|
||||
#include <systemd/sd-daemon.h>
|
||||
#endif
|
||||
|
||||
static int inetd_flag = -1;
|
||||
static int help_flag;
|
||||
@@ -156,6 +159,11 @@ main(int argc, char **argv)
|
||||
mini_inetd (krb5_getportbyname (context, "hprop", "tcp",
|
||||
HPROP_PORT), &sock);
|
||||
}
|
||||
#ifdef HAVE_SYSTEMD
|
||||
/* Notify systemd that the service is ready when running standalone */
|
||||
if (getenv("NOTIFY_SOCKET") != NULL)
|
||||
sd_notify(0, "READY=1");
|
||||
#endif
|
||||
socket_set_keepalive(sock, 1);
|
||||
sin_len = sizeof(ss);
|
||||
if (getpeername(sock, sa, &sin_len) < 0)
|
||||
|
||||
@@ -744,6 +744,14 @@ doit(krb5_keytab keytab, int port)
|
||||
krb5_errx(context, 1, "No sockets!");
|
||||
|
||||
roken_detach_finish(NULL, daemon_child);
|
||||
#ifdef HAVE_SYSTEMD
|
||||
/* If built with systemd support and running under systemd, notify readiness.
|
||||
We check NOTIFY_SOCKET to avoid unnecessary calls when not under systemd. */
|
||||
if (getenv("NOTIFY_SOCKET") != NULL) {
|
||||
extern int sd_notify(int, const char *);
|
||||
(void) sd_notify(0, "READY=1");
|
||||
}
|
||||
#endif
|
||||
|
||||
while (exit_flag == 0) {
|
||||
krb5_ssize_t retx;
|
||||
|
||||
@@ -53,6 +53,17 @@
|
||||
#define LOG_VERSION_FIRST 1
|
||||
#define LOG_VERSION_UBER 0
|
||||
|
||||
#define LOG_HEADER_SZ ((off_t)(sizeof(uint32_t) * 4))
|
||||
#define LOG_TRAILER_SZ ((off_t)(sizeof(uint32_t) * 2))
|
||||
#define LOG_WRAPPER_SZ ((off_t)(LOG_HEADER_SZ + LOG_TRAILER_SZ))
|
||||
#define LOG_UBER_LEN ((off_t)(sizeof(uint64_t) + sizeof(uint32_t) * 2))
|
||||
#define LOG_UBER_SZ ((off_t)(LOG_WRAPPER_SZ + LOG_UBER_LEN))
|
||||
|
||||
struct kadm5_log_snap {
|
||||
char bytes;
|
||||
char buf[LOG_UBER_SZ + LOG_HEADER_SZ];
|
||||
};
|
||||
|
||||
#include <krb5.h>
|
||||
|
||||
#define KRB5_KDB_DISALLOW_POSTDATED 0x00000001
|
||||
|
||||
@@ -203,12 +203,10 @@ kadm5_c_dup_context(void *vin, void **out)
|
||||
kadm5_client_context *ctx;
|
||||
|
||||
*out = NULL;
|
||||
ctx = malloc(sizeof(*ctx));
|
||||
ctx = calloc(1, sizeof(*ctx));
|
||||
if (ctx == NULL)
|
||||
return krb5_enomem(in->context);
|
||||
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
set_funcs(ctx);
|
||||
ctx->readonly_kadmind_port = in->readonly_kadmind_port;
|
||||
ctx->kadmind_port = in->kadmind_port;
|
||||
@@ -226,8 +224,13 @@ kadm5_c_dup_context(void *vin, void **out)
|
||||
if (in->readonly_admin_server &&
|
||||
(ctx->readonly_admin_server = strdup(in->readonly_admin_server)) == NULL)
|
||||
ret = krb5_enomem(context);
|
||||
if (in->client_name != NULL &&
|
||||
(ctx->client_name = strdup(in->client_name)) == NULL)
|
||||
ret = krb5_enomem(context);
|
||||
if (in->keytab && (ctx->keytab = strdup(in->keytab)) == NULL)
|
||||
ret = krb5_enomem(context);
|
||||
ctx->prompter = in->prompter;
|
||||
|
||||
if (in->ccache) {
|
||||
char *fullname = NULL;
|
||||
|
||||
@@ -455,9 +458,19 @@ _kadm5_c_get_cred_cache(krb5_context context,
|
||||
}
|
||||
|
||||
if(ccache != NULL) {
|
||||
id = ccache;
|
||||
char *fullname = NULL;
|
||||
|
||||
ret = krb5_cc_get_full_name(context, ccache, &fullname);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = krb5_cc_resolve(context, fullname, &id);
|
||||
free(fullname);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = krb5_cc_get_principal(context, id, &client);
|
||||
if(ret)
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
/* get principal from default cache, ok if this doesn't work */
|
||||
@@ -515,7 +528,7 @@ _kadm5_c_get_cred_cache(krb5_context context,
|
||||
return -1;
|
||||
}
|
||||
/* get creds via AS request */
|
||||
if(id && (id != ccache))
|
||||
if (id)
|
||||
krb5_cc_close(context, id);
|
||||
if (client != default_client)
|
||||
krb5_free_principal(context, default_client);
|
||||
@@ -708,7 +721,8 @@ kadm5_c_init_with_context(krb5_context context,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (password != NULL && *password != '\0') {
|
||||
if ((password != NULL && *password != '\0') ||
|
||||
(client_name && !keytab)) {
|
||||
ret = _kadm5_c_get_cred_cache(context,
|
||||
client_name,
|
||||
service_name,
|
||||
|
||||
@@ -132,7 +132,6 @@ struct slave {
|
||||
krb5_auth_context ac;
|
||||
uint32_t version;
|
||||
uint32_t version_tstamp;
|
||||
uint32_t version_ack;
|
||||
time_t seen;
|
||||
unsigned long flags;
|
||||
#define SLAVE_F_DEAD 0x1
|
||||
@@ -376,7 +375,6 @@ add_slave (krb5_context context, krb5_keytab keytab, slave **root,
|
||||
krb5_warnx (context, "connection from %s", s->name);
|
||||
|
||||
s->version = 0;
|
||||
s->version_ack = 0;
|
||||
s->flags = 0;
|
||||
slave_seen(s);
|
||||
s->next = *root;
|
||||
@@ -862,8 +860,11 @@ diffready(krb5_context context, slave *s)
|
||||
* Don't send any diffs until slave has sent an I_HAVE telling us the
|
||||
* initial version number!
|
||||
*/
|
||||
if ((s->flags & SLAVE_F_READY) == 0)
|
||||
if ((s->flags & SLAVE_F_READY) == 0) {
|
||||
if (verbose)
|
||||
krb5_warnx(context, "not sending diffs to not-ready slave %s", s->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (s->flags & SLAVE_F_DEAD) {
|
||||
if (verbose)
|
||||
@@ -872,8 +873,11 @@ diffready(krb5_context context, slave *s)
|
||||
}
|
||||
|
||||
/* Write any remainder of previous write, if we can. */
|
||||
if (send_tail(context, s) != 0)
|
||||
if (send_tail(context, s) != 0 && have_tail(s)) {
|
||||
if (verbose)
|
||||
krb5_warnx(context, "not sending diffs to busy slave %s", s->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -881,10 +885,6 @@ diffready(krb5_context context, slave *s)
|
||||
static int
|
||||
nodiffs(krb5_context context, slave *s, uint32_t current_version)
|
||||
{
|
||||
krb5_storage *sp;
|
||||
krb5_data data;
|
||||
int ret;
|
||||
|
||||
if (s->version < current_version)
|
||||
return 0;
|
||||
|
||||
@@ -897,6 +897,20 @@ nodiffs(krb5_context context, slave *s, uint32_t current_version)
|
||||
if (verbose)
|
||||
krb5_warnx(context, "slave %s version %ld already sent", s->name,
|
||||
(long)s->version);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
send_you_have_last_version(krb5_context context, slave *s)
|
||||
{
|
||||
krb5_storage *sp;
|
||||
krb5_data data;
|
||||
int ret;
|
||||
|
||||
if (verbose)
|
||||
krb5_warnx(context, "slave %s version %ld is latest", s->name,
|
||||
(long)s->version);
|
||||
|
||||
sp = krb5_storage_emem();
|
||||
if (sp == NULL)
|
||||
krb5_errx(context, IPROPD_RESTART, "krb5_storage_from_mem");
|
||||
@@ -909,19 +923,17 @@ nodiffs(krb5_context context, slave *s, uint32_t current_version)
|
||||
krb5_storage_free(sp);
|
||||
if (ret == 0) {
|
||||
ret = mk_priv_tail(context, s, &data);
|
||||
if (ret)
|
||||
krb5_err(context, IPROPD_RESTART, ret,
|
||||
"sending you have last version");
|
||||
krb5_data_free(&data);
|
||||
}
|
||||
if (ret == 0)
|
||||
send_tail(context, s);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock the log and return initial version and timestamp
|
||||
*/
|
||||
static int
|
||||
get_first(kadm5_server_context *server_context, int log_fd,
|
||||
get_first(kadm5_server_context *server_context,
|
||||
uint32_t *initial_verp, uint32_t *initial_timep)
|
||||
{
|
||||
krb5_context context = server_context->context;
|
||||
@@ -931,19 +943,14 @@ get_first(kadm5_server_context *server_context, int log_fd,
|
||||
* We don't want to perform tight retry loops on log access errors, so on
|
||||
* error mark the slave dead. The slave reconnect after a delay...
|
||||
*/
|
||||
if (flock(log_fd, LOCK_SH) == -1) {
|
||||
krb5_warn(context, errno, "could not obtain shared lock on log file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_FIRST,
|
||||
ret = kadm5_log_get_version_fd(server_context, -1, LOG_VERSION_FIRST,
|
||||
initial_verp, initial_timep);
|
||||
if (ret == HEIM_ERR_EOF)
|
||||
ret = kadm5_log_get_version_fd(server_context, log_fd,
|
||||
if (ret == HEIM_ERR_EOF || ret == KADM5_LOG_EMPTY)
|
||||
ret = kadm5_log_get_version_fd(server_context, -1,
|
||||
LOG_VERSION_UBER, initial_verp,
|
||||
initial_timep);
|
||||
if (ret != 0) {
|
||||
flock(log_fd, LOCK_UN);
|
||||
krb5_warn(context, ret, "could not read initial log entry");
|
||||
return -1;
|
||||
}
|
||||
@@ -954,17 +961,20 @@ get_first(kadm5_server_context *server_context, int log_fd,
|
||||
/*-
|
||||
* Find the left end of the diffs in the log we want to send.
|
||||
*
|
||||
* - On success, return a positive offset to the first new entry, retaining
|
||||
* a read lock on the log file.
|
||||
* - On error, return a negative offset, with the lock released.
|
||||
* - On success, return a positive offset to the first new entry.
|
||||
* - On error, return a negative offset.
|
||||
* - If we simply find no successor entry in the log, return zero
|
||||
* with the lock released, which indicates that fallback to send_complete()
|
||||
* is needed.
|
||||
*
|
||||
* The caller must use kadm5_log_snapshot()/kadm5_log_snapshot_verify() to
|
||||
* ensure that the data read here is consistent because there have been only
|
||||
* append writes and/or there have been no log rotations.
|
||||
*/
|
||||
static off_t
|
||||
get_left(kadm5_server_context *server_context, slave *s, krb5_storage *sp,
|
||||
int log_fd, uint32_t current_version,
|
||||
uint32_t *initial_verp, uint32_t *initial_timep)
|
||||
uint32_t current_version, uint32_t *initial_verp,
|
||||
uint32_t *initial_timep)
|
||||
{
|
||||
krb5_context context = server_context->context;
|
||||
off_t pos;
|
||||
@@ -974,17 +984,13 @@ get_left(kadm5_server_context *server_context, slave *s, krb5_storage *sp,
|
||||
for (;;) {
|
||||
uint32_t ver = s->version;
|
||||
|
||||
/* This acquires a read lock on success */
|
||||
ret = get_first(server_context, log_fd,
|
||||
initial_verp, initial_timep);
|
||||
ret = get_first(server_context, initial_verp, initial_timep);
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
|
||||
/* When the slave version is out of range, send the whole database. */
|
||||
if (ver == 0 || ver < *initial_verp || ver > current_version) {
|
||||
flock(log_fd, LOCK_UN);
|
||||
if (ver == 0 || ver + 1 < *initial_verp || ver > current_version)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Avoid seeking past the last committed record */
|
||||
if (kadm5_log_goto_end(server_context, sp) != 0 ||
|
||||
@@ -1016,13 +1022,12 @@ get_left(kadm5_server_context *server_context, slave *s, krb5_storage *sp,
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* Drop the lock and try to find the left entry by seeking backward
|
||||
* from the end of the end of the log. If we succeed, re-acquire the
|
||||
* lock, update "next_diff", and retry the fast-path.
|
||||
* Slow path: seek backwards, entry by entry, from the end.
|
||||
*
|
||||
* Try to find the left entry by seeking backward from the end of the
|
||||
* end of the log. If we succeed, update "next_diff", and retry the
|
||||
* fast-path.
|
||||
*/
|
||||
flock(log_fd, LOCK_UN);
|
||||
|
||||
/* Slow path: seek backwards, entry by entry, from the end */
|
||||
for (;;) {
|
||||
enum kadm_ops op;
|
||||
uint32_t len;
|
||||
@@ -1038,7 +1043,7 @@ get_left(kadm5_server_context *server_context, slave *s, krb5_storage *sp,
|
||||
|
||||
/*
|
||||
* We don't expect to reach the slave's version, unless the log
|
||||
* has been modified after we released the lock.
|
||||
* has been rotated.
|
||||
*/
|
||||
if (ver == s->version) {
|
||||
krb5_warnx(context, "iprop log truncated while sending diffs "
|
||||
@@ -1060,33 +1065,28 @@ get_left(kadm5_server_context *server_context, slave *s, krb5_storage *sp,
|
||||
|
||||
/*
|
||||
* If we loop then we're hoping to hit the fast path so we can return a
|
||||
* non-zero, positive left offset with the lock held.
|
||||
* non-zero, positive left offset.
|
||||
*
|
||||
* We just updated the fast path pre-conditions, so unless a log
|
||||
* truncation event happens between the point where we dropped the lock
|
||||
* and the point where we rearcuire it above, we will hit the fast
|
||||
* path.
|
||||
* truncation event happens again, we will hit the fast path.
|
||||
*/
|
||||
}
|
||||
|
||||
err:
|
||||
flock(log_fd, LOCK_UN);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static off_t
|
||||
get_right(krb5_context context, int log_fd, krb5_storage *sp,
|
||||
int lastver, slave *s, off_t left, uint32_t *verp)
|
||||
get_right(krb5_context context, krb5_storage *sp, int lastver, slave *s,
|
||||
off_t left, uint32_t *verp)
|
||||
{
|
||||
int ret = 0;
|
||||
int i = 0;
|
||||
uint32_t ver = s->version;
|
||||
off_t right = krb5_storage_seek(sp, left, SEEK_SET);
|
||||
|
||||
if (right <= 0) {
|
||||
flock(log_fd, LOCK_UN);
|
||||
if (right <= 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* The "lastver" bound should preclude us reaching EOF */
|
||||
for (; ret == 0 && i < SEND_DIFFS_MAX_RECORDS && ver < lastver; ++i) {
|
||||
@@ -1101,18 +1101,23 @@ get_right(krb5_context context, int log_fd, krb5_storage *sp,
|
||||
right = krb5_storage_seek(sp, 0, SEEK_CUR);
|
||||
else
|
||||
right = -1;
|
||||
if (right <= 0) {
|
||||
flock(log_fd, LOCK_UN);
|
||||
if (right <= 0)
|
||||
return -1;
|
||||
}
|
||||
*verp = ver;
|
||||
return right;
|
||||
}
|
||||
|
||||
static void
|
||||
send_diffs(kadm5_server_context *server_context, slave *s, int log_fd,
|
||||
/*
|
||||
* This function _must_ send something to the client unless it's dead, and if
|
||||
* dead it must mark it so (and so close the connection). It is an error to
|
||||
* return from this function without having done so _unless_ the client is
|
||||
* either dead or not ready.
|
||||
*/
|
||||
static kadm5_ret_t
|
||||
send_diffs(kadm5_server_context *server_context, slave *s,
|
||||
const char *database, uint32_t current_version)
|
||||
{
|
||||
struct kadm5_log_snap snap;
|
||||
krb5_context context = server_context->context;
|
||||
krb5_storage *sp;
|
||||
uint32_t initial_version;
|
||||
@@ -1122,84 +1127,122 @@ send_diffs(kadm5_server_context *server_context, slave *s, int log_fd,
|
||||
off_t right = 0;
|
||||
krb5_ssize_t bytes;
|
||||
krb5_data data;
|
||||
int ret = 0;
|
||||
kadm5_ret_t ret = 0;
|
||||
|
||||
if (!diffready(context, s) || nodiffs(context, s, current_version))
|
||||
return;
|
||||
/*
|
||||
* diffready() can send part of an extant buffer, and does nothing if the
|
||||
* client is either dead or not ready.
|
||||
*/
|
||||
if (!diffready(context, s))
|
||||
return 0;
|
||||
|
||||
/* From this point onwards we cannot return without having sent anything */
|
||||
|
||||
if (nodiffs(context, s, current_version)) {
|
||||
if (verbose)
|
||||
krb5_warnx(context, "not sending diffs to up-to-date slave %s (no diffs)", s->name);
|
||||
|
||||
send_you_have_last_version(context, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
krb5_warnx(context, "sending diffs to live-seeming slave %s", s->name);
|
||||
|
||||
sp = krb5_storage_from_fd(log_fd);
|
||||
ret = kadm5_log_snapshot(server_context, &snap);
|
||||
if (ret) {
|
||||
/*
|
||||
* Here ret would be KADM5_LOG_CORRUPT. What can we do?
|
||||
*/
|
||||
send_you_have_last_version(context, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sp = krb5_storage_from_fd(server_context->log_context.log_fd);
|
||||
if (sp == NULL)
|
||||
krb5_err(context, IPROPD_RESTART_SLOW, ENOMEM,
|
||||
"send_diffs: out of memory");
|
||||
|
||||
left = get_left(server_context, s, sp, log_fd, current_version,
|
||||
left = get_left(server_context, s, sp, current_version,
|
||||
&initial_version, &initial_tstamp);
|
||||
if (left < 0) {
|
||||
/* Some sort of error; fallback on full prop */
|
||||
krb5_storage_free(sp);
|
||||
slave_dead(context, s);
|
||||
return;
|
||||
if (verbose)
|
||||
krb5_warnx(context, "sending full prop to slave %s "
|
||||
"(could not find offset for incremental)", s->name);
|
||||
send_complete(context, s, database, current_version,
|
||||
initial_version, initial_tstamp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (left == 0) {
|
||||
/* Slave's version is not in the log, fall back on send_complete() */
|
||||
krb5_storage_free(sp);
|
||||
if (verbose)
|
||||
krb5_warnx(context, "sending full prop to slave %s "
|
||||
"(their version is too old)", s->name);
|
||||
send_complete(context, s, database, current_version,
|
||||
initial_version, initial_tstamp);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We still hold the read lock, if right > 0 */
|
||||
right = get_right(server_context->context, log_fd, sp, current_version,
|
||||
right = get_right(server_context->context, sp, current_version,
|
||||
s, left, &ver);
|
||||
if (right == left) {
|
||||
flock(log_fd, LOCK_UN);
|
||||
/* Shouldn't happen */
|
||||
krb5_storage_free(sp);
|
||||
return;
|
||||
if (verbose)
|
||||
krb5_warnx(context, "not sending diffs to up-to-date slave %s (weird)", s->name);
|
||||
send_you_have_last_version(context, s);
|
||||
return 0;
|
||||
}
|
||||
if (right < left) {
|
||||
assert(right < 0);
|
||||
krb5_storage_free(sp);
|
||||
slave_dead(context, s);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (krb5_storage_seek(sp, left, SEEK_SET) != left) {
|
||||
ret = errno ? errno : EIO;
|
||||
flock(log_fd, LOCK_UN);
|
||||
krb5_warn(context, ret, "send_diffs: krb5_storage_seek");
|
||||
krb5_warn(context, errno ? errno : EIO, "send_diffs: krb5_storage_seek");
|
||||
krb5_storage_free(sp);
|
||||
slave_dead(context, s);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = krb5_data_alloc(&data, right - left + 4);
|
||||
if (ret) {
|
||||
flock(log_fd, LOCK_UN);
|
||||
krb5_warn(context, ret, "send_diffs: krb5_data_alloc");
|
||||
krb5_storage_free(sp);
|
||||
slave_dead(context, s);
|
||||
return;
|
||||
}
|
||||
if (ret)
|
||||
krb5_err(context, IPROPD_RESTART_SLOW, ENOMEM, "out of memory");
|
||||
|
||||
bytes = krb5_storage_read(sp, (char *)data.data + 4, data.length - 4);
|
||||
flock(log_fd, LOCK_UN);
|
||||
krb5_storage_free(sp);
|
||||
if (bytes != data.length - 4)
|
||||
krb5_errx(context, IPROPD_RESTART, "locked log truncated???");
|
||||
if (bytes != data.length - 4) {
|
||||
krb5_warnx(context, "send_diffs: log rotated");
|
||||
return KADM5_LOG_SNAPSHOT_INVALID;
|
||||
}
|
||||
|
||||
ret = kadm5_log_snapshot_verify(server_context, &snap);
|
||||
if (ret) {
|
||||
krb5_data_free(&data);
|
||||
if (ret == KADM5_LOG_SNAPSHOT_INVALID) {
|
||||
krb5_warnx(context, "send_diffs: log rotated");
|
||||
return ret;
|
||||
}
|
||||
krb5_err(context, IPROPD_RESTART_SLOW, ENOMEM, "out of memory");
|
||||
}
|
||||
|
||||
sp = krb5_storage_from_data(&data);
|
||||
if (sp == NULL) {
|
||||
if (sp == NULL)
|
||||
krb5_err(context, IPROPD_RESTART_SLOW, ENOMEM, "out of memory");
|
||||
return;
|
||||
}
|
||||
ret = krb5_store_uint32(sp, FOR_YOU);
|
||||
krb5_storage_free(sp);
|
||||
|
||||
if (ret == 0)
|
||||
if (ret == 0) {
|
||||
ret = mk_priv_tail(context, s, &data);
|
||||
if (ret == ENOMEM)
|
||||
krb5_err(context, IPROPD_RESTART_SLOW, ENOMEM, "out of memory");
|
||||
}
|
||||
krb5_data_free(&data);
|
||||
if (ret == 0) {
|
||||
/* Save the fast-path continuation */
|
||||
@@ -1221,10 +1264,10 @@ send_diffs(kadm5_server_context *server_context, slave *s, int log_fd,
|
||||
krb5_warn(context, ret, "send_diffs: making or sending "
|
||||
"KRB-PRIV message");
|
||||
slave_dead(context, s);
|
||||
return;
|
||||
return ret;
|
||||
}
|
||||
slave_seen(s);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sensible bound on slave message size */
|
||||
@@ -1299,7 +1342,7 @@ read_msg(krb5_context context, slave *s, krb5_data *out)
|
||||
}
|
||||
|
||||
static int
|
||||
process_msg(kadm5_server_context *server_context, slave *s, int log_fd,
|
||||
process_msg(kadm5_server_context *server_context, slave *s,
|
||||
const char *database, uint32_t current_version)
|
||||
{
|
||||
krb5_context context = server_context->context;
|
||||
@@ -1307,6 +1350,7 @@ process_msg(kadm5_server_context *server_context, slave *s, int log_fd,
|
||||
krb5_data out;
|
||||
krb5_storage *sp;
|
||||
uint32_t tmp;
|
||||
int tries = 3;
|
||||
|
||||
ret = read_msg(context, s, &out);
|
||||
if (ret) {
|
||||
@@ -1372,9 +1416,9 @@ process_msg(kadm5_server_context *server_context, slave *s, int log_fd,
|
||||
krb5_warnx(context, "slave %s ready for updates from version %u",
|
||||
s->name, tmp);
|
||||
}
|
||||
if ((s->version_ack = tmp) < s->version)
|
||||
break;
|
||||
send_diffs(server_context, s, log_fd, database, current_version);
|
||||
do {
|
||||
ret = send_diffs(server_context, s, database, current_version);
|
||||
} while (ret == KADM5_LOG_SNAPSHOT_INVALID && tries--);
|
||||
break;
|
||||
case I_AM_HERE :
|
||||
if (verbose)
|
||||
@@ -1490,7 +1534,7 @@ write_stats(krb5_context context, slave *slaves, uint32_t current_version)
|
||||
} else
|
||||
rtbl_add_column_entry(tbl, SLAVE_ADDRESS, "<unknown>");
|
||||
|
||||
snprintf(str, sizeof(str), "%u", (unsigned)slaves->version_ack);
|
||||
snprintf(str, sizeof(str), "%u", (unsigned)slaves->version);
|
||||
rtbl_add_column_entry(tbl, SLAVE_VERSION, str);
|
||||
|
||||
if (slaves->flags & SLAVE_F_DEAD)
|
||||
@@ -1571,7 +1615,6 @@ main(int argc, char **argv)
|
||||
kadm5_server_context *server_context;
|
||||
kadm5_config_params conf;
|
||||
krb5_socket_t signal_fd, listen_fd;
|
||||
int log_fd;
|
||||
slave *slaves = NULL;
|
||||
uint32_t current_version = 0, old_version = 0;
|
||||
krb5_keytab keytab;
|
||||
@@ -1675,21 +1718,16 @@ main(int argc, char **argv)
|
||||
|
||||
server_context = (kadm5_server_context *)kadm_handle;
|
||||
|
||||
log_fd = open (server_context->log_context.log_file, O_RDONLY, 0);
|
||||
if (log_fd < 0)
|
||||
krb5_err (context, 1, errno, "open %s",
|
||||
server_context->log_context.log_file);
|
||||
ret = kadm5_log_init_nolock(server_context);
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "Could not open iprop log file");
|
||||
|
||||
if (fstat(log_fd, &st) == -1)
|
||||
if (fstat(server_context->log_context.log_fd, &st) == -1)
|
||||
krb5_err(context, 1, errno, "stat %s",
|
||||
server_context->log_context.log_file);
|
||||
|
||||
if (flock(log_fd, LOCK_SH) == -1)
|
||||
krb5_err(context, 1, errno, "shared flock %s",
|
||||
server_context->log_context.log_file);
|
||||
kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST,
|
||||
kadm5_log_get_version_fd(server_context, -1, LOG_VERSION_LAST,
|
||||
¤t_version, NULL);
|
||||
flock(log_fd, LOCK_UN);
|
||||
|
||||
signal_fd = make_signal_socket (context);
|
||||
listen_fd = make_listen_socket (context, port_str);
|
||||
@@ -1706,7 +1744,7 @@ main(int argc, char **argv)
|
||||
int max_fd = 0;
|
||||
struct timeval to = {30, 0};
|
||||
uint32_t vers;
|
||||
struct stat st2;;
|
||||
struct stat st2;
|
||||
|
||||
#ifndef NO_LIMIT_FD_SETSIZE
|
||||
if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE ||
|
||||
@@ -1748,36 +1786,24 @@ main(int argc, char **argv)
|
||||
}
|
||||
|
||||
if (st2.st_dev != st.st_dev || st2.st_ino != st.st_ino) {
|
||||
(void) close(log_fd);
|
||||
kadm5_log_end(server_context);
|
||||
|
||||
log_fd = open(server_context->log_context.log_file, O_RDONLY, 0);
|
||||
if (log_fd < 0)
|
||||
krb5_err(context, IPROPD_RESTART_SLOW, errno, "open %s",
|
||||
server_context->log_context.log_file);
|
||||
ret = kadm5_log_init_nolock(server_context);
|
||||
if (ret)
|
||||
krb5_err(context, IPROPD_RESTART_SLOW, ret,
|
||||
"Could not re-open iprop log file");
|
||||
|
||||
if (fstat(log_fd, &st) == -1)
|
||||
if (fstat(server_context->log_context.log_fd, &st) == -1)
|
||||
krb5_err(context, IPROPD_RESTART_SLOW, errno, "stat %s",
|
||||
server_context->log_context.log_file);
|
||||
|
||||
if (flock(log_fd, LOCK_SH) == -1)
|
||||
krb5_err(context, IPROPD_RESTART, errno, "shared flock %s",
|
||||
server_context->log_context.log_file);
|
||||
kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST,
|
||||
kadm5_log_get_version_fd(server_context, -1, LOG_VERSION_LAST,
|
||||
¤t_version, NULL);
|
||||
flock(log_fd, LOCK_UN);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
/* Recover from failed transactions */
|
||||
if (kadm5_log_init_nb(server_context) == 0)
|
||||
kadm5_log_end(server_context);
|
||||
|
||||
if (flock(log_fd, LOCK_SH) == -1)
|
||||
krb5_err(context, IPROPD_RESTART, errno,
|
||||
"could not lock log file");
|
||||
kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST,
|
||||
kadm5_log_get_version_fd(server_context, -1, LOG_VERSION_LAST,
|
||||
¤t_version, NULL);
|
||||
flock(log_fd, LOCK_UN);
|
||||
|
||||
if (current_version > old_version) {
|
||||
if (verbose)
|
||||
@@ -1786,10 +1812,14 @@ main(int argc, char **argv)
|
||||
(unsigned long)old_version,
|
||||
(unsigned long)current_version);
|
||||
for (p = slaves; p != NULL; p = p->next) {
|
||||
int tries = 3;
|
||||
|
||||
if (p->flags & SLAVE_F_DEAD)
|
||||
continue;
|
||||
send_diffs(server_context, p, log_fd, database,
|
||||
do {
|
||||
ret = send_diffs(server_context, p, database,
|
||||
current_version);
|
||||
} while (ret == KADM5_LOG_SNAPSHOT_INVALID && tries--);
|
||||
}
|
||||
old_version = current_version;
|
||||
}
|
||||
@@ -1816,12 +1846,8 @@ main(int argc, char **argv)
|
||||
--ret;
|
||||
assert(ret >= 0);
|
||||
old_version = current_version;
|
||||
if (flock(log_fd, LOCK_SH) == -1)
|
||||
krb5_err(context, IPROPD_RESTART, errno, "shared flock %s",
|
||||
server_context->log_context.log_file);
|
||||
kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST,
|
||||
kadm5_log_get_version_fd(server_context, -1, LOG_VERSION_LAST,
|
||||
¤t_version, NULL);
|
||||
flock(log_fd, LOCK_UN);
|
||||
if (current_version != old_version) {
|
||||
/*
|
||||
* If current_version < old_version then the log got
|
||||
@@ -1841,10 +1867,14 @@ main(int argc, char **argv)
|
||||
(unsigned long)old_version,
|
||||
(unsigned long)current_version);
|
||||
for (p = slaves; p != NULL; p = p->next) {
|
||||
int tries = 3;
|
||||
|
||||
if (p->flags & SLAVE_F_DEAD)
|
||||
continue;
|
||||
send_diffs(server_context, p, log_fd, database,
|
||||
do {
|
||||
ret = send_diffs(server_context, p, database,
|
||||
current_version);
|
||||
} while (ret == EAGAIN && tries--);
|
||||
}
|
||||
} else {
|
||||
if (verbose)
|
||||
@@ -1859,8 +1889,12 @@ main(int argc, char **argv)
|
||||
FD_ISSET(p->fd, &writeset) &&
|
||||
((have_tail(p) && send_tail(context, p) == 0) ||
|
||||
(!have_tail(p) && more_diffs(p)))) {
|
||||
send_diffs(server_context, p, log_fd, database,
|
||||
int tries = 3;
|
||||
|
||||
do {
|
||||
ret = send_diffs(server_context, p, database,
|
||||
current_version);
|
||||
} while (ret == KADM5_LOG_SNAPSHOT_INVALID && tries--);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1870,10 +1904,12 @@ main(int argc, char **argv)
|
||||
if (ret && FD_ISSET(p->fd, &readset)) {
|
||||
--ret;
|
||||
assert(ret >= 0);
|
||||
ret = process_msg(server_context, p, log_fd, database,
|
||||
ret = process_msg(server_context, p, database,
|
||||
current_version);
|
||||
if (ret && ret != EWOULDBLOCK)
|
||||
if (ret && ret != EWOULDBLOCK) {
|
||||
krb5_warn(context, ret, "process_msg()");
|
||||
slave_dead(context, p);
|
||||
}
|
||||
} else if (slave_gone_p (p))
|
||||
slave_dead(context, p);
|
||||
else if (slave_missing_p (p))
|
||||
|
||||
@@ -355,11 +355,6 @@ receive_loop(krb5_context context,
|
||||
if (verbose)
|
||||
krb5_warnx(context, "receiving diffs");
|
||||
|
||||
ret = kadm5_log_exclusivelock(server_context);
|
||||
if (ret)
|
||||
krb5_err(context, IPROPD_RESTART, ret,
|
||||
"Failed to lock iprop log for writes");
|
||||
|
||||
/*
|
||||
* Seek to the first entry in the message from the master that is
|
||||
* past the current version of the local database.
|
||||
@@ -477,9 +472,6 @@ receive(krb5_context context,
|
||||
if (ret)
|
||||
krb5_err(context, IPROPD_RESTART_SLOW, ret, "db->close");
|
||||
|
||||
(void) kadm5_log_sharedlock(server_context);
|
||||
if (verbose)
|
||||
krb5_warnx(context, "downgraded iprop log lock to shared");
|
||||
kadm5_log_signal_master(server_context);
|
||||
if (verbose)
|
||||
krb5_warnx(context, "signaled master for hierarchical iprop");
|
||||
@@ -531,9 +523,6 @@ reinit_log(krb5_context context,
|
||||
ret = kadm5_log_reinit(server_context, vno);
|
||||
if (ret)
|
||||
krb5_err(context, IPROPD_RESTART_SLOW, ret, "kadm5_log_reinit");
|
||||
(void) kadm5_log_sharedlock(server_context);
|
||||
if (verbose)
|
||||
krb5_warnx(context, "downgraded iprop log lock to shared");
|
||||
}
|
||||
|
||||
|
||||
@@ -553,10 +542,6 @@ receive_everything(krb5_context context, int fd,
|
||||
|
||||
krb5_warnx(context, "receive complete database");
|
||||
|
||||
ret = kadm5_log_exclusivelock(server_context);
|
||||
if (ret)
|
||||
krb5_err(context, IPROPD_RESTART, ret,
|
||||
"Failed to lock iprop log for writes");
|
||||
if (server_context->db->hdb_method_name) {
|
||||
ret = asprintf(&dbname, "%.*s:%s-NEW",
|
||||
(int) strlen(server_context->db->hdb_method_name) - 1,
|
||||
@@ -900,9 +885,6 @@ main(int argc, char **argv)
|
||||
ret = kadm5_log_init(server_context);
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "kadm5_log_init");
|
||||
(void) kadm5_log_sharedlock(server_context);
|
||||
if (verbose)
|
||||
krb5_warnx(context, "downgraded iprop log lock to shared");
|
||||
|
||||
ret = server_context->db->hdb_close(context, server_context->db);
|
||||
if (ret)
|
||||
@@ -1091,9 +1073,6 @@ main(int argc, char **argv)
|
||||
krb5_err(context, IPROPD_RESTART, ret, "kadm5_log_init while "
|
||||
"handling a message from the master");
|
||||
}
|
||||
(void) kadm5_log_sharedlock(server_context);
|
||||
if (verbose)
|
||||
krb5_warnx(context, "downgraded iprop log lock to shared");
|
||||
|
||||
ret = server_context->db->hdb_close (context, server_context->db);
|
||||
if (ret)
|
||||
@@ -1125,7 +1104,6 @@ main(int argc, char **argv)
|
||||
krb5_warnx(context, "master sent us a full dump");
|
||||
ret = receive_everything(context, master_fd, server_context,
|
||||
auth_context);
|
||||
(void) kadm5_log_sharedlock(server_context);
|
||||
if (ret == 0) {
|
||||
ret = ihave(context, auth_context, master_fd,
|
||||
server_context->log_context.version);
|
||||
@@ -1134,8 +1112,6 @@ main(int argc, char **argv)
|
||||
connected = FALSE;
|
||||
else
|
||||
is_up_to_date(context, status_file, server_context);
|
||||
if (verbose)
|
||||
krb5_warnx(context, "downgraded iprop log lock to shared");
|
||||
kadm5_log_signal_master(server_context);
|
||||
if (verbose)
|
||||
krb5_warnx(context, "signaled master for hierarchical iprop");
|
||||
@@ -1152,6 +1128,7 @@ main(int argc, char **argv)
|
||||
*
|
||||
* So we don't ihave() here.
|
||||
*/
|
||||
is_up_to_date(context, status_file, server_context);
|
||||
send_im_here(context, master_fd, auth_context);
|
||||
break;
|
||||
case YOU_HAVE_LAST_VERSION:
|
||||
@@ -1175,7 +1152,7 @@ main(int argc, char **argv)
|
||||
slave_status(context, status_file, "disconnected from master");
|
||||
retry:
|
||||
if (connected == FALSE)
|
||||
krb5_warnx (context, "disconnected for server");
|
||||
krb5_warnx (context, "disconnected from server");
|
||||
|
||||
if (exit_flag)
|
||||
krb5_warnx (context, "got an exit signal");
|
||||
|
||||
@@ -73,6 +73,8 @@ error_code OLD_SERVER_HOOK_VERSION, "KADM5 server hook is too old for this versi
|
||||
error_code NEW_SERVER_HOOK_VERSION, "KADM5 server hook is too new for this version of Heimdal"
|
||||
error_code READ_ONLY, "Database is read-only; try primary server"
|
||||
error_code PASS_Q_GENERIC, "Unspecified password quality failure"
|
||||
error_code LOG_SNAPSHOT_INVALID, "Incremental propagation log rotated; restart operation"
|
||||
error_code LOG_EMPTY, "Incremental propagation log is empty"
|
||||
|
||||
|
||||
# MIT has:
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
#include <hdb.h>
|
||||
#include <der.h>
|
||||
#include <parse_units.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
#endif /* __KADM5_LOCL_H__ */
|
||||
|
||||
@@ -85,6 +85,8 @@ EXPORTS
|
||||
kadm5_log_init_sharedlock
|
||||
kadm5_log_next
|
||||
kadm5_log_nop
|
||||
kadm5_log_snapshot
|
||||
kadm5_log_snapshot_verify
|
||||
kadm5_log_truncate
|
||||
kadm5_log_modify
|
||||
_kadm5_acl_check_permission
|
||||
|
||||
188
lib/kadm5/log.c
188
lib/kadm5/log.c
@@ -174,12 +174,6 @@ RCSID("$Id$");
|
||||
* will deadlock with ipropd-slave -- don't do that.
|
||||
*/
|
||||
|
||||
#define LOG_HEADER_SZ ((off_t)(sizeof(uint32_t) * 4))
|
||||
#define LOG_TRAILER_SZ ((off_t)(sizeof(uint32_t) * 2))
|
||||
#define LOG_WRAPPER_SZ ((off_t)(LOG_HEADER_SZ + LOG_TRAILER_SZ))
|
||||
#define LOG_UBER_LEN ((off_t)(sizeof(uint64_t) + sizeof(uint32_t) * 2))
|
||||
#define LOG_UBER_SZ ((off_t)(LOG_WRAPPER_SZ + LOG_UBER_LEN))
|
||||
|
||||
#define LOG_NOPEEK 0
|
||||
#define LOG_DOPEEK 1
|
||||
|
||||
@@ -490,23 +484,53 @@ kadm5_log_get_version_fd(kadm5_server_context *server_context, int fd,
|
||||
{
|
||||
kadm5_ret_t ret = 0;
|
||||
krb5_storage *sp;
|
||||
enum kadm_ops op = kadm_get;
|
||||
uint32_t len = 0;
|
||||
uint32_t tmp;
|
||||
|
||||
if (fd == -1)
|
||||
return 0; /* /dev/null */
|
||||
|
||||
if (tstamp == NULL)
|
||||
tstamp = &tmp;
|
||||
|
||||
*ver = 0;
|
||||
*tstamp = 0;
|
||||
|
||||
if (fd == -1)
|
||||
fd = server_context->log_context.log_fd;
|
||||
|
||||
sp = krb5_storage_from_fd(fd);
|
||||
if (sp == NULL)
|
||||
return errno ? errno : ENOMEM;
|
||||
|
||||
ret = kadm5_log_get_version_sp(server_context, sp, which, ver, tstamp);
|
||||
krb5_storage_free(sp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_log_get_version_sp(kadm5_server_context *server_context, krb5_storage *sp,
|
||||
int which, uint32_t *ver, uint32_t *tstamp)
|
||||
{
|
||||
struct kadm5_log_snap snap;
|
||||
kadm5_ret_t ret = 0;
|
||||
enum kadm_ops op = kadm_get;
|
||||
uint32_t len = 0;
|
||||
uint32_t tmp;
|
||||
|
||||
/*
|
||||
* If we're operating lock-less-ly then get a snapshot of the log so we can
|
||||
* validate that what we read here is correct once we're done reading it.
|
||||
*/
|
||||
memset(&snap, 0, sizeof(snap));
|
||||
if (server_context->log_context.lock_mode == LOCK_UN) {
|
||||
ret = kadm5_log_snapshot(server_context, &snap);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (tstamp == NULL)
|
||||
tstamp = &tmp;
|
||||
|
||||
*ver = 0;
|
||||
*tstamp = 0;
|
||||
|
||||
switch (which) {
|
||||
case LOG_VERSION_LAST:
|
||||
ret = kadm5_log_goto_end(server_context, sp);
|
||||
@@ -531,10 +555,92 @@ kadm5_log_get_version_fd(kadm5_server_context *server_context, int fd,
|
||||
break;
|
||||
}
|
||||
|
||||
krb5_storage_free(sp);
|
||||
if (ret == 0 && server_context->log_context.lock_mode == LOCK_UN)
|
||||
/* Validate the snapshot when operating lock-less-ly */
|
||||
return kadm5_log_snapshot_verify(server_context, &snap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* For lock-less reading of the log first get a snapshot, then read, then
|
||||
* verify the snapshot before using the data read. If snapshot verification
|
||||
* fails, start over.
|
||||
*
|
||||
* This will be used in ipropd-master and iprop-log to avoid needing a lock on
|
||||
* the iprop log. For example, when a client says I_HAVE $ver then the server
|
||||
* gets a snapshot, finds the offset to $ver and the offset to the end of the
|
||||
* log, reads all that into memory, then the server veerifies the snapshot, and
|
||||
* finally it sends the data to client, but if the snapshot verification fails
|
||||
* then the server starts over the process of finding the offset to $ver.
|
||||
*
|
||||
* The way this works is that as long as the log is not rotated and truncated,
|
||||
* then we can expect the header of the first record after the uber-record to
|
||||
* remain the same.
|
||||
*/
|
||||
kadm5_ret_t
|
||||
kadm5_log_snapshot(kadm5_server_context *server_context,
|
||||
struct kadm5_log_snap *snap)
|
||||
{
|
||||
ssize_t bytes;
|
||||
|
||||
assert(sizeof(snap->buf) < CHAR_MAX);
|
||||
snap->bytes = 0;
|
||||
|
||||
bytes = pread(server_context->log_context.log_fd,
|
||||
snap->buf, sizeof(snap->buf), 0);
|
||||
if (bytes < 0)
|
||||
return errno;
|
||||
if (bytes < LOG_UBER_SZ)
|
||||
return KADM5_LOG_CORRUPT;
|
||||
if (bytes < sizeof(snap->buf))
|
||||
bytes = LOG_UBER_SZ;
|
||||
|
||||
snap->bytes = bytes;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* kadm5_log_snapshot_verify() checks that the iprop log has not been rotated
|
||||
* since the given `snap` snapshot. Returns -1 if the log has been rotated.
|
||||
*/
|
||||
kadm5_ret_t
|
||||
kadm5_log_snapshot_verify(kadm5_server_context *server_context,
|
||||
struct kadm5_log_snap *snap)
|
||||
{
|
||||
struct kadm5_log_snap v;
|
||||
kadm5_ret_t ret;
|
||||
|
||||
assert(sizeof(snap->buf) < CHAR_MAX);
|
||||
if (snap->bytes < LOG_UBER_SZ)
|
||||
return KADM5_LOG_SNAPSHOT_INVALID;
|
||||
|
||||
ret = kadm5_log_snapshot(server_context, &v);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (v.bytes < LOG_UBER_SZ)
|
||||
return KADM5_LOG_CORRUPT;
|
||||
|
||||
/* The two snapshots are equal -> no log rotation, no log change at all */
|
||||
if (snap->bytes == v.bytes && memcmp(snap->buf, v.buf, v.bytes) == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Both snapshots capture the first record's header past the uber record?
|
||||
* Check that that first record's header is the same for both.
|
||||
*/
|
||||
if (snap->bytes == v.bytes && v.bytes == sizeof(v.buf) &&
|
||||
memcmp(snap->buf + LOG_UBER_SZ, v.buf + LOG_UBER_SZ,
|
||||
LOG_HEADER_SZ) == 0)
|
||||
return 0; /* No log rotation, but the log did change */
|
||||
|
||||
/*
|
||||
* Log rotated, or we don't have any log entries but the uber record
|
||||
* changed.
|
||||
*/
|
||||
return KADM5_LOG_SNAPSHOT_INVALID;
|
||||
}
|
||||
|
||||
/* Get the version of the last confirmed entry in the log */
|
||||
kadm5_ret_t
|
||||
kadm5_log_get_version(kadm5_server_context *server_context, uint32_t *ver)
|
||||
@@ -2133,7 +2239,7 @@ kadm5_log_goto_first(kadm5_server_context *server_context, krb5_storage *sp)
|
||||
if (ret)
|
||||
return ret;
|
||||
if (op == kadm_nop && len == LOG_UBER_LEN && seek_next(sp) == -1)
|
||||
return KADM5_LOG_CORRUPT;
|
||||
return KADM5_LOG_EMPTY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2503,6 +2609,13 @@ load_entries(kadm5_server_context *context, krb5_data *p,
|
||||
/*
|
||||
* Truncate the log, retaining at most `keep' entries and at most `maxbytes'.
|
||||
* If `maxbytes' is zero, keep at most the default log size limit.
|
||||
*
|
||||
* I.e., rotate the log, except we don't rename a new log into place.
|
||||
*
|
||||
* Ensures that the log will be smaller than prior to truncation. This is
|
||||
* important as it ensures that the log snapshot approach used by ipropd-master
|
||||
* to read the log lock-less-ly will work. See also kadm5_log_snapshot() and
|
||||
* kadm5_log_snapshot_verify().
|
||||
*/
|
||||
kadm5_ret_t
|
||||
kadm5_log_truncate(kadm5_server_context *context, size_t keep, size_t maxbytes)
|
||||
@@ -2514,7 +2627,10 @@ kadm5_log_truncate(kadm5_server_context *context, size_t keep, size_t maxbytes)
|
||||
krb5_storage *sp;
|
||||
ssize_t bytes;
|
||||
uint64_t sz;
|
||||
off_t off;
|
||||
off_t off, fsz;
|
||||
|
||||
if ((fsz = lseek(context->log_context.log_fd, 0, SEEK_END)) < 0)
|
||||
return errno;
|
||||
|
||||
if (maxbytes == 0)
|
||||
maxbytes = get_max_log_size(context->context);
|
||||
@@ -2549,6 +2665,50 @@ kadm5_log_truncate(kadm5_server_context *context, size_t keep, size_t maxbytes)
|
||||
return EOVERFLOW; /* caller should ask for fewer entries */
|
||||
}
|
||||
|
||||
/* Ensure that the iprop log will be smaller, or defer truncation */
|
||||
if (sz >= fsz) {
|
||||
uint32_t skip;
|
||||
|
||||
krb5_data_free(&entries);
|
||||
|
||||
if (first > last)
|
||||
first = 1;
|
||||
|
||||
skip = (last - first) / 2;
|
||||
|
||||
if (skip < 2) {
|
||||
/*
|
||||
* There must be an entry that is just large enough, or larger,
|
||||
* that it alone causes the log to exceed the log size
|
||||
* limit.
|
||||
*/
|
||||
krb5_warnx(context->context,
|
||||
"Unable to truncate log due to very large record; "
|
||||
"deferring truncation");
|
||||
return 0;
|
||||
}
|
||||
first += (last - first) / 2;
|
||||
ret = load_entries(context, &entries, last - first, maxbytes, &first, &last);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sz = LOG_UBER_SZ + entries.length;
|
||||
off = (off_t)sz;
|
||||
if (off < 0 || off != sz || sz < entries.length) {
|
||||
krb5_data_free(&entries);
|
||||
return EOVERFLOW; /* caller should ask for fewer entries */
|
||||
}
|
||||
|
||||
if (sz >= fsz) {
|
||||
/* Should never happen! */
|
||||
krb5_data_free(&entries);
|
||||
krb5_warnx(context->context,
|
||||
"Unable to truncate log due to very large record; "
|
||||
"deferring truncation");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Truncate to zero size and seek to zero offset */
|
||||
if (ftruncate(context->log_context.log_fd, 0) < 0 ||
|
||||
lseek(context->log_context.log_fd, 0, SEEK_SET) < 0) {
|
||||
|
||||
@@ -87,6 +87,8 @@ HEIMDAL_KAMD5_SERVER_1.0 {
|
||||
kadm5_log_init_sharedlock;
|
||||
kadm5_log_next;
|
||||
kadm5_log_nop;
|
||||
kadm5_log_snapshot;
|
||||
kadm5_log_snapshot_verify;
|
||||
kadm5_log_truncate;
|
||||
kadm5_log_modify;
|
||||
_kadm5_acl_check_permission;
|
||||
|
||||
@@ -9,7 +9,7 @@ CLEANFILES = roken.h make-roken.c $(XHEADERS)
|
||||
lib_LTLIBRARIES = libroken.la
|
||||
|
||||
libroken_la_LDFLAGS = -version-info 20:0:1
|
||||
libroken_la_CPPFLAGS = -DBUILD_ROKEN_LIB
|
||||
libroken_la_CPPFLAGS = -DBUILD_ROKEN_LIB $(SYSTEMD_CFLAGS)
|
||||
|
||||
if versionscript
|
||||
libroken_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
|
||||
@@ -199,7 +199,7 @@ EXTRA_libroken_la_SOURCES = \
|
||||
search.hin \
|
||||
vis.hin
|
||||
|
||||
libroken_la_LIBADD = @LTLIBOBJS@ $(LIB_crypt) $(LIB_pidfile)
|
||||
libroken_la_LIBADD = @LTLIBOBJS@ $(LIB_crypt) $(LIB_pidfile) $(SYSTEMD_LIBS)
|
||||
if SUNOS
|
||||
libroken_la_LIBADD += -lnsl -lsocket
|
||||
endif
|
||||
|
||||
@@ -90,6 +90,7 @@ libroken_la_OBJS = \
|
||||
$(OBJ)\parse_bytes.obj \
|
||||
$(OBJ)\parse_time.obj \
|
||||
$(OBJ)\parse_units.obj \
|
||||
$(OBJ)\pread.obj \
|
||||
$(OBJ)\realloc.obj \
|
||||
$(OBJ)\rename.obj \
|
||||
$(OBJ)\resolve.obj \
|
||||
|
||||
@@ -38,6 +38,10 @@
|
||||
#endif
|
||||
#include "roken.h"
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
#include <systemd/sd-daemon.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define dup2 _dup2
|
||||
#endif
|
||||
@@ -171,6 +175,24 @@ roken_detach_prep(int argc, char **argv, const char *special_arg)
|
||||
#define dup2 _dup2
|
||||
#endif
|
||||
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
|
||||
roken_sd_notify(const char *state)
|
||||
{
|
||||
#ifdef HAVE_SYSTEMD
|
||||
int r;
|
||||
|
||||
if (state == NULL)
|
||||
return -1;
|
||||
r = sd_notify(0, state);
|
||||
if (r > 0)
|
||||
return 0;
|
||||
return -1;
|
||||
#else
|
||||
(void)state;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
|
||||
roken_detach_finish(const char *dir, int daemon_child_fd)
|
||||
{
|
||||
@@ -203,6 +225,13 @@ roken_detach_finish(const char *dir, int daemon_child_fd)
|
||||
err(1, "failed to chdir to /");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Notify systemd that we are ready (if built with systemd support).
|
||||
* Do this after setsid/chdir and after pidfiles are written so that
|
||||
* systemd sees the process in its final state.
|
||||
*/
|
||||
(void) roken_sd_notify("READY=1");
|
||||
|
||||
do {
|
||||
bytes = write(pipefds[1], buf, sizeof(buf));
|
||||
} while (bytes == -1 && errno == EINTR);
|
||||
|
||||
749
lib/roken/dlfcn.c
Normal file
749
lib/roken/dlfcn.c
Normal file
@@ -0,0 +1,749 @@
|
||||
/*
|
||||
* dlfcn-win32
|
||||
* Copyright (c) 2007 Ramiro Polla
|
||||
* Copyright (c) 2015 Tiancheng "Timothy" Gu
|
||||
* Copyright (c) 2019 Pali Rohár <pali.rohar@gmail.com>
|
||||
* Copyright (c) 2020 Ralf Habacker <ralf.habacker@freenet.de>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#endif
|
||||
#ifdef _DEBUG
|
||||
#define _CRTDBG_MAP_ALLOC
|
||||
#include <stdlib.h>
|
||||
#include <crtdbg.h>
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* https://docs.microsoft.com/en-us/cpp/intrinsics/returnaddress */
|
||||
#pragma intrinsic(_ReturnAddress)
|
||||
#else
|
||||
/* https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html */
|
||||
#ifndef _ReturnAddress
|
||||
#define _ReturnAddress() (__builtin_extract_return_addr(__builtin_return_address(0)))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef DLFCN_WIN32_SHARED
|
||||
#define DLFCN_WIN32_EXPORTS
|
||||
#endif
|
||||
#include "dlfcn.h"
|
||||
|
||||
/* Note:
|
||||
* MSDN says these functions are not thread-safe. We make no efforts to have
|
||||
* any kind of thread safety.
|
||||
*/
|
||||
|
||||
typedef struct local_object {
|
||||
HMODULE hModule;
|
||||
struct local_object *previous;
|
||||
struct local_object *next;
|
||||
} local_object;
|
||||
|
||||
static local_object first_object;
|
||||
|
||||
/* These functions implement a double linked list for the local objects. */
|
||||
static local_object *local_search( HMODULE hModule )
|
||||
{
|
||||
local_object *pobject;
|
||||
|
||||
if( hModule == NULL )
|
||||
return NULL;
|
||||
|
||||
for( pobject = &first_object; pobject; pobject = pobject->next )
|
||||
if( pobject->hModule == hModule )
|
||||
return pobject;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BOOL local_add( HMODULE hModule )
|
||||
{
|
||||
local_object *pobject;
|
||||
local_object *nobject;
|
||||
|
||||
if( hModule == NULL )
|
||||
return TRUE;
|
||||
|
||||
pobject = local_search( hModule );
|
||||
|
||||
/* Do not add object again if it's already on the list */
|
||||
if( pobject )
|
||||
return TRUE;
|
||||
|
||||
for( pobject = &first_object; pobject->next; pobject = pobject->next );
|
||||
|
||||
nobject = (local_object*) malloc( sizeof( local_object ) );
|
||||
|
||||
if( !nobject )
|
||||
{
|
||||
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pobject->next = nobject;
|
||||
nobject->next = NULL;
|
||||
nobject->previous = pobject;
|
||||
nobject->hModule = hModule;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void local_rem( HMODULE hModule )
|
||||
{
|
||||
local_object *pobject;
|
||||
|
||||
if( hModule == NULL )
|
||||
return;
|
||||
|
||||
pobject = local_search( hModule );
|
||||
|
||||
if( !pobject )
|
||||
return;
|
||||
|
||||
if( pobject->next )
|
||||
pobject->next->previous = pobject->previous;
|
||||
if( pobject->previous )
|
||||
pobject->previous->next = pobject->next;
|
||||
|
||||
free( pobject );
|
||||
}
|
||||
|
||||
/* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one
|
||||
* static buffer.
|
||||
* MSDN says the buffer cannot be larger than 64K bytes, so we set it to
|
||||
* the limit.
|
||||
*/
|
||||
static char error_buffer[65535];
|
||||
static BOOL error_occurred;
|
||||
|
||||
static void save_err_str( const char *str )
|
||||
{
|
||||
DWORD dwMessageId;
|
||||
DWORD ret;
|
||||
size_t pos, len;
|
||||
|
||||
dwMessageId = GetLastError( );
|
||||
|
||||
if( dwMessageId == 0 )
|
||||
return;
|
||||
|
||||
len = strlen( str );
|
||||
if( len > sizeof( error_buffer ) - 5 )
|
||||
len = sizeof( error_buffer ) - 5;
|
||||
|
||||
/* Format error message to:
|
||||
* "<argument to function that failed>": <Windows localized error message>
|
||||
*/
|
||||
pos = 0;
|
||||
error_buffer[pos++] = '"';
|
||||
memcpy( error_buffer+pos, str, len );
|
||||
pos += len;
|
||||
error_buffer[pos++] = '"';
|
||||
error_buffer[pos++] = ':';
|
||||
error_buffer[pos++] = ' ';
|
||||
|
||||
ret = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwMessageId,
|
||||
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
|
||||
error_buffer+pos, (DWORD) (sizeof(error_buffer)-pos), NULL );
|
||||
pos += ret;
|
||||
|
||||
/* When FormatMessageA() fails it returns zero and does not touch buffer
|
||||
* so add trailing null byte */
|
||||
if( ret == 0 )
|
||||
error_buffer[pos] = '\0';
|
||||
|
||||
if( pos > 1 )
|
||||
{
|
||||
/* POSIX says the string must not have trailing <newline> */
|
||||
if( error_buffer[pos-2] == '\r' && error_buffer[pos-1] == '\n' )
|
||||
error_buffer[pos-2] = '\0';
|
||||
}
|
||||
|
||||
error_occurred = TRUE;
|
||||
}
|
||||
|
||||
static void save_err_ptr_str( const void *ptr )
|
||||
{
|
||||
char ptr_buf[2 + 2 * sizeof( ptr ) + 1];
|
||||
char num;
|
||||
size_t i;
|
||||
|
||||
ptr_buf[0] = '0';
|
||||
ptr_buf[1] = 'x';
|
||||
|
||||
for( i = 0; i < 2 * sizeof( ptr ); i++ )
|
||||
{
|
||||
num = ( ( (ULONG_PTR) ptr ) >> ( 8 * sizeof( ptr ) - 4 * ( i + 1 ) ) ) & 0xF;
|
||||
ptr_buf[2+i] = num + ( ( num < 0xA ) ? '0' : ( 'A' - 0xA ) );
|
||||
}
|
||||
|
||||
ptr_buf[2 + 2 * sizeof( ptr )] = 0;
|
||||
|
||||
save_err_str( ptr_buf );
|
||||
}
|
||||
|
||||
/* Load Psapi.dll at runtime, this avoids linking caveat */
|
||||
static BOOL MyEnumProcessModules( HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded )
|
||||
{
|
||||
static BOOL (WINAPI *EnumProcessModulesPtr)(HANDLE, HMODULE *, DWORD, LPDWORD);
|
||||
HMODULE psapi;
|
||||
|
||||
if( !EnumProcessModulesPtr )
|
||||
{
|
||||
psapi = LoadLibraryA( "Psapi.dll" );
|
||||
if( psapi )
|
||||
EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress( psapi, "EnumProcessModules" );
|
||||
if( !EnumProcessModulesPtr )
|
||||
return 0;
|
||||
}
|
||||
|
||||
return EnumProcessModulesPtr( hProcess, lphModule, cb, lpcbNeeded );
|
||||
}
|
||||
|
||||
DLFCN_EXPORT
|
||||
void *dlopen( const char *file, int mode )
|
||||
{
|
||||
HMODULE hModule;
|
||||
UINT uMode;
|
||||
|
||||
error_occurred = FALSE;
|
||||
|
||||
/* Do not let Windows display the critical-error-handler message box */
|
||||
uMode = SetErrorMode( SEM_FAILCRITICALERRORS );
|
||||
|
||||
if( file == 0 )
|
||||
{
|
||||
/* POSIX says that if the value of file is 0, a handle on a global
|
||||
* symbol object must be provided. That object must be able to access
|
||||
* all symbols from the original program file, and any objects loaded
|
||||
* with the RTLD_GLOBAL flag.
|
||||
* The return value from GetModuleHandle( ) allows us to retrieve
|
||||
* symbols only from the original program file. EnumProcessModules() is
|
||||
* used to access symbols from other libraries. For objects loaded
|
||||
* with the RTLD_LOCAL flag, we create our own list later on. They are
|
||||
* excluded from EnumProcessModules() iteration.
|
||||
*/
|
||||
hModule = GetModuleHandle( NULL );
|
||||
|
||||
if( !hModule )
|
||||
save_err_str( "(null)" );
|
||||
}
|
||||
else
|
||||
{
|
||||
HANDLE hCurrentProc;
|
||||
DWORD dwProcModsBefore, dwProcModsAfter;
|
||||
char lpFileName[MAX_PATH];
|
||||
size_t i, len;
|
||||
|
||||
len = strlen( file );
|
||||
|
||||
if( len >= sizeof( lpFileName ) )
|
||||
{
|
||||
SetLastError( ERROR_FILENAME_EXCED_RANGE );
|
||||
save_err_str( file );
|
||||
hModule = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* MSDN says backslashes *must* be used instead of forward slashes. */
|
||||
for( i = 0; i < len; i++ )
|
||||
{
|
||||
if( file[i] == '/' )
|
||||
lpFileName[i] = '\\';
|
||||
else
|
||||
lpFileName[i] = file[i];
|
||||
}
|
||||
lpFileName[len] = '\0';
|
||||
|
||||
hCurrentProc = GetCurrentProcess( );
|
||||
|
||||
if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsBefore ) == 0 )
|
||||
dwProcModsBefore = 0;
|
||||
|
||||
/* POSIX says the search path is implementation-defined.
|
||||
* LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely
|
||||
* to UNIX's search paths (start with system folders instead of current
|
||||
* folder).
|
||||
*/
|
||||
hModule = LoadLibraryExA( lpFileName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
|
||||
|
||||
if( !hModule )
|
||||
{
|
||||
save_err_str( lpFileName );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwProcModsAfter ) == 0 )
|
||||
dwProcModsAfter = 0;
|
||||
|
||||
/* If the object was loaded with RTLD_LOCAL, add it to list of local
|
||||
* objects, so that its symbols cannot be retrieved even if the handle for
|
||||
* the original program file is passed. POSIX says that if the same
|
||||
* file is specified in multiple invocations, and any of them are
|
||||
* RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the
|
||||
* symbols will remain global. If number of loaded modules was not
|
||||
* changed after calling LoadLibraryEx(), it means that library was
|
||||
* already loaded.
|
||||
*/
|
||||
if( (mode & RTLD_LOCAL) && dwProcModsBefore != dwProcModsAfter )
|
||||
{
|
||||
if( !local_add( hModule ) )
|
||||
{
|
||||
save_err_str( lpFileName );
|
||||
FreeLibrary( hModule );
|
||||
hModule = NULL;
|
||||
}
|
||||
}
|
||||
else if( !(mode & RTLD_LOCAL) && dwProcModsBefore == dwProcModsAfter )
|
||||
{
|
||||
local_rem( hModule );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return to previous state of the error-mode bit flags. */
|
||||
SetErrorMode( uMode );
|
||||
|
||||
return (void *) hModule;
|
||||
}
|
||||
|
||||
DLFCN_EXPORT
|
||||
int dlclose( void *handle )
|
||||
{
|
||||
HMODULE hModule = (HMODULE) handle;
|
||||
BOOL ret;
|
||||
|
||||
error_occurred = FALSE;
|
||||
|
||||
ret = FreeLibrary( hModule );
|
||||
|
||||
/* If the object was loaded with RTLD_LOCAL, remove it from list of local
|
||||
* objects.
|
||||
*/
|
||||
if( ret )
|
||||
local_rem( hModule );
|
||||
else
|
||||
save_err_ptr_str( handle );
|
||||
|
||||
/* dlclose's return value in inverted in relation to FreeLibrary's. */
|
||||
ret = !ret;
|
||||
|
||||
return (int) ret;
|
||||
}
|
||||
|
||||
__declspec(noinline) /* Needed for _ReturnAddress() */
|
||||
DLFCN_EXPORT
|
||||
void *dlsym( void *handle, const char *name )
|
||||
{
|
||||
FARPROC symbol;
|
||||
HMODULE hCaller;
|
||||
HMODULE hModule;
|
||||
HANDLE hCurrentProc;
|
||||
|
||||
error_occurred = FALSE;
|
||||
|
||||
symbol = NULL;
|
||||
hCaller = NULL;
|
||||
hModule = GetModuleHandle( NULL );
|
||||
hCurrentProc = GetCurrentProcess( );
|
||||
|
||||
if( handle == RTLD_DEFAULT )
|
||||
{
|
||||
/* The symbol lookup happens in the normal global scope; that is,
|
||||
* a search for a symbol using this handle would find the same
|
||||
* definition as a direct use of this symbol in the program code.
|
||||
* So use same lookup procedure as when filename is NULL.
|
||||
*/
|
||||
handle = hModule;
|
||||
}
|
||||
else if( handle == RTLD_NEXT )
|
||||
{
|
||||
/* Specifies the next object after this one that defines name.
|
||||
* This one refers to the object containing the invocation of dlsym().
|
||||
* The next object is the one found upon the application of a load
|
||||
* order symbol resolution algorithm. To get caller function of dlsym()
|
||||
* use _ReturnAddress() intrinsic. To get HMODULE of caller function
|
||||
* use standard GetModuleHandleExA() function.
|
||||
*/
|
||||
if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR) _ReturnAddress( ), &hCaller ) )
|
||||
goto end;
|
||||
}
|
||||
|
||||
if( handle != RTLD_NEXT )
|
||||
{
|
||||
symbol = GetProcAddress( (HMODULE) handle, name );
|
||||
|
||||
if( symbol != NULL )
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* If the handle for the original program file is passed, also search
|
||||
* in all globally loaded objects.
|
||||
*/
|
||||
|
||||
if( hModule == handle || handle == RTLD_NEXT )
|
||||
{
|
||||
HMODULE *modules;
|
||||
DWORD cbNeeded;
|
||||
DWORD dwSize;
|
||||
size_t i;
|
||||
|
||||
/* GetModuleHandle( NULL ) only returns the current program file. So
|
||||
* if we want to get ALL loaded module including those in linked DLLs,
|
||||
* we have to use EnumProcessModules( ).
|
||||
*/
|
||||
if( MyEnumProcessModules( hCurrentProc, NULL, 0, &dwSize ) != 0 )
|
||||
{
|
||||
modules = malloc( dwSize );
|
||||
if( modules )
|
||||
{
|
||||
if( MyEnumProcessModules( hCurrentProc, modules, dwSize, &cbNeeded ) != 0 && dwSize == cbNeeded )
|
||||
{
|
||||
for( i = 0; i < dwSize / sizeof( HMODULE ); i++ )
|
||||
{
|
||||
if( handle == RTLD_NEXT && hCaller )
|
||||
{
|
||||
/* Next modules can be used for RTLD_NEXT */
|
||||
if( hCaller == modules[i] )
|
||||
hCaller = NULL;
|
||||
continue;
|
||||
}
|
||||
if( local_search( modules[i] ) )
|
||||
continue;
|
||||
symbol = GetProcAddress( modules[i], name );
|
||||
if( symbol != NULL )
|
||||
{
|
||||
free( modules );
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
free( modules );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if( symbol == NULL )
|
||||
{
|
||||
if( GetLastError() == 0 )
|
||||
SetLastError( ERROR_PROC_NOT_FOUND );
|
||||
save_err_str( name );
|
||||
}
|
||||
|
||||
return *(void **) (&symbol);
|
||||
}
|
||||
|
||||
DLFCN_EXPORT
|
||||
char *dlerror( void )
|
||||
{
|
||||
/* If this is the second consecutive call to dlerror, return NULL */
|
||||
if( !error_occurred )
|
||||
return NULL;
|
||||
|
||||
/* POSIX says that invoking dlerror( ) a second time, immediately following
|
||||
* a prior invocation, shall result in NULL being returned.
|
||||
*/
|
||||
error_occurred = FALSE;
|
||||
|
||||
return error_buffer;
|
||||
}
|
||||
|
||||
/* See https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/march/inside-windows-an-in-depth-look-into-the-win32-portable-executable-file-format-part-2
|
||||
* for details */
|
||||
|
||||
/* Get specific image section */
|
||||
static BOOL get_image_section( HMODULE module, int index, void **ptr, DWORD *size )
|
||||
{
|
||||
IMAGE_DOS_HEADER *dosHeader;
|
||||
IMAGE_OPTIONAL_HEADER *optionalHeader;
|
||||
|
||||
dosHeader = (IMAGE_DOS_HEADER *) module;
|
||||
|
||||
if( dosHeader->e_magic != 0x5A4D )
|
||||
return FALSE;
|
||||
|
||||
optionalHeader = (IMAGE_OPTIONAL_HEADER *) ( (BYTE *) module + dosHeader->e_lfanew + 24 );
|
||||
|
||||
if( optionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC )
|
||||
return FALSE;
|
||||
|
||||
if( index < 0 || index > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR )
|
||||
return FALSE;
|
||||
|
||||
if( optionalHeader->DataDirectory[index].Size == 0 || optionalHeader->DataDirectory[index].VirtualAddress == 0 )
|
||||
return FALSE;
|
||||
|
||||
if( size != NULL )
|
||||
*size = optionalHeader->DataDirectory[index].Size;
|
||||
|
||||
*ptr = (void *)( (BYTE *) module + optionalHeader->DataDirectory[index].VirtualAddress );
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Return symbol name for a given address from import table */
|
||||
static const char *get_import_symbol_name( HMODULE module, IMAGE_IMPORT_DESCRIPTOR *iid, void *addr, void **func_address )
|
||||
{
|
||||
int i;
|
||||
void *candidateAddr = NULL;
|
||||
const char *candidateName = NULL;
|
||||
BYTE *base = (BYTE *) module; /* Required to have correct calculations */
|
||||
|
||||
for( i = 0; iid[i].Characteristics != 0 && iid[i].FirstThunk != 0; i++ )
|
||||
{
|
||||
IMAGE_THUNK_DATA *thunkILT = (IMAGE_THUNK_DATA *)( base + iid[i].Characteristics );
|
||||
IMAGE_THUNK_DATA *thunkIAT = (IMAGE_THUNK_DATA *)( base + iid[i].FirstThunk );
|
||||
|
||||
for( ; thunkILT->u1.AddressOfData != 0; thunkILT++, thunkIAT++ )
|
||||
{
|
||||
IMAGE_IMPORT_BY_NAME *nameData;
|
||||
|
||||
if( IMAGE_SNAP_BY_ORDINAL( thunkILT->u1.Ordinal ) )
|
||||
continue;
|
||||
|
||||
if( (void *) thunkIAT->u1.Function > addr || candidateAddr >= (void *) thunkIAT->u1.Function )
|
||||
continue;
|
||||
|
||||
candidateAddr = (void *) thunkIAT->u1.Function;
|
||||
nameData = (IMAGE_IMPORT_BY_NAME *)( base + (ULONG_PTR) thunkILT->u1.AddressOfData );
|
||||
candidateName = (const char *) nameData->Name;
|
||||
}
|
||||
}
|
||||
|
||||
*func_address = candidateAddr;
|
||||
return candidateName;
|
||||
}
|
||||
|
||||
/* Return symbol name for a given address from export table */
|
||||
static const char *get_export_symbol_name( HMODULE module, IMAGE_EXPORT_DIRECTORY *ied, void *addr, void **func_address )
|
||||
{
|
||||
DWORD i;
|
||||
void *candidateAddr = NULL;
|
||||
int candidateIndex = -1;
|
||||
BYTE *base = (BYTE *) module;
|
||||
DWORD *functionAddressesOffsets = (DWORD *) (base + ied->AddressOfFunctions);
|
||||
DWORD *functionNamesOffsets = (DWORD *) (base + ied->AddressOfNames);
|
||||
USHORT *functionNameOrdinalsIndexes = (USHORT *) (base + ied->AddressOfNameOrdinals);
|
||||
|
||||
for( i = 0; i < ied->NumberOfFunctions; i++ )
|
||||
{
|
||||
if( (void *) ( base + functionAddressesOffsets[i] ) > addr || candidateAddr >= (void *) ( base + functionAddressesOffsets[i] ) )
|
||||
continue;
|
||||
|
||||
candidateAddr = (void *) ( base + functionAddressesOffsets[i] );
|
||||
candidateIndex = i;
|
||||
}
|
||||
|
||||
if( candidateIndex == -1 )
|
||||
return NULL;
|
||||
|
||||
*func_address = candidateAddr;
|
||||
|
||||
for( i = 0; i < ied->NumberOfNames; i++ )
|
||||
{
|
||||
if( functionNameOrdinalsIndexes[i] == candidateIndex )
|
||||
return (const char *) ( base + functionNamesOffsets[i] );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BOOL is_valid_address( void *addr )
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION info;
|
||||
SIZE_T result;
|
||||
|
||||
if( addr == NULL )
|
||||
return FALSE;
|
||||
|
||||
/* check valid pointer */
|
||||
result = VirtualQuery( addr, &info, sizeof( info ) );
|
||||
|
||||
if( result == 0 || info.AllocationBase == NULL || info.AllocationProtect == 0 || info.AllocationProtect == PAGE_NOACCESS )
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Return state if address points to an import thunk
|
||||
*
|
||||
* An import thunk is setup with a 'jmp' instruction followed by an
|
||||
* absolute address (32bit) or relative offset (64bit) pointing into
|
||||
* the import address table (iat), which is partially maintained by
|
||||
* the runtime linker.
|
||||
*/
|
||||
static BOOL is_import_thunk( void *addr )
|
||||
{
|
||||
return *(short *) addr == 0x25ff ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
/* Return adress from the import address table (iat),
|
||||
* if the original address points to a thunk table entry.
|
||||
*/
|
||||
static void *get_address_from_import_address_table( void *iat, DWORD iat_size, void *addr )
|
||||
{
|
||||
BYTE *thkp = (BYTE *) addr;
|
||||
/* Get offset from thunk table (after instruction 0xff 0x25)
|
||||
* 4018c8 <_VirtualQuery>: ff 25 4a 8a 00 00
|
||||
*/
|
||||
ULONG offset = *(ULONG *)( thkp + 2 );
|
||||
#ifdef _WIN64
|
||||
/* On 64 bit the offset is relative
|
||||
* 4018c8: ff 25 4a 8a 00 00 jmpq *0x8a4a(%rip) # 40a318 <__imp_VirtualQuery>
|
||||
* And can be also negative (MSVC in WDK)
|
||||
* 100002f20: ff 25 3a e1 ff ff jmpq *-0x1ec6(%rip) # 0x100001060
|
||||
* So cast to signed LONG type
|
||||
*/
|
||||
BYTE *ptr = (BYTE *)( thkp + 6 + (LONG) offset );
|
||||
#else
|
||||
/* On 32 bit the offset is absolute
|
||||
* 4019b4: ff 25 90 71 40 00 jmp *0x40719
|
||||
*/
|
||||
BYTE *ptr = (BYTE *) offset;
|
||||
#endif
|
||||
|
||||
if( !is_valid_address( ptr ) || ptr < (BYTE *) iat || ptr > (BYTE *) iat + iat_size )
|
||||
return NULL;
|
||||
|
||||
return *(void **) ptr;
|
||||
}
|
||||
|
||||
/* Holds module filename */
|
||||
static char module_filename[2*MAX_PATH];
|
||||
|
||||
static BOOL fill_module_info( HMODULE hModuleImport, void *addr, Dl_info *info )
|
||||
{
|
||||
HMODULE hModule;
|
||||
DWORD dwSize;
|
||||
IMAGE_EXPORT_DIRECTORY *ied;
|
||||
IMAGE_IMPORT_DESCRIPTOR *iid;
|
||||
const char *name;
|
||||
void *funcAddress = NULL;
|
||||
|
||||
/* Get module of the specified address */
|
||||
if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule ) || hModule == NULL )
|
||||
return FALSE;
|
||||
|
||||
dwSize = GetModuleFileNameA( hModule, module_filename, sizeof( module_filename ) );
|
||||
|
||||
if( dwSize == 0 || dwSize == sizeof( module_filename ) )
|
||||
return FALSE;
|
||||
|
||||
info->dli_fname = module_filename;
|
||||
info->dli_fbase = (void *) hModule;
|
||||
info->dli_sname = NULL;
|
||||
|
||||
/* First try to find function name and function address in module's export table */
|
||||
if( get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_EXPORT, (void **) &ied, NULL ) )
|
||||
info->dli_sname = get_export_symbol_name( hModule, ied, addr, &funcAddress );
|
||||
|
||||
/* If symbol name is not known and we know which module is importing this address
|
||||
* then try to find symbol name in this module's import table as the last resort. */
|
||||
if( info->dli_sname == NULL && hModuleImport != NULL )
|
||||
{
|
||||
if( get_image_section( hModuleImport, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, NULL ) )
|
||||
{
|
||||
name = get_import_symbol_name( hModuleImport, iid, addr, &funcAddress );
|
||||
if( name != NULL )
|
||||
info->dli_sname = name;
|
||||
}
|
||||
}
|
||||
|
||||
info->dli_saddr = info->dli_sname == NULL ? NULL : funcAddress != NULL ? funcAddress : addr;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
DLFCN_EXPORT
|
||||
int dladdr( void *addr, Dl_info *info )
|
||||
{
|
||||
HMODULE hModule = NULL;
|
||||
|
||||
if( addr == NULL || info == NULL )
|
||||
return 0;
|
||||
|
||||
if( !is_valid_address( addr ) )
|
||||
return 0;
|
||||
|
||||
if( is_import_thunk( addr ) )
|
||||
{
|
||||
void *iat;
|
||||
DWORD iatSize;
|
||||
|
||||
/* Get module of the import thunk address */
|
||||
if( !GetModuleHandleExA( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule ) || hModule == NULL )
|
||||
return 0;
|
||||
|
||||
if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IAT, &iat, &iatSize ) )
|
||||
{
|
||||
/* Fallback for cases where the iat is not defined,
|
||||
* for example i586-mingw32msvc-gcc */
|
||||
IMAGE_IMPORT_DESCRIPTOR *iid;
|
||||
DWORD iidSize;
|
||||
|
||||
if( !get_image_section( hModule, IMAGE_DIRECTORY_ENTRY_IMPORT, (void **) &iid, &iidSize ) )
|
||||
return 0;
|
||||
|
||||
if( iid == NULL || iid->Characteristics == 0 || iid->FirstThunk == 0 )
|
||||
return 0;
|
||||
|
||||
iat = (void *)( (BYTE *) hModule + iid->FirstThunk );
|
||||
/* We assume that in this case iid and iat's are in linear order */
|
||||
iatSize = iidSize - (DWORD) ( (BYTE *) iat - (BYTE *) iid );
|
||||
}
|
||||
|
||||
addr = get_address_from_import_address_table( iat, iatSize, addr );
|
||||
|
||||
if( addr == NULL )
|
||||
return 0;
|
||||
|
||||
if( !is_valid_address( addr ) )
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( !fill_module_info( hModule, addr, info ) )
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef DLFCN_WIN32_SHARED
|
||||
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
|
||||
{
|
||||
(void) hinstDLL;
|
||||
(void) fdwReason;
|
||||
(void) lpvReserved;
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
109
lib/roken/pread.c
Normal file
109
lib/roken/pread.c
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. 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.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "roken.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
|
||||
/*
|
||||
* This is a WIN32 implementation of the POSIX pread() function.
|
||||
* It reads from the file descriptor at the specified offset without
|
||||
* changing the file position.
|
||||
*/
|
||||
ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL
|
||||
pread(int fd, void *buf, size_t nbytes, off_t off)
|
||||
{
|
||||
OVERLAPPED ov;
|
||||
HANDLE h;
|
||||
DWORD nread = 0;
|
||||
BOOL ret;
|
||||
|
||||
h = (HANDLE)_get_osfhandle(fd);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (off < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&ov, 0, sizeof(ov));
|
||||
ov.Offset = ((uint64_t)off & 0xFFFFFFFF);
|
||||
ov.OffsetHigh = (((uint64_t)off >> 32) & 0xFFFFFFFF);
|
||||
|
||||
SetLastError(0);
|
||||
ret = ReadFile(h, buf, (DWORD)nbytes, &nread, &ov);
|
||||
if (ret) {
|
||||
ssize_t bytes = nread;
|
||||
|
||||
if (bytes >= 0 && nread != (DWORD)bytes) {
|
||||
errno = EOVERFLOW;
|
||||
return -1;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map common Windows errors to errno values
|
||||
*/
|
||||
switch (GetLastError()) {
|
||||
case ERROR_HANDLE_EOF:
|
||||
return 0;
|
||||
case ERROR_INVALID_HANDLE:
|
||||
errno = EBADF;
|
||||
break;
|
||||
case ERROR_ACCESS_DENIED:
|
||||
errno = EACCES;
|
||||
break;
|
||||
case ERROR_INVALID_PARAMETER:
|
||||
errno = EINVAL;
|
||||
break;
|
||||
case ERROR_NOT_ENOUGH_QUOTA:
|
||||
errno = ENOMEM;
|
||||
break;
|
||||
case ERROR_OPERATION_ABORTED:
|
||||
errno = EINTR;
|
||||
break;
|
||||
default:
|
||||
errno = EIO;
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
@@ -860,6 +860,12 @@ ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL
|
||||
readv(int, const struct iovec *, int);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_PREAD
|
||||
#define pread rk_pread
|
||||
ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL
|
||||
pread(int, void *, size_t, off_t);
|
||||
#endif
|
||||
|
||||
#ifdef NO_PIDFILES
|
||||
#define rk_pidfile(x) ((void) 0)
|
||||
#else
|
||||
@@ -890,6 +896,7 @@ ROKEN_LIB_FUNCTION unsigned short ROKEN_LIB_CALL bswap16(unsigned short);
|
||||
* - Fallback to system flock() if available
|
||||
* - Fallback to POSIX fcntl() locks for NFS and other filesystems
|
||||
*/
|
||||
#ifndef HAVE_FLOCK
|
||||
#ifndef LOCK_SH
|
||||
#define LOCK_SH 1 /* Shared lock */
|
||||
#endif
|
||||
@@ -902,6 +909,7 @@ ROKEN_LIB_FUNCTION unsigned short ROKEN_LIB_CALL bswap16(unsigned short);
|
||||
#ifndef LOCK_UN
|
||||
#define LOCK_UN 8 /* Unlock */
|
||||
#endif
|
||||
#endif /* !HAVE_FLOCK */
|
||||
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_flock(int fd, int operation);
|
||||
#define flock(_x,_y) rk_flock(_x,_y)
|
||||
@@ -931,6 +939,7 @@ ROKEN_LIB_FUNCTION size_t ROKEN_LIB_CALL
|
||||
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL roken_detach_prep(int, char **, const char *);
|
||||
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL roken_detach_finish(const char *, int);
|
||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL roken_sd_notify(const char *);
|
||||
|
||||
ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL
|
||||
net_write (rk_socket_t, const void *, size_t);
|
||||
|
||||
26
nix/heimdal/0001-Define-HAVE_DB_185_H.patch
Normal file
26
nix/heimdal/0001-Define-HAVE_DB_185_H.patch
Normal file
@@ -0,0 +1,26 @@
|
||||
From 08d719e96214f648ae95043acc308deca36e1f7a Mon Sep 17 00:00:00 2001
|
||||
From: Ihar Hrachyshka <ihar.hrachyshka@gmail.com>
|
||||
Date: Tue, 15 Oct 2024 13:52:39 -0400
|
||||
Subject: [PATCH] Define HAVE_DB_185_H
|
||||
|
||||
---
|
||||
cf/db.m4 | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/cf/db.m4 b/cf/db.m4
|
||||
index c0b4510b6..c95a9dee9 100644
|
||||
--- a/cf/db.m4
|
||||
+++ b/cf/db.m4
|
||||
@@ -57,6 +57,9 @@ AS_IF([test "x$with_berkeley_db" != xno],
|
||||
db.h \
|
||||
])])
|
||||
|
||||
+dnl detect if compat db_185.h is present
|
||||
+AC_CHECK_HEADERS([db_185.h])
|
||||
+
|
||||
dnl db_create is used by db3 and db4 and db5 and db6
|
||||
|
||||
AC_FIND_FUNC_NO_LIBS(db_create, [$dbheader] db-6 db-5 db4 db3 db, [
|
||||
--
|
||||
2.46.0
|
||||
|
||||
25
nix/heimdal/0001-Include-db.h-for-nbdb-compat-mode.patch
Normal file
25
nix/heimdal/0001-Include-db.h-for-nbdb-compat-mode.patch
Normal file
@@ -0,0 +1,25 @@
|
||||
From 749d9451293f9d9f8a3f506401cae369003aeebf Mon Sep 17 00:00:00 2001
|
||||
From: Ihar Hrachyshka <ihar.hrachyshka@gmail.com>
|
||||
Date: Sun, 13 Oct 2024 17:16:13 -0400
|
||||
Subject: [PATCH] Include db.h for nbdb compat mode
|
||||
|
||||
---
|
||||
lib/otp/otp_db.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/lib/otp/otp_db.c b/lib/otp/otp_db.c
|
||||
index 036359c1d..32c04bc8c 100644
|
||||
--- a/lib/otp/otp_db.c
|
||||
+++ b/lib/otp/otp_db.c
|
||||
@@ -39,7 +39,7 @@ RCSID("$Id$");
|
||||
#include "otp_locl.h"
|
||||
|
||||
#if defined(HAVE_DB_NDBM)
|
||||
-# include <ndbm.h>
|
||||
+# include <db.h>
|
||||
#elif !defined(HAVE_NDBM)
|
||||
# include "ndbm_wrap.h"
|
||||
#endif
|
||||
--
|
||||
2.46.0
|
||||
|
||||
51
nix/heimdal/0001-Link-tests-with-libresolv.patch
Normal file
51
nix/heimdal/0001-Link-tests-with-libresolv.patch
Normal file
@@ -0,0 +1,51 @@
|
||||
From 862900febaec4a2c70257a39374b81138ee9f168 Mon Sep 17 00:00:00 2001
|
||||
From: Ihar Hrachyshka <ihar.hrachyshka@gmail.com>
|
||||
Date: Tue, 15 Oct 2024 16:06:33 -0400
|
||||
Subject: [PATCH] Link tests with libresolv
|
||||
|
||||
---
|
||||
lib/gssapi/Makefile.am | 1 +
|
||||
lib/krb5/Makefile.am | 2 ++
|
||||
lib/roken/Makefile.am | 1 +
|
||||
3 files changed, 4 insertions(+)
|
||||
|
||||
diff --git a/lib/gssapi/Makefile.am b/lib/gssapi/Makefile.am
|
||||
index 3254866dc..db967e586 100644
|
||||
--- a/lib/gssapi/Makefile.am
|
||||
+++ b/lib/gssapi/Makefile.am
|
||||
@@ -403,6 +403,7 @@ LDADD = libgssapi.la \
|
||||
$(top_builddir)/lib/krb5/libkrb5.la \
|
||||
$(LIB_roken)
|
||||
|
||||
+test_names_LDFLAGS = -lresolv
|
||||
test_names_LDADD = $(LDADD) $(top_builddir)/lib/asn1/libasn1.la
|
||||
test_context_LDADD = $(LDADD) $(top_builddir)/lib/asn1/libasn1.la $(top_builddir)/lib/wind/libwind.la
|
||||
|
||||
diff --git a/lib/krb5/Makefile.am b/lib/krb5/Makefile.am
|
||||
index ecce461dd..e22cfe87c 100644
|
||||
--- a/lib/krb5/Makefile.am
|
||||
+++ b/lib/krb5/Makefile.am
|
||||
@@ -330,6 +330,8 @@ test_rfc3961_LDADD = \
|
||||
$(LIB_hcrypto) \
|
||||
$(LIB_roken)
|
||||
|
||||
+test_plugin_LDFLAGS = -lresolv
|
||||
+
|
||||
if DEVELOPER_MODE
|
||||
headerdeps = $(dist_libkrb5_la_SOURCES)
|
||||
endif
|
||||
diff --git a/lib/roken/Makefile.am b/lib/roken/Makefile.am
|
||||
index 1f530c7ae..8350d7034 100644
|
||||
--- a/lib/roken/Makefile.am
|
||||
+++ b/lib/roken/Makefile.am
|
||||
@@ -54,6 +54,7 @@ libtest_la_CFLAGS = -DTEST_SNPRINTF -DTEST_STRPFTIME
|
||||
|
||||
parse_reply_test_SOURCES = parse_reply-test.c resolve.c
|
||||
parse_reply_test_CFLAGS = -DTEST_RESOLVE
|
||||
+parse_reply_test_LDFLAGS = -lresolv
|
||||
|
||||
test_readenv_SOURCES = test-readenv.c test-mem.c
|
||||
test_auxval_SOURCES = test-auxval.c
|
||||
--
|
||||
2.46.0
|
||||
|
||||
192
nix/heimdal/default.nix
Normal file
192
nix/heimdal/default.nix
Normal file
@@ -0,0 +1,192 @@
|
||||
{
|
||||
src,
|
||||
|
||||
lib,
|
||||
stdenv,
|
||||
fetchFromGitHub,
|
||||
autoreconfHook,
|
||||
pkg-config,
|
||||
python3,
|
||||
perl,
|
||||
bison,
|
||||
flex,
|
||||
texinfo,
|
||||
perlPackages,
|
||||
|
||||
openldap,
|
||||
libcap_ng,
|
||||
sqlite,
|
||||
openssl,
|
||||
db,
|
||||
libedit,
|
||||
pam,
|
||||
libmicrohttpd,
|
||||
cjson,
|
||||
systemdLibs,
|
||||
|
||||
CoreFoundation,
|
||||
Security,
|
||||
SystemConfiguration,
|
||||
|
||||
curl,
|
||||
jdk_headless,
|
||||
unzip,
|
||||
which,
|
||||
|
||||
nixosTests,
|
||||
|
||||
withCJSON ? true,
|
||||
withCapNG ? stdenv.hostPlatform.isLinux,
|
||||
# libmicrohttpd should theoretically work for darwin as well, but something is broken.
|
||||
# It affects tests check-bx509d and check-httpkadmind.
|
||||
withMicroHTTPD ? stdenv.hostPlatform.isLinux,
|
||||
withOpenLDAP ? true,
|
||||
withOpenLDAPAsHDBModule ? false,
|
||||
withOpenSSL ? true,
|
||||
withSQLite3 ? true,
|
||||
withSystemd ? true,
|
||||
}:
|
||||
|
||||
assert lib.assertMsg (withOpenLDAPAsHDBModule -> withOpenLDAP) ''
|
||||
OpenLDAP needs to be enabled in order to build the OpenLDAP HDB Module.
|
||||
'';
|
||||
|
||||
stdenv.mkDerivation {
|
||||
pname = "heimdal";
|
||||
version = "7.8.0-unstable-local";
|
||||
|
||||
inherit src;
|
||||
|
||||
outputs = [
|
||||
"out"
|
||||
"dev"
|
||||
"man"
|
||||
"info"
|
||||
];
|
||||
|
||||
nativeBuildInputs = [
|
||||
autoreconfHook
|
||||
pkg-config
|
||||
python3
|
||||
perl
|
||||
bison
|
||||
flex
|
||||
perlPackages.JSON
|
||||
texinfo
|
||||
];
|
||||
|
||||
buildInputs =
|
||||
[
|
||||
db
|
||||
libedit
|
||||
pam
|
||||
]
|
||||
++ lib.optionals (stdenv.hostPlatform.isDarwin) [
|
||||
CoreFoundation
|
||||
Security
|
||||
SystemConfiguration
|
||||
]
|
||||
++ lib.optionals (withCJSON) [ cjson ]
|
||||
++ lib.optionals (withCapNG) [ libcap_ng ]
|
||||
++ lib.optionals (withMicroHTTPD) [ libmicrohttpd ]
|
||||
++ lib.optionals (withOpenLDAP) [ openldap ]
|
||||
++ lib.optionals (withOpenSSL) [ openssl ]
|
||||
++ lib.optionals (withSQLite3) [ sqlite ]
|
||||
++ lib.optionals (withSystemd) [ systemdLibs ];
|
||||
|
||||
# env = {
|
||||
# CHECK_LOCAL = "no-check-local";
|
||||
# };
|
||||
|
||||
# doCheck = true;
|
||||
nativeCheckInputs = [
|
||||
curl
|
||||
jdk_headless
|
||||
unzip
|
||||
which
|
||||
];
|
||||
|
||||
configureFlags =
|
||||
[
|
||||
"--with-hdbdir=/var/lib/heimdal"
|
||||
|
||||
"--with-libedit-include=${libedit.dev}/include"
|
||||
"--with-libedit-lib=${libedit}/lib"
|
||||
"--with-berkeley-db-include=${db.dev}/include"
|
||||
"--with-berkeley-db"
|
||||
|
||||
"--without-x"
|
||||
"--disable-afs-string-to-key"
|
||||
]
|
||||
++ lib.optionals (withCapNG) [
|
||||
"--with-capng"
|
||||
]
|
||||
++ lib.optionals (withCJSON) [
|
||||
"--with-cjson=${cjson}"
|
||||
]
|
||||
++ lib.optionals (withOpenLDAP) [
|
||||
"--with-openldap=${openldap.dev}"
|
||||
]
|
||||
++ lib.optionals (withOpenLDAPAsHDBModule) [
|
||||
"--enable-hdb-openldap-module"
|
||||
]
|
||||
++ lib.optionals (withSQLite3) [
|
||||
"--with-sqlite3=${sqlite.dev}"
|
||||
];
|
||||
|
||||
patches = [
|
||||
# Proposed @ https://github.com/heimdal/heimdal/pull/1264
|
||||
./0001-Define-HAVE_DB_185_H.patch
|
||||
];
|
||||
|
||||
# (check-ldap) slapd resides within ${openldap}/libexec,
|
||||
# which is not part of $PATH by default.
|
||||
# (check-ldap) prepending ${openldap}/bin to the path to avoid
|
||||
# using the default installation of openldap on unsandboxed darwin systems,
|
||||
# which does not support the new mdb backend at the moment (2024-01-13).
|
||||
postPatch = ''
|
||||
substituteInPlace tests/ldap/slapd-init.in \
|
||||
--replace-fail 'SCHEMA_PATHS="' 'SCHEMA_PATHS="${openldap}/etc/schema '
|
||||
substituteInPlace tests/ldap/check-ldap.in \
|
||||
--replace-fail 'PATH=' 'PATH=${openldap}/libexec:${openldap}/bin:'
|
||||
substituteInPlace tests/kdc/check-iprop.in \
|
||||
--replace-fail '/bin/pwd' 'pwd'
|
||||
'';
|
||||
|
||||
# (test_cc) heimdal uses librokens implementation of `secure_getenv` on darwin,
|
||||
# which expects either USER or LOGNAME to be set.
|
||||
preCheck = lib.optionalString (stdenv.hostPlatform.isDarwin) ''
|
||||
export USER=nix-builder
|
||||
'';
|
||||
|
||||
postInstall = ''
|
||||
mkdir -p $dev/bin
|
||||
mv $out/bin/krb5-config $dev/bin/
|
||||
|
||||
# asn1 compilers, move them to $dev
|
||||
mv $out/libexec/heimdal/* $dev/bin
|
||||
rmdir $out/libexec/heimdal
|
||||
|
||||
# compile_et is needed for cross-compiling this package and samba
|
||||
mv lib/com_err/.libs/compile_et $dev/bin
|
||||
'';
|
||||
|
||||
# Issues with hydra
|
||||
# In file included from hxtool.c:34:0:
|
||||
# hx_locl.h:67:25: fatal error: pkcs10_asn1.h: No such file or directory
|
||||
#enableParallelBuilding = true;
|
||||
|
||||
passthru = {
|
||||
implementation = "heimdal";
|
||||
tests.nixos = nixosTests.kerberos.heimdal;
|
||||
};
|
||||
|
||||
meta = with lib; {
|
||||
homepage = "https://www.heimdal.software";
|
||||
changelog = "https://github.com/heimdal/heimdal/releases";
|
||||
description = "Implementation of Kerberos 5 (and some more stuff)";
|
||||
license = licenses.bsd3;
|
||||
platforms = platforms.unix;
|
||||
maintainers = with maintainers; [ h7x4 ];
|
||||
};
|
||||
}
|
||||
69
nix/module/default.nix
Normal file
69
nix/module/default.nix
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
cfg = config.services.kerberos_server;
|
||||
inherit (config.security.krb5) package;
|
||||
|
||||
format = import ./krb5-conf-format.nix { inherit pkgs lib; } {
|
||||
enableKdcACLEntries = true;
|
||||
};
|
||||
in
|
||||
|
||||
{
|
||||
imports = [
|
||||
(lib.mkRenamedOptionModule
|
||||
[ "services" "kerberos_server" "realms" ]
|
||||
[ "services" "kerberos_server" "settings" "realms" ]
|
||||
)
|
||||
|
||||
# ./mit.nix
|
||||
./heimdal.nix
|
||||
];
|
||||
|
||||
options = {
|
||||
services.kerberos_server = {
|
||||
enable = lib.mkEnableOption "the kerberos authentication server";
|
||||
|
||||
settings = mkOption {
|
||||
type = format.type;
|
||||
description = ''
|
||||
Settings for the kerberos server of choice.
|
||||
|
||||
See the following documentation:
|
||||
- Heimdal: {manpage}`kdc.conf(5)`
|
||||
- MIT Kerberos: <https://web.mit.edu/kerberos/krb5-1.21/doc/admin/conf_files/kdc_conf.html>
|
||||
'';
|
||||
default = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
environment.systemPackages = [ package ];
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.settings.realms != { };
|
||||
message = "The server needs at least one realm";
|
||||
}
|
||||
{
|
||||
assertion = lib.length (lib.attrNames cfg.settings.realms) <= 1;
|
||||
message = "Only one realm per server is currently supported.";
|
||||
}
|
||||
];
|
||||
|
||||
systemd.slices.system-kerberos-server = { };
|
||||
systemd.targets.kerberos-server = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
};
|
||||
};
|
||||
|
||||
# meta = {
|
||||
# doc = ./kerberos-server.md;
|
||||
# };
|
||||
}
|
||||
108
nix/module/heimdal.nix
Normal file
108
nix/module/heimdal.nix
Normal file
@@ -0,0 +1,108 @@
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib) mapAttrs;
|
||||
cfg = config.services.kerberos_server;
|
||||
package = config.security.krb5.package;
|
||||
|
||||
aclConfigs = lib.pipe cfg.settings.realms [
|
||||
(mapAttrs (
|
||||
name:
|
||||
{ acl, ... }:
|
||||
lib.concatMapStringsSep "\n" (
|
||||
{
|
||||
principal,
|
||||
access,
|
||||
target,
|
||||
...
|
||||
}:
|
||||
"${principal}\t${lib.concatStringsSep "," (lib.toList access)}\t${target}"
|
||||
) acl
|
||||
))
|
||||
(lib.mapAttrsToList (
|
||||
name: text: {
|
||||
dbname = "/var/lib/heimdal/heimdal";
|
||||
acl_file = pkgs.writeText "${name}.acl" text;
|
||||
}
|
||||
))
|
||||
];
|
||||
|
||||
finalConfig = cfg.settings // {
|
||||
realms = mapAttrs (_: v: removeAttrs v [ "acl" ]) (cfg.settings.realms or { });
|
||||
kdc = (cfg.settings.kdc or { }) // {
|
||||
database = aclConfigs;
|
||||
};
|
||||
};
|
||||
|
||||
format = import ./krb5-conf-format.nix { inherit pkgs lib; } {
|
||||
enableKdcACLEntries = true;
|
||||
};
|
||||
|
||||
kdcConfFile = format.generate "kdc.conf" finalConfig;
|
||||
in
|
||||
|
||||
{
|
||||
config = lib.mkIf (cfg.enable && package.passthru.implementation == "heimdal") {
|
||||
environment.etc."heimdal-kdc/kdc.conf".source = kdcConfFile;
|
||||
|
||||
systemd.tmpfiles.settings."10-heimdal" =
|
||||
let
|
||||
databases = lib.pipe finalConfig.kdc.database [
|
||||
(map (dbAttrs: dbAttrs.dbname or null))
|
||||
(lib.filter (x: x != null))
|
||||
lib.unique
|
||||
];
|
||||
in
|
||||
lib.genAttrs databases (_: {
|
||||
d = {
|
||||
user = "root";
|
||||
group = "root";
|
||||
mode = "0700";
|
||||
};
|
||||
});
|
||||
|
||||
systemd.services.kadmind = {
|
||||
description = "Kerberos Administration Daemon";
|
||||
partOf = [ "kerberos-server.target" ];
|
||||
wantedBy = [ "kerberos-server.target" ];
|
||||
serviceConfig = {
|
||||
Type = "notify";
|
||||
ExecStart = "${package}/libexec/kadmind --config-file=/etc/heimdal-kdc/kdc.conf";
|
||||
Slice = "system-kerberos-server.slice";
|
||||
StateDirectory = "heimdal";
|
||||
};
|
||||
restartTriggers = [ kdcConfFile ];
|
||||
};
|
||||
|
||||
systemd.services.kdc = {
|
||||
description = "Key Distribution Center daemon";
|
||||
partOf = [ "kerberos-server.target" ];
|
||||
wantedBy = [ "kerberos-server.target" ];
|
||||
serviceConfig = {
|
||||
Type = "notify";
|
||||
ExecStart = "${package}/libexec/kdc --config-file=/etc/heimdal-kdc/kdc.conf";
|
||||
Slice = "system-kerberos-server.slice";
|
||||
StateDirectory = "heimdal";
|
||||
};
|
||||
restartTriggers = [ kdcConfFile ];
|
||||
};
|
||||
|
||||
systemd.services.kpasswdd = {
|
||||
description = "Kerberos Password Changing daemon";
|
||||
partOf = [ "kerberos-server.target" ];
|
||||
wantedBy = [ "kerberos-server.target" ];
|
||||
serviceConfig = {
|
||||
Type = "notify";
|
||||
ExecStart = "${package}/libexec/kpasswdd";
|
||||
Slice = "system-kerberos-server.slice";
|
||||
StateDirectory = "heimdal";
|
||||
};
|
||||
restartTriggers = [ kdcConfFile ];
|
||||
};
|
||||
};
|
||||
}
|
||||
204
nix/module/krb5-conf-format.nix
Normal file
204
nix/module/krb5-conf-format.nix
Normal file
@@ -0,0 +1,204 @@
|
||||
{ pkgs, lib, ... }:
|
||||
|
||||
# Based on
|
||||
# - https://web.mit.edu/kerberos/krb5-1.12/doc/admin/conf_files/krb5_conf.html
|
||||
# - https://manpages.debian.org/unstable/heimdal-docs/krb5.conf.5heimdal.en.html
|
||||
|
||||
let
|
||||
inherit (lib)
|
||||
boolToString
|
||||
concatMapStringsSep
|
||||
concatStringsSep
|
||||
filter
|
||||
isAttrs
|
||||
isBool
|
||||
isList
|
||||
mapAttrsToList
|
||||
mkOption
|
||||
singleton
|
||||
splitString
|
||||
;
|
||||
inherit (lib.types)
|
||||
attrsOf
|
||||
bool
|
||||
coercedTo
|
||||
either
|
||||
enum
|
||||
int
|
||||
listOf
|
||||
oneOf
|
||||
path
|
||||
str
|
||||
submodule
|
||||
;
|
||||
in
|
||||
{
|
||||
enableKdcACLEntries ? false,
|
||||
}:
|
||||
rec {
|
||||
sectionType =
|
||||
let
|
||||
relation = oneOf [
|
||||
(listOf (attrsOf value))
|
||||
(attrsOf value)
|
||||
value
|
||||
];
|
||||
value = either (listOf atom) atom;
|
||||
atom = oneOf [
|
||||
int
|
||||
str
|
||||
bool
|
||||
];
|
||||
in
|
||||
attrsOf relation;
|
||||
|
||||
type =
|
||||
let
|
||||
aclEntry = submodule {
|
||||
options = {
|
||||
principal = mkOption {
|
||||
type = str;
|
||||
description = "Which principal the rule applies to";
|
||||
};
|
||||
access = mkOption {
|
||||
type = either (listOf (enum [
|
||||
"add"
|
||||
"cpw"
|
||||
"delete"
|
||||
"get"
|
||||
"list"
|
||||
"modify"
|
||||
])) (enum [ "all" ]);
|
||||
default = "all";
|
||||
description = "The changes the principal is allowed to make.";
|
||||
};
|
||||
target = mkOption {
|
||||
type = str;
|
||||
default = "*";
|
||||
description = "The principals that 'access' applies to.";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
realm = submodule (
|
||||
{ name, ... }:
|
||||
{
|
||||
freeformType = sectionType;
|
||||
options = {
|
||||
acl = mkOption {
|
||||
type = listOf aclEntry;
|
||||
default = [
|
||||
{
|
||||
principal = "*/admin";
|
||||
access = "all";
|
||||
}
|
||||
{
|
||||
principal = "admin";
|
||||
access = "all";
|
||||
}
|
||||
];
|
||||
description = ''
|
||||
The privileges granted to a user.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
in
|
||||
submodule {
|
||||
freeformType = attrsOf sectionType;
|
||||
options =
|
||||
{
|
||||
include = mkOption {
|
||||
default = [ ];
|
||||
description = ''
|
||||
Files to include in the Kerberos configuration.
|
||||
'';
|
||||
type = coercedTo path singleton (listOf path);
|
||||
};
|
||||
includedir = mkOption {
|
||||
default = [ ];
|
||||
description = ''
|
||||
Directories containing files to include in the Kerberos configuration.
|
||||
'';
|
||||
type = coercedTo path singleton (listOf path);
|
||||
};
|
||||
module = mkOption {
|
||||
default = [ ];
|
||||
description = ''
|
||||
Modules to obtain Kerberos configuration from.
|
||||
'';
|
||||
type = coercedTo path singleton (listOf path);
|
||||
};
|
||||
|
||||
}
|
||||
// (lib.optionalAttrs enableKdcACLEntries {
|
||||
realms = mkOption {
|
||||
type = attrsOf realm;
|
||||
description = ''
|
||||
The realm(s) to serve keys for.
|
||||
'';
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
generate =
|
||||
let
|
||||
indent = str: concatMapStringsSep "\n" (line: " " + line) (splitString "\n" str);
|
||||
|
||||
formatToplevel =
|
||||
args@{
|
||||
include ? [ ],
|
||||
includedir ? [ ],
|
||||
module ? [ ],
|
||||
...
|
||||
}:
|
||||
let
|
||||
sections = removeAttrs args [
|
||||
"include"
|
||||
"includedir"
|
||||
"module"
|
||||
];
|
||||
in
|
||||
concatStringsSep "\n" (
|
||||
filter (x: x != "") [
|
||||
(concatStringsSep "\n" (mapAttrsToList formatSection sections))
|
||||
(concatMapStringsSep "\n" (m: "module ${m}") module)
|
||||
(concatMapStringsSep "\n" (i: "include ${i}") include)
|
||||
(concatMapStringsSep "\n" (i: "includedir ${i}") includedir)
|
||||
]
|
||||
);
|
||||
|
||||
formatSection = name: section: ''
|
||||
[${name}]
|
||||
${indent (concatStringsSep "\n" (mapAttrsToList formatRelation section))}
|
||||
'';
|
||||
|
||||
formatRelation =
|
||||
name: relation:
|
||||
if isAttrs relation then
|
||||
''
|
||||
${name} = {
|
||||
${indent (concatStringsSep "\n" (mapAttrsToList formatValue relation))}
|
||||
}''
|
||||
else if isList relation then
|
||||
concatMapStringsSep "\n" (formatRelation name) relation
|
||||
else
|
||||
formatValue name relation;
|
||||
|
||||
formatValue =
|
||||
name: value:
|
||||
if isList value then concatMapStringsSep "\n" (formatAtom name) value else formatAtom name value;
|
||||
|
||||
formatAtom =
|
||||
name: atom:
|
||||
let
|
||||
v = if isBool atom then boolToString atom else toString atom;
|
||||
in
|
||||
"${name} = ${v}";
|
||||
in
|
||||
name: value:
|
||||
pkgs.writeText name ''
|
||||
${formatToplevel value}
|
||||
'';
|
||||
}
|
||||
268
nix/nixosTest.nix
Normal file
268
nix/nixosTest.nix
Normal file
@@ -0,0 +1,268 @@
|
||||
{ nixpkgs }:
|
||||
(
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
name = "kerberos_server-heimdal";
|
||||
|
||||
nodes = {
|
||||
server =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
disabledModules = [ "services/system/kerberos/default.nix" ];
|
||||
imports = [
|
||||
"${nixpkgs}/nixos/tests/common/user-account.nix"
|
||||
./module
|
||||
];
|
||||
|
||||
users.users.alice.extraGroups = [ "wheel" ];
|
||||
|
||||
services.getty.autologinUser = "alice";
|
||||
|
||||
virtualisation.vlans = [ 1 ];
|
||||
|
||||
time.timeZone = "Etc/UTC";
|
||||
|
||||
networking = {
|
||||
domain = "foo.bar";
|
||||
useDHCP = false;
|
||||
firewall.enable = false;
|
||||
hosts."10.0.0.1" = [ "server.foo.bar" ];
|
||||
hosts."10.0.0.2" = [ "client.foo.bar" ];
|
||||
};
|
||||
|
||||
systemd.network.networks."01-eth1" = {
|
||||
name = "eth1";
|
||||
networkConfig.Address = "10.0.0.1/24";
|
||||
};
|
||||
|
||||
security.krb5 = {
|
||||
enable = true;
|
||||
package = pkgs.heimdal;
|
||||
settings = {
|
||||
libdefaults.default_realm = "FOO.BAR";
|
||||
|
||||
# Enable extra debug output
|
||||
logging = {
|
||||
admin_server = "SYSLOG:DEBUG:AUTH";
|
||||
default = "SYSLOG:DEBUG:AUTH";
|
||||
kdc = "SYSLOG:DEBUG:AUTH";
|
||||
};
|
||||
|
||||
realms = {
|
||||
"FOO.BAR" = {
|
||||
admin_server = "server.foo.bar";
|
||||
kpasswd_server = "server.foo.bar";
|
||||
kdc = [ "server.foo.bar" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.kerberos_server = {
|
||||
enable = true;
|
||||
settings.realms = {
|
||||
"FOO.BAR" = {
|
||||
acl = [
|
||||
{
|
||||
principal = "kadmin/admin@FOO.BAR";
|
||||
access = "all";
|
||||
}
|
||||
{
|
||||
principal = "alice/admin@FOO.BAR";
|
||||
access = [
|
||||
"add"
|
||||
"cpw"
|
||||
"delete"
|
||||
"get"
|
||||
"list"
|
||||
"modify"
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
client =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
disabledModules = [ "services/system/kerberos/default.nix" ];
|
||||
imports = [
|
||||
"${nixpkgs}/nixos/tests/common/user-account.nix"
|
||||
./module
|
||||
];
|
||||
|
||||
users.users.alice.extraGroups = [ "wheel" ];
|
||||
|
||||
services.getty.autologinUser = "alice";
|
||||
|
||||
virtualisation.vlans = [ 1 ];
|
||||
|
||||
time.timeZone = "Etc/UTC";
|
||||
|
||||
networking = {
|
||||
domain = "foo.bar";
|
||||
useDHCP = false;
|
||||
hosts."10.0.0.1" = [ "server.foo.bar" ];
|
||||
hosts."10.0.0.2" = [ "client.foo.bar" ];
|
||||
};
|
||||
|
||||
systemd.network.networks."01-eth1" = {
|
||||
name = "eth1";
|
||||
networkConfig.Address = "10.0.0.2/24";
|
||||
};
|
||||
|
||||
security.krb5 = {
|
||||
enable = true;
|
||||
package = pkgs.heimdal;
|
||||
settings = {
|
||||
libdefaults.default_realm = "FOO.BAR";
|
||||
|
||||
logging = {
|
||||
admin_server = "SYSLOG:DEBUG:AUTH";
|
||||
default = "SYSLOG:DEBUG:AUTH";
|
||||
kdc = "SYSLOG:DEBUG:AUTH";
|
||||
};
|
||||
|
||||
realms = {
|
||||
"FOO.BAR" = {
|
||||
admin_server = "server.foo.bar";
|
||||
kpasswd_server = "server.foo.bar";
|
||||
kdc = [ "server.foo.bar" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript =
|
||||
{ nodes, ... }:
|
||||
''
|
||||
import string
|
||||
import random
|
||||
random.seed(0)
|
||||
|
||||
start_all()
|
||||
|
||||
with subtest("Server: initialize realm"):
|
||||
# for unit in ["kadmind.service", "kdc.socket", "kpasswdd.socket"]:
|
||||
for unit in ["kadmind.service", "kdc.service", "kpasswdd.service"]:
|
||||
server.wait_for_unit(unit)
|
||||
|
||||
server.succeed("kadmin -l init --realm-max-ticket-life='8 day' --realm-max-renewable-life='10 day' FOO.BAR")
|
||||
|
||||
for unit in ["kadmind.service", "kdc.service", "kpasswdd.service"]:
|
||||
server.systemctl(f"restart {unit}")
|
||||
|
||||
alice_krb_pw = "alice_hunter2"
|
||||
alice_old_krb_pw = ""
|
||||
alice_krb_admin_pw = "alice_admin_hunter2"
|
||||
|
||||
def random_password():
|
||||
password_chars = string.ascii_letters + string.digits + string.punctuation.replace('"', "")
|
||||
return "".join(random.choice(password_chars) for _ in range(16))
|
||||
|
||||
with subtest("Server: initialize user principals and keytabs"):
|
||||
server.succeed(f'kadmin -l add --password="{alice_krb_admin_pw}" --use-defaults alice/admin')
|
||||
server.succeed("kadmin -l ext_keytab --keytab=admin.keytab alice/admin")
|
||||
|
||||
server.succeed(f'kadmin -p alice/admin -K admin.keytab add --password="{alice_krb_pw}" --use-defaults alice')
|
||||
server.succeed("kadmin -l ext_keytab --keytab=alice.keytab alice")
|
||||
|
||||
server.wait_for_unit("getty@tty1.service")
|
||||
server.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
|
||||
server.wait_for_unit("default.target")
|
||||
|
||||
with subtest("Server: initialize host principal with keytab"):
|
||||
server.send_chars("sudo ktutil get -p alice/admin host/server.foo.bar\n")
|
||||
server.wait_until_tty_matches("1", "password for alice:")
|
||||
server.send_chars("${nodes.server.config.users.users.alice.password}\n")
|
||||
server.wait_until_tty_matches("1", "alice/admin@FOO.BAR's Password:")
|
||||
server.send_chars(f'{alice_krb_admin_pw}\n')
|
||||
server.wait_for_file("/etc/krb5.keytab")
|
||||
|
||||
ktutil_list = server.succeed("sudo ktutil list")
|
||||
if not "host/server.foo.bar" in ktutil_list:
|
||||
exit(1)
|
||||
|
||||
server.send_chars("clear\n")
|
||||
|
||||
client.systemctl("start network-online.target")
|
||||
client.wait_for_unit("network-online.target")
|
||||
client.wait_for_unit("getty@tty1.service")
|
||||
client.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
|
||||
client.wait_for_unit("default.target")
|
||||
|
||||
with subtest("Client: initialize host principal with keytab"):
|
||||
client.succeed(
|
||||
f'echo "{alice_krb_admin_pw}" > pw.txt',
|
||||
"kinit -p --password-file=pw.txt alice/admin",
|
||||
)
|
||||
|
||||
client.send_chars("sudo ktutil get -p alice/admin host/client.foo.bar\n")
|
||||
client.wait_until_tty_matches("1", "password for alice:")
|
||||
client.send_chars("${nodes.client.config.users.users.alice.password}\n")
|
||||
client.wait_until_tty_matches("1", "alice/admin@FOO.BAR's Password:")
|
||||
client.send_chars(f"{alice_krb_admin_pw}\n")
|
||||
client.wait_for_file("/etc/krb5.keytab")
|
||||
|
||||
ktutil_list = client.succeed("sudo ktutil list")
|
||||
if not "host/client.foo.bar" in ktutil_list:
|
||||
exit(1)
|
||||
|
||||
client.send_chars("clear\n")
|
||||
|
||||
with subtest("Client: kinit alice"):
|
||||
client.succeed(
|
||||
f"echo '{alice_krb_pw}' > pw.txt",
|
||||
"kinit -p --password-file=pw.txt alice",
|
||||
)
|
||||
tickets = client.succeed("klist")
|
||||
assert "Principal: alice@FOO.BAR" in tickets
|
||||
client.send_chars("clear\n")
|
||||
|
||||
with subtest("Client: kpasswd alice"):
|
||||
alice_old_krb_pw = alice_krb_pw
|
||||
alice_krb_pw = random_password()
|
||||
client.send_chars("kpasswd\n")
|
||||
client.wait_until_tty_matches("1", "alice@FOO.BAR's Password:")
|
||||
client.send_chars(f"{alice_old_krb_pw}\n", 0.1)
|
||||
client.wait_until_tty_matches("1", "New password:")
|
||||
client.send_chars(f"{alice_krb_pw}\n", 0.1)
|
||||
client.wait_until_tty_matches("1", "Verify password - New password:")
|
||||
client.send_chars(f"{alice_krb_pw}\n", 0.1)
|
||||
|
||||
client.wait_until_tty_matches("1", "Success : Password changed")
|
||||
|
||||
client.send_chars("clear\n")
|
||||
|
||||
with subtest("Server: kinit alice"):
|
||||
server.succeed(
|
||||
"echo 'alice_pw_2' > pw.txt"
|
||||
"kinit -p --password-file=pw.txt alice",
|
||||
)
|
||||
tickets = client.succeed("klist")
|
||||
assert "Principal: alice@FOO.BAR" in tickets
|
||||
server.send_chars("clear\n")
|
||||
|
||||
with subtest("Server: kpasswd alice"):
|
||||
alice_old_krb_pw = alice_krb_pw
|
||||
alice_krb_pw = random_password()
|
||||
server.send_chars("kpasswd\n")
|
||||
server.wait_until_tty_matches("1", "alice@FOO.BAR's Password:")
|
||||
server.send_chars(f"{alice_old_krb_pw}\n", 0.1)
|
||||
server.wait_until_tty_matches("1", "New password:")
|
||||
server.send_chars(f"{alice_krb_pw}\n", 0.1)
|
||||
server.wait_until_tty_matches("1", "Verify password - New password:")
|
||||
server.send_chars(f"{alice_krb_pw}\n", 0.1)
|
||||
|
||||
server.wait_until_tty_matches("1", "Success : Password changed")
|
||||
|
||||
server.send_chars("clear\n")
|
||||
'';
|
||||
|
||||
meta.maintainers = pkgs.heimdal.meta.maintainers;
|
||||
}
|
||||
)
|
||||
51
nix/shell.nix
Normal file
51
nix/shell.nix
Normal file
@@ -0,0 +1,51 @@
|
||||
{ pkgs, lib }:
|
||||
pkgs.mkShell {
|
||||
nativeBuildInputs = with pkgs; [
|
||||
autoconf
|
||||
automake
|
||||
autogen
|
||||
autoreconfHook
|
||||
|
||||
pkg-config
|
||||
python3
|
||||
perl
|
||||
bison
|
||||
flex
|
||||
perlPackages.JSON
|
||||
texinfo
|
||||
|
||||
# check inputs
|
||||
curl
|
||||
jdk_headless
|
||||
unzip
|
||||
which
|
||||
];
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
db
|
||||
libedit
|
||||
pam
|
||||
cjson
|
||||
libcap_ng
|
||||
libmicrohttpd
|
||||
openldap
|
||||
openssl
|
||||
sqlite
|
||||
];
|
||||
|
||||
env = {
|
||||
CFLAGS = lib.concatStringsSep " " [
|
||||
# From github workflows
|
||||
"-Wno-error=shadow"
|
||||
"-Wno-error=bad-function-cast"
|
||||
"-Wno-error=unused-function"
|
||||
"-Wno-error=unused-result"
|
||||
"-Wno-error=deprecated-declarations"
|
||||
|
||||
# idk, but it complained about these during compilation.
|
||||
# maybe they come from the nix environment, or too new compiler?
|
||||
"-Wno-error=maybe-uninitialized"
|
||||
"-Wno-error=format-overflow"
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -272,7 +272,7 @@ test_run_x() {
|
||||
|
||||
# Run with tracing enabled
|
||||
(
|
||||
set -x
|
||||
set -vx
|
||||
"$@"
|
||||
) > "$cmd_out" 2>"$trace_out"
|
||||
rc=$?
|
||||
|
||||
@@ -77,6 +77,8 @@ rm -f mkey.file*
|
||||
|
||||
test_init
|
||||
|
||||
alias test_run=test_run_x
|
||||
|
||||
test_section "Creating database"
|
||||
# add both lucid and lucid.test.h5l.se to simulate aliases
|
||||
# XXX ext should ext aliases too
|
||||
|
||||
@@ -31,9 +31,6 @@
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
|
||||
# Skip until hierarchical iprop deadlock is fixed
|
||||
exit 77
|
||||
|
||||
top_builddir="@top_builddir@"
|
||||
env_setup="@env_setup@"
|
||||
objdir="@objdir@"
|
||||
@@ -285,14 +282,14 @@ rm -f iprop-slave-status iprop-slave-status2
|
||||
|
||||
ipropd_slave2=$ipropd_slave
|
||||
ipropd_master2=$ipropd_master
|
||||
ipropd_slave="${ipropd_slave} --status-file=iprop-slave-status --port=$ipropport"
|
||||
ipropd_slave="${ipropd_slave} --verbose --status-file=iprop-slave-status --port=$ipropport"
|
||||
ipropd_slave="${ipropd_slave} --hostname=slave.test.h5l.se -k ${keytab}"
|
||||
ipropd_slave="${ipropd_slave} --detach localhost"
|
||||
ipropd_master="${ipropd_master} --hostname=localhost -k ${keytab}"
|
||||
ipropd_master="${ipropd_master} --verbose --hostname=localhost -k ${keytab}"
|
||||
ipropd_master="${ipropd_master} --port=$ipropport"
|
||||
ipropd_master="${ipropd_master} --database=${objdir}/current-db --detach"
|
||||
|
||||
ipropd_slave2="${ipropd_slave2} --status-file=iprop-slave-status2 --port=$ipropport2"
|
||||
ipropd_slave2="${ipropd_slave2} --verbose --status-file=iprop-slave-status2 --port=$ipropport2"
|
||||
ipropd_slave2="${ipropd_slave2} --hostname=slave.test.h5l.se -k ${keytab}"
|
||||
ipropd_slave2="${ipropd_slave2} --pidfile-basename=ipropd-slave2"
|
||||
ipropd_slave2="${ipropd_slave2} --detach localhost"
|
||||
@@ -554,7 +551,7 @@ wait_for_master_down
|
||||
wait_for "slave to disconnect" \
|
||||
${EGREP} 'disconnected' iprop-slave-status >/dev/null
|
||||
|
||||
if ! tail -30 messages.log | grep 'disconnected for server' > /dev/null; then
|
||||
if ! tail -30 messages.log | grep 'disconnected from server' > /dev/null; then
|
||||
echo "client didnt disconnect"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user