Compare commits
3 Commits
master
...
kadmin-get
| Author | SHA1 | Date | |
|---|---|---|---|
|
845fb73ad1
|
|||
|
d0b5821e9a
|
|||
|
072c1f062f
|
1
.github/workflows/coverage.yml
vendored
1
.github/workflows/coverage.yml
vendored
@@ -36,7 +36,6 @@ jobs:
|
|||||||
../configure --srcdir=`dirname "$PWD"` --enable-maintainer-mode --enable-developer --with-ldap --prefix=$HOME/inst CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS"
|
../configure --srcdir=`dirname "$PWD"` --enable-maintainer-mode --enable-developer --with-ldap --prefix=$HOME/inst CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS"
|
||||||
make -j4
|
make -j4
|
||||||
- name: Test
|
- name: Test
|
||||||
timeout-minutes: 40
|
|
||||||
run: |
|
run: |
|
||||||
cd build
|
cd build
|
||||||
sudo sysctl kernel.core_pattern=core.%p || true
|
sudo sysctl kernel.core_pattern=core.%p || true
|
||||||
|
|||||||
1
.github/workflows/linux-interop.yml
vendored
1
.github/workflows/linux-interop.yml
vendored
@@ -78,7 +78,6 @@ 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
|
../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
|
make -j4
|
||||||
- name: Test
|
- name: Test
|
||||||
timeout-minutes: 20
|
|
||||||
env:
|
env:
|
||||||
CC: ${{ matrix.compiler }}
|
CC: ${{ matrix.compiler }}
|
||||||
MAKEVARS: ${{ matrix.makevars }}
|
MAKEVARS: ${{ matrix.makevars }}
|
||||||
|
|||||||
22
.github/workflows/linux.yml
vendored
22
.github/workflows/linux.yml
vendored
@@ -90,7 +90,6 @@ 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"
|
../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
|
make -j4
|
||||||
- name: Test
|
- name: Test
|
||||||
timeout-minutes: 40
|
|
||||||
env:
|
env:
|
||||||
CC: ${{ matrix.compiler }}
|
CC: ${{ matrix.compiler }}
|
||||||
MAKEVARS: ${{ matrix.makevars }}
|
MAKEVARS: ${{ matrix.makevars }}
|
||||||
@@ -108,27 +107,6 @@ jobs:
|
|||||||
make DESTDIR=/tmp/h5l install
|
make DESTDIR=/tmp/h5l install
|
||||||
cd /tmp/h5l
|
cd /tmp/h5l
|
||||||
tar czf $HOME/heimdal-install-linux-${{ matrix.compiler }}.tgz .
|
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
|
- name: Core dump stacks
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
23
.github/workflows/osx.yml
vendored
23
.github/workflows/osx.yml
vendored
@@ -93,7 +93,6 @@ jobs:
|
|||||||
# with:
|
# with:
|
||||||
# limit-access-to-actor: true
|
# limit-access-to-actor: true
|
||||||
- name: Test
|
- name: Test
|
||||||
timeout-minutes: 25
|
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
CC: ${{ matrix.compiler }}
|
CC: ${{ matrix.compiler }}
|
||||||
@@ -106,27 +105,6 @@ jobs:
|
|||||||
sudo lsof -nP -i:49188 || true
|
sudo lsof -nP -i:49188 || true
|
||||||
cd build
|
cd build
|
||||||
make check
|
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
|
- name: Install
|
||||||
run: |
|
run: |
|
||||||
cd build || true
|
cd build || true
|
||||||
@@ -134,7 +112,6 @@ jobs:
|
|||||||
cd /tmp/h5l
|
cd /tmp/h5l
|
||||||
tar czf $HOME/heimdal-install-osx.tgz .
|
tar czf $HOME/heimdal-install-osx.tgz .
|
||||||
- name: Test logs
|
- name: Test logs
|
||||||
if: ${{ always() }}
|
|
||||||
run: |
|
run: |
|
||||||
find build -depth -name \*.trs|xargs grep -lw FAIL|sed -e 's/trs$/log/' | cpio -o > $HOME/logs-osx.cpio
|
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
|
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,7 +49,6 @@ jobs:
|
|||||||
(cd lib/sqlite && make -j4)
|
(cd lib/sqlite && make -j4)
|
||||||
scan-build --keep-going make -j4
|
scan-build --keep-going make -j4
|
||||||
- name: Test
|
- name: Test
|
||||||
timeout-minutes: 40
|
|
||||||
env:
|
env:
|
||||||
CC: ${{ matrix.compiler }}
|
CC: ${{ matrix.compiler }}
|
||||||
MAKEVARS: ${{ matrix.makevars }}
|
MAKEVARS: ${{ matrix.makevars }}
|
||||||
|
|||||||
1
.github/workflows/ubsan.yml
vendored
1
.github/workflows/ubsan.yml
vendored
@@ -90,7 +90,6 @@ 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 }}"
|
../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
|
make -j4
|
||||||
- name: Test
|
- name: Test
|
||||||
timeout-minutes: 80
|
|
||||||
env:
|
env:
|
||||||
CC: ${{ matrix.compiler }}
|
CC: ${{ matrix.compiler }}
|
||||||
MAKEVARS: ${{ matrix.makevars }}
|
MAKEVARS: ${{ matrix.makevars }}
|
||||||
|
|||||||
1
.github/workflows/valgrind.yml
vendored
1
.github/workflows/valgrind.yml
vendored
@@ -46,7 +46,6 @@ 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"
|
../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
|
make -j4
|
||||||
- name: Test
|
- name: Test
|
||||||
timeout-minutes: 120
|
|
||||||
env:
|
env:
|
||||||
CC: ${{ matrix.compiler }}
|
CC: ${{ matrix.compiler }}
|
||||||
MAKEVARS: ${{ matrix.makevars }}
|
MAKEVARS: ${{ matrix.makevars }}
|
||||||
|
|||||||
1
.github/workflows/windows.yml
vendored
1
.github/workflows/windows.yml
vendored
@@ -119,7 +119,6 @@ jobs:
|
|||||||
)
|
)
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
timeout-minutes: 20
|
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
set "PATH=%PATH%;C:\msys64\usr\bin"
|
set "PATH=%PATH%;C:\msys64\usr\bin"
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -720,6 +720,3 @@ asn1_*_asn1.c
|
|||||||
/tools/krb5-gssapi.pc
|
/tools/krb5-gssapi.pc
|
||||||
/tools/krb5.pc
|
/tools/krb5.pc
|
||||||
/tools/krb5-config
|
/tools/krb5-config
|
||||||
|
|
||||||
result
|
|
||||||
result-*
|
|
||||||
|
|||||||
@@ -360,7 +360,6 @@ AC_BROKEN([ \
|
|||||||
mergesort \
|
mergesort \
|
||||||
mergesort_r \
|
mergesort_r \
|
||||||
mkstemp \
|
mkstemp \
|
||||||
pread \
|
|
||||||
putenv \
|
putenv \
|
||||||
rcmd \
|
rcmd \
|
||||||
readv \
|
readv \
|
||||||
|
|||||||
6
flake.lock
generated
6
flake.lock
generated
@@ -2,11 +2,11 @@
|
|||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1770380644,
|
"lastModified": 1742800061,
|
||||||
"narHash": "sha256-P7dWMHRUWG5m4G+06jDyThXO7kwSk46C1kgjEWcybkE=",
|
"narHash": "sha256-oDJGK1UMArK52vcW9S5S2apeec4rbfNELgc50LqiPNs=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "ae67888ff7ef9dff69b3cf0cc0fbfbcd3a722abe",
|
"rev": "1750f3c1c89488e2ffdd47cab9d05454dddfb734",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
22
flake.nix
22
flake.nix
@@ -22,27 +22,13 @@
|
|||||||
default = pkgs.callPackage ./nix/shell.nix { };
|
default = pkgs.callPackage ./nix/shell.nix { };
|
||||||
});
|
});
|
||||||
|
|
||||||
packages = forAllSystems (system: pkgs: let
|
packages = forAllSystems (system: pkgs: with pkgs; {
|
||||||
src = lib.fileset.toSource {
|
|
||||||
root = ./.;
|
|
||||||
fileset = lib.fileset.difference ./. (lib.fileset.unions [
|
|
||||||
./.github
|
|
||||||
./.gitignore
|
|
||||||
./.zed
|
|
||||||
./flake.nix
|
|
||||||
./flake.lock
|
|
||||||
./nix
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
in {
|
|
||||||
default = self.packages.${system}.heimdal;
|
default = self.packages.${system}.heimdal;
|
||||||
|
|
||||||
src = pkgs.runCommand "heimdal-src" {} "ln -s ${src} \"$out\"";
|
heimdal = pkgs.callPackage ./nix/heimdal {
|
||||||
|
src = lib.cleanSource ./.;
|
||||||
heimdal = pkgs.callPackage ./nix/heimdal/package.nix {
|
|
||||||
inherit src;
|
|
||||||
inherit (pkgs.darwin.apple_sdk.frameworks) CoreFoundation Security SystemConfiguration;
|
inherit (pkgs.darwin.apple_sdk.frameworks) CoreFoundation Security SystemConfiguration;
|
||||||
autoreconfHook = pkgs.buildPackages.autoreconfHook271;
|
autoreconfHook = pkgs.buildPackages.autoreconfHook269;
|
||||||
};
|
};
|
||||||
|
|
||||||
nixosTest = pkgs.testers.runNixOSTest (import ./nix/nixosTest.nix { inherit nixpkgs; });
|
nixosTest = pkgs.testers.runNixOSTest (import ./nix/nixosTest.nix { inherit nixpkgs; });
|
||||||
|
|||||||
17
kadmin/ext.c
17
kadmin/ext.c
@@ -68,6 +68,12 @@ do_ext_keytab(krb5_principal principal, void *data)
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
/* Debug: record which principal was fetched and some context */
|
||||||
|
krb5_warnx(context, "do_ext_keytab: fetched principal %s mask=0x%x n_key_data=%d",
|
||||||
|
unparsed ? unparsed : "<unparsed>",
|
||||||
|
mask,
|
||||||
|
(int)princ.n_key_data);
|
||||||
|
|
||||||
if (!e->random_key_flag) {
|
if (!e->random_key_flag) {
|
||||||
if (princ.n_key_data == 0) {
|
if (princ.n_key_data == 0) {
|
||||||
krb5_warnx(context, "principal has no keys, or user lacks "
|
krb5_warnx(context, "principal has no keys, or user lacks "
|
||||||
@@ -111,6 +117,13 @@ do_ext_keytab(krb5_principal principal, void *data)
|
|||||||
keys[i].keyblock.keyvalue.data = kd->key_data_contents[0];
|
keys[i].keyblock.keyvalue.data = kd->key_data_contents[0];
|
||||||
keys[i].timestamp = time(NULL);
|
keys[i].timestamp = time(NULL);
|
||||||
n_k++;
|
n_k++;
|
||||||
|
|
||||||
|
/* Debug: log each key extracted (kvno/enctype) for the principal */
|
||||||
|
krb5_warnx(context, "do_ext_keytab: principal=%s key_index=%zu kvno=%d enctype=%d",
|
||||||
|
unparsed ? unparsed : "<unparsed>",
|
||||||
|
i,
|
||||||
|
keys[i].vno,
|
||||||
|
keys[i].keyblock.keytype);
|
||||||
}
|
}
|
||||||
} else if (e->random_key_flag) {
|
} else if (e->random_key_flag) {
|
||||||
ret = kadm5_randkey_principal_3(e->kadm_handle, principal, e->keep,
|
ret = kadm5_randkey_principal_3(e->kadm_handle, principal, e->keep,
|
||||||
@@ -208,6 +221,10 @@ ext_keytab(struct ext_keytab_options *opt, int argc, char **argv)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Debug: record ext_keytab invocation details */
|
||||||
|
krb5_warnx(context, "ext_keytab: invoking foreach_principal for %zu principals, enctypes=%s",
|
||||||
|
(size_t)argc, enctypes ? enctypes : "<none>");
|
||||||
|
|
||||||
for(i = 0; i < argc; i++) {
|
for(i = 0; i < argc; i++) {
|
||||||
ret = foreach_principal(argv[i], do_ext_keytab, "ext", &data);
|
ret = foreach_principal(argv[i], do_ext_keytab, "ext", &data);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|||||||
69
kadmin/get.c
69
kadmin/get.c
@@ -488,12 +488,71 @@ do_get_entry(krb5_principal principal, void *data)
|
|||||||
e->upto--;
|
e->upto--;
|
||||||
|
|
||||||
memset(&princ, 0, sizeof(princ));
|
memset(&princ, 0, sizeof(princ));
|
||||||
|
|
||||||
|
/* Loud tracing: record which principal we are about to request from kadmind.
|
||||||
|
*
|
||||||
|
* Guard the unparse call: don't call into unparse with a NULL principal
|
||||||
|
* and only use the unparsed string if the call succeeds.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
char *want = NULL;
|
||||||
|
if (principal != NULL && krb5_unparse_name(context, principal, &want) == 0 && want != NULL) {
|
||||||
|
krb5_warnx(context, "kadmin:get: do_get_entry: requesting principal lookup for %s (mask=0x%x extra_mask=0x%x) (pid=%d)",
|
||||||
|
want, e->mask, e->extra_mask, (int)getpid());
|
||||||
|
} else {
|
||||||
|
krb5_warnx(context, "kadmin:get: do_get_entry: requesting principal lookup for <unparsed> (mask=0x%x extra_mask=0x%x) (pid=%d)",
|
||||||
|
e->mask, e->extra_mask, (int)getpid());
|
||||||
|
}
|
||||||
|
free(want);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Always request the principal field so we can safely unparse/format
|
||||||
|
* the returned entry. Some callers request only TL_DATA (eg aliases),
|
||||||
|
* and the kadm5 backend may return successful results without filling
|
||||||
|
* the principal pointer which leads to crashes when we try to use it.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
uint32_t want_mask = e->mask | e->extra_mask | KADM5_PRINCIPAL;
|
||||||
ret = kadm5_get_principal(e->kadm_handle, principal,
|
ret = kadm5_get_principal(e->kadm_handle, principal,
|
||||||
&princ,
|
&princ,
|
||||||
e->mask | e->extra_mask);
|
want_mask);
|
||||||
|
}
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
|
/* Success: log the fact we received a principal record and some basic info */
|
||||||
|
{
|
||||||
|
char *got = NULL;
|
||||||
|
if (krb5_unparse_name(context, princ.principal, &got) == 0) {
|
||||||
|
krb5_warnx(context, "kadmin:get: do_get_entry: kadm5_get_principal succeeded: principal=%s kvno=%d n_key_data=%d (pid=%d)",
|
||||||
|
got ? got : "<unparsed>",
|
||||||
|
princ.kvno,
|
||||||
|
princ.n_key_data,
|
||||||
|
(int)getpid());
|
||||||
|
} else {
|
||||||
|
krb5_warnx(context, "kadmin:get: do_get_entry: kadm5_get_principal succeeded: <unparsed principal> kvno=%d n_key_data=%d (pid=%d)",
|
||||||
|
princ.kvno, princ.n_key_data, (int)getpid());
|
||||||
|
}
|
||||||
|
free(got);
|
||||||
|
}
|
||||||
|
|
||||||
(e->format)(e, &princ);
|
(e->format)(e, &princ);
|
||||||
kadm5_free_principal_ent(e->kadm_handle, &princ);
|
kadm5_free_principal_ent(e->kadm_handle, &princ);
|
||||||
|
} else {
|
||||||
|
/* Failure: log explicit error so we can trace failing lookups.
|
||||||
|
*
|
||||||
|
* Guard the unparse call similarly to above.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
char *want2 = NULL;
|
||||||
|
if (principal != NULL && krb5_unparse_name(context, principal, &want2) == 0 && want2 != NULL) {
|
||||||
|
krb5_warn(context, ret, "kadmin:get: do_get_entry: kadm5_get_principal(%s) failed (pid=%d)",
|
||||||
|
want2, (int)getpid());
|
||||||
|
} else {
|
||||||
|
krb5_warn(context, ret, "kadmin:get: do_get_entry: kadm5_get_principal(<unparsed>) failed (pid=%d)",
|
||||||
|
(int)getpid());
|
||||||
|
}
|
||||||
|
free(want2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
e->n++;
|
e->n++;
|
||||||
@@ -627,8 +686,14 @@ getit(struct get_options *opt, const char *name, int argc, char **argv)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(i = 0; i < argc; i++)
|
for(i = 0; i < argc; i++) {
|
||||||
|
/* Loud tracing: announce each foreach_principal invocation */
|
||||||
|
krb5_warnx(context, "kadmin:get: getit: invoking foreach_principal for arg[%d]=%s (call %d of %d) (pid=%d)",
|
||||||
|
i, argv[i] ? argv[i] : "<null>", i+1, argc, (int)getpid());
|
||||||
ret = foreach_principal(argv[i], do_get_entry, name, &data);
|
ret = foreach_principal(argv[i], do_get_entry, name, &data);
|
||||||
|
if (ret)
|
||||||
|
krb5_warn(context, ret, "kadmin:get: getit: foreach_principal(%s) returned error", argv[i] ? argv[i] : "<null>");
|
||||||
|
}
|
||||||
|
|
||||||
kadm5_destroy(data.kadm_handle);
|
kadm5_destroy(data.kadm_handle);
|
||||||
|
|
||||||
|
|||||||
24
kadmin/rpc.c
24
kadmin/rpc.c
@@ -624,7 +624,31 @@ proc_get_principal(kadm5_server_context *contextp,
|
|||||||
if(ret)
|
if(ret)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
/* Loud tracing: log which client requested this GET and what principal is being looked up */
|
||||||
|
{
|
||||||
|
char *want = NULL;
|
||||||
|
(void) krb5_unparse_name(contextp->context, princ, &want);
|
||||||
|
krb5_warnx(contextp->context,
|
||||||
|
"proc_get_principal: received GET request for principal=%s mask=0x%x (pid=%d ppid=%d uid=%d)",
|
||||||
|
want ? want : "<unparsed>",
|
||||||
|
mask,
|
||||||
|
(int)getpid(),
|
||||||
|
(int)getppid(),
|
||||||
|
(int)getuid());
|
||||||
|
free(want);
|
||||||
|
}
|
||||||
|
|
||||||
ret = kadm5_get_principal(contextp, princ, &ent, mask);
|
ret = kadm5_get_principal(contextp, princ, &ent, mask);
|
||||||
|
if (ret) {
|
||||||
|
/* Extra log to make failures obvious at the call site */
|
||||||
|
char *want2 = NULL;
|
||||||
|
(void) krb5_unparse_name(contextp->context, princ, &want2);
|
||||||
|
krb5_warn(contextp->context, ret,
|
||||||
|
"proc_get_principal: kadm5_get_principal failed for principal=%s (pid=%d)",
|
||||||
|
want2 ? want2 : "<unparsed>",
|
||||||
|
(int)getpid());
|
||||||
|
free(want2);
|
||||||
|
}
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
krb5_warn(contextp->context, ret, "get principal principal");
|
krb5_warn(contextp->context, ret, "get principal principal");
|
||||||
|
|||||||
@@ -226,6 +226,8 @@ kadmind_dispatch_int(void *kadm_handlep, krb5_boolean initial,
|
|||||||
ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
|
ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
/* Debug: record which client and numeric command we received */
|
||||||
|
krb5_warnx(contextp->context, "kadmind_dispatch: received request from client=%s cmd=%d", client, cmd);
|
||||||
|
|
||||||
switch(cmd){
|
switch(cmd){
|
||||||
case kadm_nop:{
|
case kadm_nop:{
|
||||||
@@ -268,6 +270,8 @@ kadmind_dispatch_int(void *kadm_handlep, krb5_boolean initial,
|
|||||||
mask |= KADM5_PRINCIPAL;
|
mask |= KADM5_PRINCIPAL;
|
||||||
krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
|
krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
|
||||||
krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
|
krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
|
||||||
|
/* Debug: include mask/keys_ok context so we can correlate admin requests to keytab lookups */
|
||||||
|
krb5_warnx(contextp->context, "kadmind_dispatch: GET invoked by %s for %s (mask=0x%x, keys_ok=%d)", client, name, mask, keys_ok);
|
||||||
|
|
||||||
/* If the caller doesn't have KADM5_PRIV_GET, we're done. */
|
/* If the caller doesn't have KADM5_PRIV_GET, we're done. */
|
||||||
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
|
ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
|
||||||
@@ -307,6 +311,7 @@ kadmind_dispatch_int(void *kadm_handlep, krb5_boolean initial,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
krb5_warnx(contextp->context, "kadmind_dispatch: calling kadm5_get_principal for %s (mask=0x%x, keys_ok=%d)", name, mask, keys_ok);
|
||||||
ret = kadm5_get_principal(kadm_handlep, princ, &ent, mask);
|
ret = kadm5_get_principal(kadm_handlep, princ, &ent, mask);
|
||||||
ret_sp = krb5_store_int32(rsp, ret);
|
ret_sp = krb5_store_int32(rsp, ret);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
|
|||||||
@@ -149,10 +149,55 @@ hkt_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal,
|
|||||||
* enctypes should work.
|
* enctypes should work.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* Debug: record the requested principal and keytab context */
|
||||||
|
{
|
||||||
|
char *princ_name = NULL;
|
||||||
|
if (krb5_unparse_name(context, principal, &princ_name) == 0) {
|
||||||
|
krb5_warnx(context, "hkt_fetch_kvno: looking up principal=%s in keytab=%s kvno=%u flags=0x%x",
|
||||||
|
princ_name ? princ_name : "<unparsed>",
|
||||||
|
k->path ? k->path : "<unknown>",
|
||||||
|
(unsigned)kvno,
|
||||||
|
(unsigned)flags);
|
||||||
|
} else {
|
||||||
|
krb5_warnx(context, "hkt_fetch_kvno: looking up <unparsed principal> in keytab=%s kvno=%u flags=0x%x",
|
||||||
|
k->path ? k->path : "<unknown>",
|
||||||
|
(unsigned)kvno,
|
||||||
|
(unsigned)flags);
|
||||||
|
}
|
||||||
|
free(princ_name);
|
||||||
|
}
|
||||||
|
|
||||||
ret = krb5_kt_get_entry(context, k->keytab, principal, kvno, 0, &ktentry);
|
ret = krb5_kt_get_entry(context, k->keytab, principal, kvno, 0, &ktentry);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
char *princ_name2 = NULL;
|
||||||
|
if (krb5_unparse_name(context, principal, &princ_name2) == 0) {
|
||||||
|
krb5_warnx(context, "hkt_fetch_kvno: krb5_kt_get_entry returned %d for principal=%s keytab=%s",
|
||||||
|
ret, princ_name2 ? princ_name2 : "<unparsed>", k->path ? k->path : "<unknown>");
|
||||||
|
} else {
|
||||||
|
krb5_warnx(context, "hkt_fetch_kvno: krb5_kt_get_entry returned %d for <unparsed principal> keytab=%s",
|
||||||
|
ret, k->path ? k->path : "<unknown>");
|
||||||
|
}
|
||||||
|
free(princ_name2);
|
||||||
ret = HDB_ERR_NOENTRY;
|
ret = HDB_ERR_NOENTRY;
|
||||||
goto out;
|
goto out;
|
||||||
|
} else {
|
||||||
|
/* Success: record what the keytab returned for inspection */
|
||||||
|
{
|
||||||
|
char *entry_princ = NULL;
|
||||||
|
if (krb5_unparse_name(context, ktentry.principal, &entry_princ) == 0) {
|
||||||
|
krb5_warnx(context, "hkt_fetch_kvno: krb5_kt_get_entry succeeded: entry_principal=%s vno=%d enctype=%d keytab=%s",
|
||||||
|
entry_princ ? entry_princ : "<unparsed>",
|
||||||
|
ktentry.vno,
|
||||||
|
ktentry.keyblock.keytype,
|
||||||
|
k->path ? k->path : "<unknown>");
|
||||||
|
} else {
|
||||||
|
krb5_warnx(context, "hkt_fetch_kvno: krb5_kt_get_entry succeeded: entry_principal=<unparsed> vno=%d enctype=%d keytab=%s",
|
||||||
|
ktentry.vno,
|
||||||
|
ktentry.keyblock.keytype,
|
||||||
|
k->path ? k->path : "<unknown>");
|
||||||
|
}
|
||||||
|
free(entry_princ);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = krb5_copy_principal(context, principal, &entry->principal);
|
ret = krb5_copy_principal(context, principal, &entry->principal);
|
||||||
|
|||||||
@@ -53,17 +53,6 @@
|
|||||||
#define LOG_VERSION_FIRST 1
|
#define LOG_VERSION_FIRST 1
|
||||||
#define LOG_VERSION_UBER 0
|
#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>
|
#include <krb5.h>
|
||||||
|
|
||||||
#define KRB5_KDB_DISALLOW_POSTDATED 0x00000001
|
#define KRB5_KDB_DISALLOW_POSTDATED 0x00000001
|
||||||
|
|||||||
@@ -191,6 +191,20 @@ _kadm5_c_init_context(kadm5_client_context **ctx,
|
|||||||
}
|
}
|
||||||
if ((*ctx)->readonly_kadmind_port == 0)
|
if ((*ctx)->readonly_kadmind_port == 0)
|
||||||
(*ctx)->readonly_kadmind_port = (*ctx)->kadmind_port;
|
(*ctx)->readonly_kadmind_port = (*ctx)->kadmind_port;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extra debug: emit context-level info to help trace client init-time
|
||||||
|
* decisions in test logs.
|
||||||
|
*/
|
||||||
|
krb5_warnx(context,
|
||||||
|
"_kadm5_c_init_context: realm=%s admin_server=%s readonly_admin_server=%s kadmind_port=%u readonly_port=%u (pid=%d)",
|
||||||
|
(*ctx)->realm ? (*ctx)->realm : "<null>",
|
||||||
|
(*ctx)->admin_server ? (*ctx)->admin_server : "<null>",
|
||||||
|
(*ctx)->readonly_admin_server ? (*ctx)->readonly_admin_server : "<null>",
|
||||||
|
(unsigned)ntohs((*ctx)->kadmind_port),
|
||||||
|
(unsigned)ntohs((*ctx)->readonly_kadmind_port),
|
||||||
|
(int)getpid());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,10 +217,12 @@ kadm5_c_dup_context(void *vin, void **out)
|
|||||||
kadm5_client_context *ctx;
|
kadm5_client_context *ctx;
|
||||||
|
|
||||||
*out = NULL;
|
*out = NULL;
|
||||||
ctx = calloc(1, sizeof(*ctx));
|
ctx = malloc(sizeof(*ctx));
|
||||||
if (ctx == NULL)
|
if (ctx == NULL)
|
||||||
return krb5_enomem(in->context);
|
return krb5_enomem(in->context);
|
||||||
|
|
||||||
|
|
||||||
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
set_funcs(ctx);
|
set_funcs(ctx);
|
||||||
ctx->readonly_kadmind_port = in->readonly_kadmind_port;
|
ctx->readonly_kadmind_port = in->readonly_kadmind_port;
|
||||||
ctx->kadmind_port = in->kadmind_port;
|
ctx->kadmind_port = in->kadmind_port;
|
||||||
@@ -224,13 +240,18 @@ kadm5_c_dup_context(void *vin, void **out)
|
|||||||
if (in->readonly_admin_server &&
|
if (in->readonly_admin_server &&
|
||||||
(ctx->readonly_admin_server = strdup(in->readonly_admin_server)) == NULL)
|
(ctx->readonly_admin_server = strdup(in->readonly_admin_server)) == NULL)
|
||||||
ret = krb5_enomem(context);
|
ret = krb5_enomem(context);
|
||||||
if (in->client_name != NULL &&
|
|
||||||
(ctx->client_name = strdup(in->client_name)) == NULL)
|
/* Preserve client identity and prompter behavior in duplicated context */
|
||||||
|
if (in->client_name && (ctx->client_name = strdup(in->client_name)) == NULL)
|
||||||
ret = krb5_enomem(context);
|
ret = krb5_enomem(context);
|
||||||
if (in->keytab && (ctx->keytab = strdup(in->keytab)) == NULL)
|
if (in->service_name && (ctx->service_name = strdup(in->service_name)) == NULL)
|
||||||
ret = krb5_enomem(context);
|
ret = krb5_enomem(context);
|
||||||
|
/* Copy the prompter pointer so duplicated contexts behave the same
|
||||||
|
with respect to prompting vs keytab-based auth. */
|
||||||
ctx->prompter = in->prompter;
|
ctx->prompter = in->prompter;
|
||||||
|
|
||||||
|
if (in->keytab && (ctx->keytab = strdup(in->keytab)) == NULL)
|
||||||
|
ret = krb5_enomem(context);
|
||||||
if (in->ccache) {
|
if (in->ccache) {
|
||||||
char *fullname = NULL;
|
char *fullname = NULL;
|
||||||
|
|
||||||
@@ -295,7 +316,24 @@ get_new_cache(krb5_context context,
|
|||||||
krb5_get_init_creds_opt_set_forwardable (opt, FALSE);
|
krb5_get_init_creds_opt_set_forwardable (opt, FALSE);
|
||||||
krb5_get_init_creds_opt_set_proxiable (opt, FALSE);
|
krb5_get_init_creds_opt_set_proxiable (opt, FALSE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extra debug: record the inputs to credential selection so tests can
|
||||||
|
* observe why keytab vs password paths were chosen.
|
||||||
|
*/
|
||||||
|
krb5_warnx(context,
|
||||||
|
"get_new_cache: entry client=%s password_provided=%d prompter=%p keytab=%s server_name=%s (pid=%d)",
|
||||||
|
client ? krb5_principal_get_comp_string(context, client, 0) : "<null>",
|
||||||
|
password ? 1 : 0,
|
||||||
|
(void *)prompter,
|
||||||
|
keytab ? keytab : "<default>",
|
||||||
|
server_name ? server_name : "<null>",
|
||||||
|
(int)getpid());
|
||||||
|
|
||||||
if(password == NULL && prompter == NULL) {
|
if(password == NULL && prompter == NULL) {
|
||||||
|
/* Debug: choosing keytab-based initial credentials */
|
||||||
|
krb5_warnx(context, "get_new_cache: choosing keytab auth (keytab=%s, client=%s)",
|
||||||
|
keytab ? keytab : "<default>",
|
||||||
|
client ? krb5_principal_get_comp_string(context, client, 0) : "<null>");
|
||||||
krb5_keytab kt;
|
krb5_keytab kt;
|
||||||
if(keytab == NULL)
|
if(keytab == NULL)
|
||||||
ret = krb5_kt_default(context, &kt);
|
ret = krb5_kt_default(context, &kt);
|
||||||
@@ -312,8 +350,21 @@ get_new_cache(krb5_context context,
|
|||||||
0,
|
0,
|
||||||
server_name,
|
server_name,
|
||||||
opt);
|
opt);
|
||||||
|
/* Log success/failure of keytab credential attempt for diagnostics */
|
||||||
|
if (ret == 0) {
|
||||||
|
krb5_warnx(context, "get_new_cache: krb5_get_init_creds_keytab succeeded for principal=%s using keytab=%s",
|
||||||
|
client ? krb5_principal_get_comp_string(context, client, 0) : "<null>",
|
||||||
|
keytab ? keytab : "<default>");
|
||||||
|
} else {
|
||||||
|
krb5_warn(context, ret, "get_new_cache: krb5_get_init_creds_keytab failed (keytab=%s)", keytab ? keytab : "<default>");
|
||||||
|
}
|
||||||
krb5_kt_close(context, kt);
|
krb5_kt_close(context, kt);
|
||||||
} else {
|
} else {
|
||||||
|
/* Debug: choosing password-based initial credentials */
|
||||||
|
krb5_warnx(context, "get_new_cache: choosing password auth (password_provided=%d prompter=%p client=%s)",
|
||||||
|
password ? 1 : 0,
|
||||||
|
(void *)prompter,
|
||||||
|
client ? krb5_principal_get_comp_string(context, client, 0) : "<null>");
|
||||||
ret = krb5_get_init_creds_password (context,
|
ret = krb5_get_init_creds_password (context,
|
||||||
&cred,
|
&cred,
|
||||||
client,
|
client,
|
||||||
@@ -323,6 +374,13 @@ get_new_cache(krb5_context context,
|
|||||||
0,
|
0,
|
||||||
server_name,
|
server_name,
|
||||||
opt);
|
opt);
|
||||||
|
if (ret == 0) {
|
||||||
|
krb5_warnx(context, "get_new_cache: krb5_get_init_creds_password succeeded for principal=%s",
|
||||||
|
client ? krb5_principal_get_comp_string(context, client, 0) : "<null>");
|
||||||
|
} else {
|
||||||
|
krb5_warn(context, ret, "get_new_cache: krb5_get_init_creds_password failed for principal=%s",
|
||||||
|
client ? krb5_principal_get_comp_string(context, client, 0) : "<null>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
krb5_get_init_creds_opt_free(context, opt);
|
krb5_get_init_creds_opt_free(context, opt);
|
||||||
switch(ret){
|
switch(ret){
|
||||||
@@ -451,6 +509,27 @@ _kadm5_c_get_cred_cache(krb5_context context,
|
|||||||
if(server_name == NULL)
|
if(server_name == NULL)
|
||||||
server_name = KADM5_ADMIN_SERVICE;
|
server_name = KADM5_ADMIN_SERVICE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extra debug: log the invocation and environment so we can correlate
|
||||||
|
* with test harness behavior (e.g. whether KRB5_KTNAME / KRB5CCNAME
|
||||||
|
* were set).
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
const char *env_kt = secure_getenv ? secure_getenv("KRB5_KTNAME") : getenv("KRB5_KTNAME");
|
||||||
|
const char *env_cc = secure_getenv ? secure_getenv("KRB5CCNAME") : getenv("KRB5CCNAME");
|
||||||
|
krb5_warnx(context,
|
||||||
|
"_kadm5_c_get_cred_cache: called client_name=%s server_name=%s password_provided=%d prompter=%p keytab=%s ccache=%p env.KRB5_KTNAME=%s env.KRB5CCNAME=%s (pid=%d)",
|
||||||
|
client_name ? client_name : "<null>",
|
||||||
|
server_name ? server_name : "<null>",
|
||||||
|
password ? 1 : 0,
|
||||||
|
(void *)prompter,
|
||||||
|
keytab ? keytab : "<null>",
|
||||||
|
(void *)ccache,
|
||||||
|
env_kt ? env_kt : "<unset>",
|
||||||
|
env_cc ? env_cc : "<unset>",
|
||||||
|
(int)getpid());
|
||||||
|
}
|
||||||
|
|
||||||
if(client_name != NULL) {
|
if(client_name != NULL) {
|
||||||
ret = krb5_parse_name(context, client_name, &client);
|
ret = krb5_parse_name(context, client_name, &client);
|
||||||
if(ret)
|
if(ret)
|
||||||
@@ -458,17 +537,7 @@ _kadm5_c_get_cred_cache(krb5_context context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(ccache != NULL) {
|
if(ccache != NULL) {
|
||||||
char *fullname = NULL;
|
id = ccache;
|
||||||
|
|
||||||
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);
|
ret = krb5_cc_get_principal(context, id, &client);
|
||||||
if(ret)
|
if(ret)
|
||||||
return ret;
|
return ret;
|
||||||
@@ -515,26 +584,47 @@ _kadm5_c_get_cred_cache(krb5_context context,
|
|||||||
|
|
||||||
if(id && client && (default_client == NULL ||
|
if(id && client && (default_client == NULL ||
|
||||||
krb5_principal_compare(context, client, default_client) != 0)) {
|
krb5_principal_compare(context, client, default_client) != 0)) {
|
||||||
|
/* Debug: attempting to obtain kadmin ticket from existing credential cache */
|
||||||
|
krb5_warnx(context, "_kadm5_c_get_cred_cache: trying credential cache to get kadmin ticket (client=%s)",
|
||||||
|
client ? krb5_principal_get_comp_string(context, client, 0) : "<null>");
|
||||||
ret = get_kadm_ticket(context, id, client, server_name);
|
ret = get_kadm_ticket(context, id, client, server_name);
|
||||||
if(ret == 0) {
|
if(ret == 0) {
|
||||||
|
krb5_warnx(context, "_kadm5_c_get_cred_cache: got kadmin ticket from cache for client=%s",
|
||||||
|
client ? krb5_principal_get_comp_string(context, client, 0) : "<null>");
|
||||||
*ret_cache = id;
|
*ret_cache = id;
|
||||||
krb5_free_principal(context, default_client);
|
krb5_free_principal(context, default_client);
|
||||||
if (default_client != client)
|
if (default_client != client)
|
||||||
krb5_free_principal(context, client);
|
krb5_free_principal(context, client);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
krb5_warn(context, ret, "_kadm5_c_get_cred_cache: get_kadm_ticket from cache failed for client=%s", client ? krb5_principal_get_comp_string(context, client, 0) : "<null>");
|
||||||
if(ccache != NULL)
|
if(ccache != NULL)
|
||||||
/* couldn't get ticket from cache */
|
/* couldn't get ticket from cache */
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
/* get creds via AS request */
|
/* get creds via AS request */
|
||||||
if (id)
|
if(id && (id != ccache))
|
||||||
krb5_cc_close(context, id);
|
krb5_cc_close(context, id);
|
||||||
if (client != default_client)
|
if (client != default_client)
|
||||||
krb5_free_principal(context, default_client);
|
krb5_free_principal(context, default_client);
|
||||||
|
|
||||||
ret = get_new_cache(context, client, password, prompter, keytab,
|
ret = get_new_cache(context, client, password, prompter, keytab,
|
||||||
server_name, ret_cache);
|
server_name, ret_cache);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extra debug: report the result of get_new_cache so caller logs
|
||||||
|
* can show why auth succeeded/failed.
|
||||||
|
*/
|
||||||
|
if (ret == 0) {
|
||||||
|
krb5_warnx(context, "_kadm5_c_get_cred_cache: get_new_cache succeeded for client=%s (pid=%d)",
|
||||||
|
client ? krb5_principal_get_comp_string(context, client, 0) : "<null>",
|
||||||
|
(int)getpid());
|
||||||
|
} else {
|
||||||
|
krb5_warn(context, ret, "_kadm5_c_get_cred_cache: get_new_cache failed for client=%s (pid=%d)",
|
||||||
|
client ? krb5_principal_get_comp_string(context, client, 0) : "<null>",
|
||||||
|
(int)getpid());
|
||||||
|
}
|
||||||
|
|
||||||
krb5_free_principal(context, client);
|
krb5_free_principal(context, client);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -558,6 +648,17 @@ kadm_connect(kadm5_client_context *ctx)
|
|||||||
krb5_context context = ctx->context;
|
krb5_context context = ctx->context;
|
||||||
int writable = 0;
|
int writable = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extra debug: record connect intent and client context pointers
|
||||||
|
* to help correlate kadmin network activity with credential selection.
|
||||||
|
*/
|
||||||
|
krb5_warnx(context, "kadm_connect: entering (client_name=%s prompter=%p keytab=%s ccache=%p pid=%d)",
|
||||||
|
ctx->client_name ? ctx->client_name : "<null>",
|
||||||
|
(void *)ctx->prompter,
|
||||||
|
ctx->keytab ? ctx->keytab : "<null>",
|
||||||
|
(void *)ctx->ccache,
|
||||||
|
(int)getpid());
|
||||||
|
|
||||||
if (ctx->ac)
|
if (ctx->ac)
|
||||||
krb5_auth_con_free(context, ctx->ac);
|
krb5_auth_con_free(context, ctx->ac);
|
||||||
ctx->ac = NULL;
|
ctx->ac = NULL;
|
||||||
@@ -671,6 +772,12 @@ out:
|
|||||||
rk_closesocket(s);
|
rk_closesocket(s);
|
||||||
krb5_auth_con_free(context, ctx->ac);
|
krb5_auth_con_free(context, ctx->ac);
|
||||||
ctx->ac = NULL;
|
ctx->ac = NULL;
|
||||||
|
} else {
|
||||||
|
/* Extra debug: successful connect and auth */
|
||||||
|
krb5_warnx(context, "kadm_connect: success connected_to_writable=%d sock=%d (pid=%d)",
|
||||||
|
(int)ctx->connected_to_writable,
|
||||||
|
(int)ctx->sock,
|
||||||
|
(int)getpid());
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -717,12 +824,25 @@ kadm5_c_init_with_context(krb5_context context,
|
|||||||
kadm5_client_context *ctx = NULL;
|
kadm5_client_context *ctx = NULL;
|
||||||
krb5_ccache cc;
|
krb5_ccache cc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extra debug: record initialization flags and pointers so we can later
|
||||||
|
* understand whether a prompter was provided (which affects keytab vs
|
||||||
|
* password selection) and whether a keytab or ccache was requested.
|
||||||
|
*/
|
||||||
|
krb5_warnx(context, "kadm5_c_init_with_context: client_name=%s password_provided=%d prompter=%p keytab=%s ccache=%p service_name=%s (pid=%d)",
|
||||||
|
client_name ? client_name : "<null>",
|
||||||
|
password ? 1 : 0,
|
||||||
|
(void *)prompter,
|
||||||
|
keytab ? keytab : "<null>",
|
||||||
|
(void *)ccache,
|
||||||
|
service_name ? service_name : "<null>",
|
||||||
|
(int)getpid());
|
||||||
|
|
||||||
ret = _kadm5_c_init_context(&ctx, realm_params, context);
|
ret = _kadm5_c_init_context(&ctx, realm_params, context);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if ((password != NULL && *password != '\0') ||
|
if (password != NULL && *password != '\0') {
|
||||||
(client_name && !keytab)) {
|
|
||||||
ret = _kadm5_c_get_cred_cache(context,
|
ret = _kadm5_c_get_cred_cache(context,
|
||||||
client_name,
|
client_name,
|
||||||
service_name,
|
service_name,
|
||||||
@@ -750,6 +870,19 @@ kadm5_c_init_with_context(krb5_context context,
|
|||||||
ctx->sock = -1;
|
ctx->sock = -1;
|
||||||
|
|
||||||
*server_handle = ctx;
|
*server_handle = ctx;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extra debug: final context values after init so logs show what the
|
||||||
|
* kadm client will carry into subsequent connect attempts.
|
||||||
|
*/
|
||||||
|
krb5_warnx(context, "kadm5_c_init_with_context: done ctx=%p client_name=%s prompter=%p keytab=%s ccache=%p (pid=%d)",
|
||||||
|
(void *)ctx,
|
||||||
|
ctx->client_name ? ctx->client_name : "<null>",
|
||||||
|
(void *)ctx->prompter,
|
||||||
|
ctx->keytab ? ctx->keytab : "<null>",
|
||||||
|
(void *)ctx->ccache,
|
||||||
|
(int)getpid());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -936,4 +1069,3 @@ kadm5_init(char *client_name, char *pass,
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ struct slave {
|
|||||||
krb5_auth_context ac;
|
krb5_auth_context ac;
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
uint32_t version_tstamp;
|
uint32_t version_tstamp;
|
||||||
|
uint32_t version_ack;
|
||||||
time_t seen;
|
time_t seen;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
#define SLAVE_F_DEAD 0x1
|
#define SLAVE_F_DEAD 0x1
|
||||||
@@ -375,6 +376,7 @@ add_slave (krb5_context context, krb5_keytab keytab, slave **root,
|
|||||||
krb5_warnx (context, "connection from %s", s->name);
|
krb5_warnx (context, "connection from %s", s->name);
|
||||||
|
|
||||||
s->version = 0;
|
s->version = 0;
|
||||||
|
s->version_ack = 0;
|
||||||
s->flags = 0;
|
s->flags = 0;
|
||||||
slave_seen(s);
|
slave_seen(s);
|
||||||
s->next = *root;
|
s->next = *root;
|
||||||
@@ -860,11 +862,8 @@ diffready(krb5_context context, slave *s)
|
|||||||
* Don't send any diffs until slave has sent an I_HAVE telling us the
|
* Don't send any diffs until slave has sent an I_HAVE telling us the
|
||||||
* initial version number!
|
* 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;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (s->flags & SLAVE_F_DEAD) {
|
if (s->flags & SLAVE_F_DEAD) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
@@ -873,11 +872,8 @@ diffready(krb5_context context, slave *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Write any remainder of previous write, if we can. */
|
/* Write any remainder of previous write, if we can. */
|
||||||
if (send_tail(context, s) != 0 && have_tail(s)) {
|
if (send_tail(context, s) != 0)
|
||||||
if (verbose)
|
|
||||||
krb5_warnx(context, "not sending diffs to busy slave %s", s->name);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -885,6 +881,10 @@ diffready(krb5_context context, slave *s)
|
|||||||
static int
|
static int
|
||||||
nodiffs(krb5_context context, slave *s, uint32_t current_version)
|
nodiffs(krb5_context context, slave *s, uint32_t current_version)
|
||||||
{
|
{
|
||||||
|
krb5_storage *sp;
|
||||||
|
krb5_data data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (s->version < current_version)
|
if (s->version < current_version)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -897,20 +897,6 @@ nodiffs(krb5_context context, slave *s, uint32_t current_version)
|
|||||||
if (verbose)
|
if (verbose)
|
||||||
krb5_warnx(context, "slave %s version %ld already sent", s->name,
|
krb5_warnx(context, "slave %s version %ld already sent", s->name,
|
||||||
(long)s->version);
|
(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();
|
sp = krb5_storage_emem();
|
||||||
if (sp == NULL)
|
if (sp == NULL)
|
||||||
krb5_errx(context, IPROPD_RESTART, "krb5_storage_from_mem");
|
krb5_errx(context, IPROPD_RESTART, "krb5_storage_from_mem");
|
||||||
@@ -923,17 +909,19 @@ send_you_have_last_version(krb5_context context, slave *s)
|
|||||||
krb5_storage_free(sp);
|
krb5_storage_free(sp);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
ret = mk_priv_tail(context, s, &data);
|
ret = mk_priv_tail(context, s, &data);
|
||||||
if (ret)
|
|
||||||
krb5_err(context, IPROPD_RESTART, ret,
|
|
||||||
"sending you have last version");
|
|
||||||
krb5_data_free(&data);
|
krb5_data_free(&data);
|
||||||
}
|
}
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
send_tail(context, s);
|
send_tail(context, s);
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lock the log and return initial version and timestamp
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
get_first(kadm5_server_context *server_context,
|
get_first(kadm5_server_context *server_context, int log_fd,
|
||||||
uint32_t *initial_verp, uint32_t *initial_timep)
|
uint32_t *initial_verp, uint32_t *initial_timep)
|
||||||
{
|
{
|
||||||
krb5_context context = server_context->context;
|
krb5_context context = server_context->context;
|
||||||
@@ -943,14 +931,19 @@ get_first(kadm5_server_context *server_context,
|
|||||||
* We don't want to perform tight retry loops on log access errors, so on
|
* 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...
|
* 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, -1, LOG_VERSION_FIRST,
|
ret = kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_FIRST,
|
||||||
initial_verp, initial_timep);
|
initial_verp, initial_timep);
|
||||||
if (ret == HEIM_ERR_EOF || ret == KADM5_LOG_EMPTY)
|
if (ret == HEIM_ERR_EOF)
|
||||||
ret = kadm5_log_get_version_fd(server_context, -1,
|
ret = kadm5_log_get_version_fd(server_context, log_fd,
|
||||||
LOG_VERSION_UBER, initial_verp,
|
LOG_VERSION_UBER, initial_verp,
|
||||||
initial_timep);
|
initial_timep);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
|
flock(log_fd, LOCK_UN);
|
||||||
krb5_warn(context, ret, "could not read initial log entry");
|
krb5_warn(context, ret, "could not read initial log entry");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -961,20 +954,17 @@ get_first(kadm5_server_context *server_context,
|
|||||||
/*-
|
/*-
|
||||||
* Find the left end of the diffs in the log we want to send.
|
* 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.
|
* - On success, return a positive offset to the first new entry, retaining
|
||||||
* - On error, return a negative offset.
|
* a read lock on the log file.
|
||||||
|
* - On error, return a negative offset, with the lock released.
|
||||||
* - If we simply find no successor entry in the log, return zero
|
* - If we simply find no successor entry in the log, return zero
|
||||||
* with the lock released, which indicates that fallback to send_complete()
|
* with the lock released, which indicates that fallback to send_complete()
|
||||||
* is needed.
|
* 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
|
static off_t
|
||||||
get_left(kadm5_server_context *server_context, slave *s, krb5_storage *sp,
|
get_left(kadm5_server_context *server_context, slave *s, krb5_storage *sp,
|
||||||
uint32_t current_version, uint32_t *initial_verp,
|
int log_fd, uint32_t current_version,
|
||||||
uint32_t *initial_timep)
|
uint32_t *initial_verp, uint32_t *initial_timep)
|
||||||
{
|
{
|
||||||
krb5_context context = server_context->context;
|
krb5_context context = server_context->context;
|
||||||
off_t pos;
|
off_t pos;
|
||||||
@@ -984,13 +974,17 @@ get_left(kadm5_server_context *server_context, slave *s, krb5_storage *sp,
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
uint32_t ver = s->version;
|
uint32_t ver = s->version;
|
||||||
|
|
||||||
ret = get_first(server_context, initial_verp, initial_timep);
|
/* This acquires a read lock on success */
|
||||||
|
ret = get_first(server_context, log_fd,
|
||||||
|
initial_verp, initial_timep);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* When the slave version is out of range, send the whole database. */
|
/* When the slave version is out of range, send the whole database. */
|
||||||
if (ver == 0 || ver + 1 < *initial_verp || ver > current_version)
|
if (ver == 0 || ver < *initial_verp || ver > current_version) {
|
||||||
|
flock(log_fd, LOCK_UN);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Avoid seeking past the last committed record */
|
/* Avoid seeking past the last committed record */
|
||||||
if (kadm5_log_goto_end(server_context, sp) != 0 ||
|
if (kadm5_log_goto_end(server_context, sp) != 0 ||
|
||||||
@@ -1022,12 +1016,13 @@ get_left(kadm5_server_context *server_context, slave *s, krb5_storage *sp,
|
|||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Slow path: seek backwards, entry by entry, from the end.
|
* 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
|
||||||
* Try to find the left entry by seeking backward from the end of the
|
* lock, update "next_diff", and retry the fast-path.
|
||||||
* 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 (;;) {
|
for (;;) {
|
||||||
enum kadm_ops op;
|
enum kadm_ops op;
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
@@ -1043,7 +1038,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
|
* We don't expect to reach the slave's version, unless the log
|
||||||
* has been rotated.
|
* has been modified after we released the lock.
|
||||||
*/
|
*/
|
||||||
if (ver == s->version) {
|
if (ver == s->version) {
|
||||||
krb5_warnx(context, "iprop log truncated while sending diffs "
|
krb5_warnx(context, "iprop log truncated while sending diffs "
|
||||||
@@ -1065,28 +1060,33 @@ 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
|
* If we loop then we're hoping to hit the fast path so we can return a
|
||||||
* non-zero, positive left offset.
|
* non-zero, positive left offset with the lock held.
|
||||||
*
|
*
|
||||||
* We just updated the fast path pre-conditions, so unless a log
|
* We just updated the fast path pre-conditions, so unless a log
|
||||||
* truncation event happens again, we will hit the fast path.
|
* 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.
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
flock(log_fd, LOCK_UN);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static off_t
|
static off_t
|
||||||
get_right(krb5_context context, krb5_storage *sp, int lastver, slave *s,
|
get_right(krb5_context context, int log_fd, krb5_storage *sp,
|
||||||
off_t left, uint32_t *verp)
|
int lastver, slave *s, off_t left, uint32_t *verp)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
uint32_t ver = s->version;
|
uint32_t ver = s->version;
|
||||||
off_t right = krb5_storage_seek(sp, left, SEEK_SET);
|
off_t right = krb5_storage_seek(sp, left, SEEK_SET);
|
||||||
|
|
||||||
if (right <= 0)
|
if (right <= 0) {
|
||||||
|
flock(log_fd, LOCK_UN);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* The "lastver" bound should preclude us reaching EOF */
|
/* The "lastver" bound should preclude us reaching EOF */
|
||||||
for (; ret == 0 && i < SEND_DIFFS_MAX_RECORDS && ver < lastver; ++i) {
|
for (; ret == 0 && i < SEND_DIFFS_MAX_RECORDS && ver < lastver; ++i) {
|
||||||
@@ -1101,23 +1101,18 @@ get_right(krb5_context context, krb5_storage *sp, int lastver, slave *s,
|
|||||||
right = krb5_storage_seek(sp, 0, SEEK_CUR);
|
right = krb5_storage_seek(sp, 0, SEEK_CUR);
|
||||||
else
|
else
|
||||||
right = -1;
|
right = -1;
|
||||||
if (right <= 0)
|
if (right <= 0) {
|
||||||
|
flock(log_fd, LOCK_UN);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
*verp = ver;
|
*verp = ver;
|
||||||
return right;
|
return right;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void
|
||||||
* This function _must_ send something to the client unless it's dead, and if
|
send_diffs(kadm5_server_context *server_context, slave *s, int log_fd,
|
||||||
* 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)
|
const char *database, uint32_t current_version)
|
||||||
{
|
{
|
||||||
struct kadm5_log_snap snap;
|
|
||||||
krb5_context context = server_context->context;
|
krb5_context context = server_context->context;
|
||||||
krb5_storage *sp;
|
krb5_storage *sp;
|
||||||
uint32_t initial_version;
|
uint32_t initial_version;
|
||||||
@@ -1127,122 +1122,84 @@ send_diffs(kadm5_server_context *server_context, slave *s,
|
|||||||
off_t right = 0;
|
off_t right = 0;
|
||||||
krb5_ssize_t bytes;
|
krb5_ssize_t bytes;
|
||||||
krb5_data data;
|
krb5_data data;
|
||||||
kadm5_ret_t ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/*
|
if (!diffready(context, s) || nodiffs(context, s, current_version))
|
||||||
* diffready() can send part of an extant buffer, and does nothing if the
|
return;
|
||||||
* 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)
|
if (verbose)
|
||||||
krb5_warnx(context, "sending diffs to live-seeming slave %s", s->name);
|
krb5_warnx(context, "sending diffs to live-seeming slave %s", s->name);
|
||||||
|
|
||||||
ret = kadm5_log_snapshot(server_context, &snap);
|
sp = krb5_storage_from_fd(log_fd);
|
||||||
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)
|
if (sp == NULL)
|
||||||
krb5_err(context, IPROPD_RESTART_SLOW, ENOMEM,
|
krb5_err(context, IPROPD_RESTART_SLOW, ENOMEM,
|
||||||
"send_diffs: out of memory");
|
"send_diffs: out of memory");
|
||||||
|
|
||||||
left = get_left(server_context, s, sp, current_version,
|
left = get_left(server_context, s, sp, log_fd, current_version,
|
||||||
&initial_version, &initial_tstamp);
|
&initial_version, &initial_tstamp);
|
||||||
if (left < 0) {
|
if (left < 0) {
|
||||||
/* Some sort of error; fallback on full prop */
|
|
||||||
krb5_storage_free(sp);
|
krb5_storage_free(sp);
|
||||||
if (verbose)
|
slave_dead(context, s);
|
||||||
krb5_warnx(context, "sending full prop to slave %s "
|
return;
|
||||||
"(could not find offset for incremental)", s->name);
|
|
||||||
send_complete(context, s, database, current_version,
|
|
||||||
initial_version, initial_tstamp);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (left == 0) {
|
if (left == 0) {
|
||||||
/* Slave's version is not in the log, fall back on send_complete() */
|
/* Slave's version is not in the log, fall back on send_complete() */
|
||||||
krb5_storage_free(sp);
|
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,
|
send_complete(context, s, database, current_version,
|
||||||
initial_version, initial_tstamp);
|
initial_version, initial_tstamp);
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
right = get_right(server_context->context, sp, current_version,
|
/* We still hold the read lock, if right > 0 */
|
||||||
|
right = get_right(server_context->context, log_fd, sp, current_version,
|
||||||
s, left, &ver);
|
s, left, &ver);
|
||||||
if (right == left) {
|
if (right == left) {
|
||||||
/* Shouldn't happen */
|
flock(log_fd, LOCK_UN);
|
||||||
krb5_storage_free(sp);
|
krb5_storage_free(sp);
|
||||||
if (verbose)
|
return;
|
||||||
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) {
|
if (right < left) {
|
||||||
assert(right < 0);
|
assert(right < 0);
|
||||||
krb5_storage_free(sp);
|
krb5_storage_free(sp);
|
||||||
slave_dead(context, s);
|
slave_dead(context, s);
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (krb5_storage_seek(sp, left, SEEK_SET) != left) {
|
if (krb5_storage_seek(sp, left, SEEK_SET) != left) {
|
||||||
krb5_warn(context, errno ? errno : EIO, "send_diffs: krb5_storage_seek");
|
ret = errno ? errno : EIO;
|
||||||
|
flock(log_fd, LOCK_UN);
|
||||||
|
krb5_warn(context, ret, "send_diffs: krb5_storage_seek");
|
||||||
krb5_storage_free(sp);
|
krb5_storage_free(sp);
|
||||||
slave_dead(context, s);
|
slave_dead(context, s);
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = krb5_data_alloc(&data, right - left + 4);
|
ret = krb5_data_alloc(&data, right - left + 4);
|
||||||
if (ret)
|
if (ret) {
|
||||||
krb5_err(context, IPROPD_RESTART_SLOW, ENOMEM, "out of memory");
|
flock(log_fd, LOCK_UN);
|
||||||
|
krb5_warn(context, ret, "send_diffs: krb5_data_alloc");
|
||||||
|
krb5_storage_free(sp);
|
||||||
|
slave_dead(context, s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bytes = krb5_storage_read(sp, (char *)data.data + 4, data.length - 4);
|
bytes = krb5_storage_read(sp, (char *)data.data + 4, data.length - 4);
|
||||||
|
flock(log_fd, LOCK_UN);
|
||||||
krb5_storage_free(sp);
|
krb5_storage_free(sp);
|
||||||
if (bytes != data.length - 4) {
|
if (bytes != data.length - 4)
|
||||||
krb5_warnx(context, "send_diffs: log rotated");
|
krb5_errx(context, IPROPD_RESTART, "locked log truncated???");
|
||||||
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);
|
sp = krb5_storage_from_data(&data);
|
||||||
if (sp == NULL)
|
if (sp == NULL) {
|
||||||
krb5_err(context, IPROPD_RESTART_SLOW, ENOMEM, "out of memory");
|
krb5_err(context, IPROPD_RESTART_SLOW, ENOMEM, "out of memory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
ret = krb5_store_uint32(sp, FOR_YOU);
|
ret = krb5_store_uint32(sp, FOR_YOU);
|
||||||
krb5_storage_free(sp);
|
krb5_storage_free(sp);
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0)
|
||||||
ret = mk_priv_tail(context, s, &data);
|
ret = mk_priv_tail(context, s, &data);
|
||||||
if (ret == ENOMEM)
|
|
||||||
krb5_err(context, IPROPD_RESTART_SLOW, ENOMEM, "out of memory");
|
|
||||||
}
|
|
||||||
krb5_data_free(&data);
|
krb5_data_free(&data);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
/* Save the fast-path continuation */
|
/* Save the fast-path continuation */
|
||||||
@@ -1264,10 +1221,10 @@ send_diffs(kadm5_server_context *server_context, slave *s,
|
|||||||
krb5_warn(context, ret, "send_diffs: making or sending "
|
krb5_warn(context, ret, "send_diffs: making or sending "
|
||||||
"KRB-PRIV message");
|
"KRB-PRIV message");
|
||||||
slave_dead(context, s);
|
slave_dead(context, s);
|
||||||
return ret;
|
return;
|
||||||
}
|
}
|
||||||
slave_seen(s);
|
slave_seen(s);
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sensible bound on slave message size */
|
/* Sensible bound on slave message size */
|
||||||
@@ -1342,7 +1299,7 @@ read_msg(krb5_context context, slave *s, krb5_data *out)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_msg(kadm5_server_context *server_context, slave *s,
|
process_msg(kadm5_server_context *server_context, slave *s, int log_fd,
|
||||||
const char *database, uint32_t current_version)
|
const char *database, uint32_t current_version)
|
||||||
{
|
{
|
||||||
krb5_context context = server_context->context;
|
krb5_context context = server_context->context;
|
||||||
@@ -1350,7 +1307,6 @@ process_msg(kadm5_server_context *server_context, slave *s,
|
|||||||
krb5_data out;
|
krb5_data out;
|
||||||
krb5_storage *sp;
|
krb5_storage *sp;
|
||||||
uint32_t tmp;
|
uint32_t tmp;
|
||||||
int tries = 3;
|
|
||||||
|
|
||||||
ret = read_msg(context, s, &out);
|
ret = read_msg(context, s, &out);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@@ -1416,9 +1372,9 @@ process_msg(kadm5_server_context *server_context, slave *s,
|
|||||||
krb5_warnx(context, "slave %s ready for updates from version %u",
|
krb5_warnx(context, "slave %s ready for updates from version %u",
|
||||||
s->name, tmp);
|
s->name, tmp);
|
||||||
}
|
}
|
||||||
do {
|
if ((s->version_ack = tmp) < s->version)
|
||||||
ret = send_diffs(server_context, s, database, current_version);
|
break;
|
||||||
} while (ret == KADM5_LOG_SNAPSHOT_INVALID && tries--);
|
send_diffs(server_context, s, log_fd, database, current_version);
|
||||||
break;
|
break;
|
||||||
case I_AM_HERE :
|
case I_AM_HERE :
|
||||||
if (verbose)
|
if (verbose)
|
||||||
@@ -1534,7 +1490,7 @@ write_stats(krb5_context context, slave *slaves, uint32_t current_version)
|
|||||||
} else
|
} else
|
||||||
rtbl_add_column_entry(tbl, SLAVE_ADDRESS, "<unknown>");
|
rtbl_add_column_entry(tbl, SLAVE_ADDRESS, "<unknown>");
|
||||||
|
|
||||||
snprintf(str, sizeof(str), "%u", (unsigned)slaves->version);
|
snprintf(str, sizeof(str), "%u", (unsigned)slaves->version_ack);
|
||||||
rtbl_add_column_entry(tbl, SLAVE_VERSION, str);
|
rtbl_add_column_entry(tbl, SLAVE_VERSION, str);
|
||||||
|
|
||||||
if (slaves->flags & SLAVE_F_DEAD)
|
if (slaves->flags & SLAVE_F_DEAD)
|
||||||
@@ -1615,6 +1571,7 @@ main(int argc, char **argv)
|
|||||||
kadm5_server_context *server_context;
|
kadm5_server_context *server_context;
|
||||||
kadm5_config_params conf;
|
kadm5_config_params conf;
|
||||||
krb5_socket_t signal_fd, listen_fd;
|
krb5_socket_t signal_fd, listen_fd;
|
||||||
|
int log_fd;
|
||||||
slave *slaves = NULL;
|
slave *slaves = NULL;
|
||||||
uint32_t current_version = 0, old_version = 0;
|
uint32_t current_version = 0, old_version = 0;
|
||||||
krb5_keytab keytab;
|
krb5_keytab keytab;
|
||||||
@@ -1718,16 +1675,21 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
server_context = (kadm5_server_context *)kadm_handle;
|
server_context = (kadm5_server_context *)kadm_handle;
|
||||||
|
|
||||||
ret = kadm5_log_init_nolock(server_context);
|
log_fd = open (server_context->log_context.log_file, O_RDONLY, 0);
|
||||||
if (ret)
|
if (log_fd < 0)
|
||||||
krb5_err(context, 1, ret, "Could not open iprop log file");
|
krb5_err (context, 1, errno, "open %s",
|
||||||
|
server_context->log_context.log_file);
|
||||||
|
|
||||||
if (fstat(server_context->log_context.log_fd, &st) == -1)
|
if (fstat(log_fd, &st) == -1)
|
||||||
krb5_err(context, 1, errno, "stat %s",
|
krb5_err(context, 1, errno, "stat %s",
|
||||||
server_context->log_context.log_file);
|
server_context->log_context.log_file);
|
||||||
|
|
||||||
kadm5_log_get_version_fd(server_context, -1, LOG_VERSION_LAST,
|
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,
|
||||||
¤t_version, NULL);
|
¤t_version, NULL);
|
||||||
|
flock(log_fd, LOCK_UN);
|
||||||
|
|
||||||
signal_fd = make_signal_socket (context);
|
signal_fd = make_signal_socket (context);
|
||||||
listen_fd = make_listen_socket (context, port_str);
|
listen_fd = make_listen_socket (context, port_str);
|
||||||
@@ -1744,7 +1706,7 @@ main(int argc, char **argv)
|
|||||||
int max_fd = 0;
|
int max_fd = 0;
|
||||||
struct timeval to = {30, 0};
|
struct timeval to = {30, 0};
|
||||||
uint32_t vers;
|
uint32_t vers;
|
||||||
struct stat st2;
|
struct stat st2;;
|
||||||
|
|
||||||
#ifndef NO_LIMIT_FD_SETSIZE
|
#ifndef NO_LIMIT_FD_SETSIZE
|
||||||
if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE ||
|
if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE ||
|
||||||
@@ -1786,24 +1748,36 @@ main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (st2.st_dev != st.st_dev || st2.st_ino != st.st_ino) {
|
if (st2.st_dev != st.st_dev || st2.st_ino != st.st_ino) {
|
||||||
kadm5_log_end(server_context);
|
(void) close(log_fd);
|
||||||
|
|
||||||
ret = kadm5_log_init_nolock(server_context);
|
log_fd = open(server_context->log_context.log_file, O_RDONLY, 0);
|
||||||
if (ret)
|
if (log_fd < 0)
|
||||||
krb5_err(context, IPROPD_RESTART_SLOW, ret,
|
krb5_err(context, IPROPD_RESTART_SLOW, errno, "open %s",
|
||||||
"Could not re-open iprop log file");
|
server_context->log_context.log_file);
|
||||||
|
|
||||||
if (fstat(server_context->log_context.log_fd, &st) == -1)
|
if (fstat(log_fd, &st) == -1)
|
||||||
krb5_err(context, IPROPD_RESTART_SLOW, errno, "stat %s",
|
krb5_err(context, IPROPD_RESTART_SLOW, errno, "stat %s",
|
||||||
server_context->log_context.log_file);
|
server_context->log_context.log_file);
|
||||||
|
|
||||||
kadm5_log_get_version_fd(server_context, -1, LOG_VERSION_LAST,
|
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,
|
||||||
¤t_version, NULL);
|
¤t_version, NULL);
|
||||||
|
flock(log_fd, LOCK_UN);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
kadm5_log_get_version_fd(server_context, -1, LOG_VERSION_LAST,
|
/* 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,
|
||||||
¤t_version, NULL);
|
¤t_version, NULL);
|
||||||
|
flock(log_fd, LOCK_UN);
|
||||||
|
|
||||||
if (current_version > old_version) {
|
if (current_version > old_version) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
@@ -1812,14 +1786,10 @@ main(int argc, char **argv)
|
|||||||
(unsigned long)old_version,
|
(unsigned long)old_version,
|
||||||
(unsigned long)current_version);
|
(unsigned long)current_version);
|
||||||
for (p = slaves; p != NULL; p = p->next) {
|
for (p = slaves; p != NULL; p = p->next) {
|
||||||
int tries = 3;
|
|
||||||
|
|
||||||
if (p->flags & SLAVE_F_DEAD)
|
if (p->flags & SLAVE_F_DEAD)
|
||||||
continue;
|
continue;
|
||||||
do {
|
send_diffs(server_context, p, log_fd, database,
|
||||||
ret = send_diffs(server_context, p, database,
|
|
||||||
current_version);
|
current_version);
|
||||||
} while (ret == KADM5_LOG_SNAPSHOT_INVALID && tries--);
|
|
||||||
}
|
}
|
||||||
old_version = current_version;
|
old_version = current_version;
|
||||||
}
|
}
|
||||||
@@ -1846,8 +1816,12 @@ main(int argc, char **argv)
|
|||||||
--ret;
|
--ret;
|
||||||
assert(ret >= 0);
|
assert(ret >= 0);
|
||||||
old_version = current_version;
|
old_version = current_version;
|
||||||
kadm5_log_get_version_fd(server_context, -1, LOG_VERSION_LAST,
|
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,
|
||||||
¤t_version, NULL);
|
¤t_version, NULL);
|
||||||
|
flock(log_fd, LOCK_UN);
|
||||||
if (current_version != old_version) {
|
if (current_version != old_version) {
|
||||||
/*
|
/*
|
||||||
* If current_version < old_version then the log got
|
* If current_version < old_version then the log got
|
||||||
@@ -1867,14 +1841,10 @@ main(int argc, char **argv)
|
|||||||
(unsigned long)old_version,
|
(unsigned long)old_version,
|
||||||
(unsigned long)current_version);
|
(unsigned long)current_version);
|
||||||
for (p = slaves; p != NULL; p = p->next) {
|
for (p = slaves; p != NULL; p = p->next) {
|
||||||
int tries = 3;
|
|
||||||
|
|
||||||
if (p->flags & SLAVE_F_DEAD)
|
if (p->flags & SLAVE_F_DEAD)
|
||||||
continue;
|
continue;
|
||||||
do {
|
send_diffs(server_context, p, log_fd, database,
|
||||||
ret = send_diffs(server_context, p, database,
|
|
||||||
current_version);
|
current_version);
|
||||||
} while (ret == EAGAIN && tries--);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
@@ -1889,12 +1859,8 @@ main(int argc, char **argv)
|
|||||||
FD_ISSET(p->fd, &writeset) &&
|
FD_ISSET(p->fd, &writeset) &&
|
||||||
((have_tail(p) && send_tail(context, p) == 0) ||
|
((have_tail(p) && send_tail(context, p) == 0) ||
|
||||||
(!have_tail(p) && more_diffs(p)))) {
|
(!have_tail(p) && more_diffs(p)))) {
|
||||||
int tries = 3;
|
send_diffs(server_context, p, log_fd, database,
|
||||||
|
|
||||||
do {
|
|
||||||
ret = send_diffs(server_context, p, database,
|
|
||||||
current_version);
|
current_version);
|
||||||
} while (ret == KADM5_LOG_SNAPSHOT_INVALID && tries--);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1904,12 +1870,10 @@ main(int argc, char **argv)
|
|||||||
if (ret && FD_ISSET(p->fd, &readset)) {
|
if (ret && FD_ISSET(p->fd, &readset)) {
|
||||||
--ret;
|
--ret;
|
||||||
assert(ret >= 0);
|
assert(ret >= 0);
|
||||||
ret = process_msg(server_context, p, database,
|
ret = process_msg(server_context, p, log_fd, database,
|
||||||
current_version);
|
current_version);
|
||||||
if (ret && ret != EWOULDBLOCK) {
|
if (ret && ret != EWOULDBLOCK)
|
||||||
krb5_warn(context, ret, "process_msg()");
|
|
||||||
slave_dead(context, p);
|
slave_dead(context, p);
|
||||||
}
|
|
||||||
} else if (slave_gone_p (p))
|
} else if (slave_gone_p (p))
|
||||||
slave_dead(context, p);
|
slave_dead(context, p);
|
||||||
else if (slave_missing_p (p))
|
else if (slave_missing_p (p))
|
||||||
|
|||||||
@@ -355,6 +355,11 @@ receive_loop(krb5_context context,
|
|||||||
if (verbose)
|
if (verbose)
|
||||||
krb5_warnx(context, "receiving diffs");
|
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
|
* Seek to the first entry in the message from the master that is
|
||||||
* past the current version of the local database.
|
* past the current version of the local database.
|
||||||
@@ -472,6 +477,9 @@ receive(krb5_context context,
|
|||||||
if (ret)
|
if (ret)
|
||||||
krb5_err(context, IPROPD_RESTART_SLOW, ret, "db->close");
|
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);
|
kadm5_log_signal_master(server_context);
|
||||||
if (verbose)
|
if (verbose)
|
||||||
krb5_warnx(context, "signaled master for hierarchical iprop");
|
krb5_warnx(context, "signaled master for hierarchical iprop");
|
||||||
@@ -523,6 +531,9 @@ reinit_log(krb5_context context,
|
|||||||
ret = kadm5_log_reinit(server_context, vno);
|
ret = kadm5_log_reinit(server_context, vno);
|
||||||
if (ret)
|
if (ret)
|
||||||
krb5_err(context, IPROPD_RESTART_SLOW, ret, "kadm5_log_reinit");
|
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -542,6 +553,10 @@ receive_everything(krb5_context context, int fd,
|
|||||||
|
|
||||||
krb5_warnx(context, "receive complete database");
|
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) {
|
if (server_context->db->hdb_method_name) {
|
||||||
ret = asprintf(&dbname, "%.*s:%s-NEW",
|
ret = asprintf(&dbname, "%.*s:%s-NEW",
|
||||||
(int) strlen(server_context->db->hdb_method_name) - 1,
|
(int) strlen(server_context->db->hdb_method_name) - 1,
|
||||||
@@ -885,6 +900,9 @@ main(int argc, char **argv)
|
|||||||
ret = kadm5_log_init(server_context);
|
ret = kadm5_log_init(server_context);
|
||||||
if (ret)
|
if (ret)
|
||||||
krb5_err(context, 1, ret, "kadm5_log_init");
|
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);
|
ret = server_context->db->hdb_close(context, server_context->db);
|
||||||
if (ret)
|
if (ret)
|
||||||
@@ -1073,6 +1091,9 @@ main(int argc, char **argv)
|
|||||||
krb5_err(context, IPROPD_RESTART, ret, "kadm5_log_init while "
|
krb5_err(context, IPROPD_RESTART, ret, "kadm5_log_init while "
|
||||||
"handling a message from the master");
|
"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);
|
ret = server_context->db->hdb_close (context, server_context->db);
|
||||||
if (ret)
|
if (ret)
|
||||||
@@ -1104,6 +1125,7 @@ main(int argc, char **argv)
|
|||||||
krb5_warnx(context, "master sent us a full dump");
|
krb5_warnx(context, "master sent us a full dump");
|
||||||
ret = receive_everything(context, master_fd, server_context,
|
ret = receive_everything(context, master_fd, server_context,
|
||||||
auth_context);
|
auth_context);
|
||||||
|
(void) kadm5_log_sharedlock(server_context);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
ret = ihave(context, auth_context, master_fd,
|
ret = ihave(context, auth_context, master_fd,
|
||||||
server_context->log_context.version);
|
server_context->log_context.version);
|
||||||
@@ -1112,6 +1134,8 @@ main(int argc, char **argv)
|
|||||||
connected = FALSE;
|
connected = FALSE;
|
||||||
else
|
else
|
||||||
is_up_to_date(context, status_file, server_context);
|
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);
|
kadm5_log_signal_master(server_context);
|
||||||
if (verbose)
|
if (verbose)
|
||||||
krb5_warnx(context, "signaled master for hierarchical iprop");
|
krb5_warnx(context, "signaled master for hierarchical iprop");
|
||||||
@@ -1128,7 +1152,6 @@ main(int argc, char **argv)
|
|||||||
*
|
*
|
||||||
* So we don't ihave() here.
|
* So we don't ihave() here.
|
||||||
*/
|
*/
|
||||||
is_up_to_date(context, status_file, server_context);
|
|
||||||
send_im_here(context, master_fd, auth_context);
|
send_im_here(context, master_fd, auth_context);
|
||||||
break;
|
break;
|
||||||
case YOU_HAVE_LAST_VERSION:
|
case YOU_HAVE_LAST_VERSION:
|
||||||
@@ -1152,7 +1175,7 @@ main(int argc, char **argv)
|
|||||||
slave_status(context, status_file, "disconnected from master");
|
slave_status(context, status_file, "disconnected from master");
|
||||||
retry:
|
retry:
|
||||||
if (connected == FALSE)
|
if (connected == FALSE)
|
||||||
krb5_warnx (context, "disconnected from server");
|
krb5_warnx (context, "disconnected for server");
|
||||||
|
|
||||||
if (exit_flag)
|
if (exit_flag)
|
||||||
krb5_warnx (context, "got an exit signal");
|
krb5_warnx (context, "got an exit signal");
|
||||||
|
|||||||
@@ -73,8 +73,6 @@ 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 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 READ_ONLY, "Database is read-only; try primary server"
|
||||||
error_code PASS_Q_GENERIC, "Unspecified password quality failure"
|
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:
|
# MIT has:
|
||||||
|
|||||||
@@ -86,7 +86,6 @@
|
|||||||
#include <hdb.h>
|
#include <hdb.h>
|
||||||
#include <der.h>
|
#include <der.h>
|
||||||
#include <parse_units.h>
|
#include <parse_units.h>
|
||||||
|
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
|
|
||||||
#endif /* __KADM5_LOCL_H__ */
|
#endif /* __KADM5_LOCL_H__ */
|
||||||
|
|||||||
@@ -85,8 +85,6 @@ EXPORTS
|
|||||||
kadm5_log_init_sharedlock
|
kadm5_log_init_sharedlock
|
||||||
kadm5_log_next
|
kadm5_log_next
|
||||||
kadm5_log_nop
|
kadm5_log_nop
|
||||||
kadm5_log_snapshot
|
|
||||||
kadm5_log_snapshot_verify
|
|
||||||
kadm5_log_truncate
|
kadm5_log_truncate
|
||||||
kadm5_log_modify
|
kadm5_log_modify
|
||||||
_kadm5_acl_check_permission
|
_kadm5_acl_check_permission
|
||||||
|
|||||||
190
lib/kadm5/log.c
190
lib/kadm5/log.c
@@ -174,6 +174,12 @@ RCSID("$Id$");
|
|||||||
* will deadlock with ipropd-slave -- don't do that.
|
* 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_NOPEEK 0
|
||||||
#define LOG_DOPEEK 1
|
#define LOG_DOPEEK 1
|
||||||
|
|
||||||
@@ -484,46 +490,12 @@ kadm5_log_get_version_fd(kadm5_server_context *server_context, int fd,
|
|||||||
{
|
{
|
||||||
kadm5_ret_t ret = 0;
|
kadm5_ret_t ret = 0;
|
||||||
krb5_storage *sp;
|
krb5_storage *sp;
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
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;
|
enum kadm_ops op = kadm_get;
|
||||||
uint32_t len = 0;
|
uint32_t len = 0;
|
||||||
uint32_t tmp;
|
uint32_t tmp;
|
||||||
|
|
||||||
/*
|
if (fd == -1)
|
||||||
* If we're operating lock-less-ly then get a snapshot of the log so we can
|
return 0; /* /dev/null */
|
||||||
* 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)
|
if (tstamp == NULL)
|
||||||
tstamp = &tmp;
|
tstamp = &tmp;
|
||||||
@@ -531,6 +503,10 @@ kadm5_log_get_version_sp(kadm5_server_context *server_context, krb5_storage *sp,
|
|||||||
*ver = 0;
|
*ver = 0;
|
||||||
*tstamp = 0;
|
*tstamp = 0;
|
||||||
|
|
||||||
|
sp = krb5_storage_from_fd(fd);
|
||||||
|
if (sp == NULL)
|
||||||
|
return errno ? errno : ENOMEM;
|
||||||
|
|
||||||
switch (which) {
|
switch (which) {
|
||||||
case LOG_VERSION_LAST:
|
case LOG_VERSION_LAST:
|
||||||
ret = kadm5_log_goto_end(server_context, sp);
|
ret = kadm5_log_goto_end(server_context, sp);
|
||||||
@@ -555,92 +531,10 @@ kadm5_log_get_version_sp(kadm5_server_context *server_context, krb5_storage *sp,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == 0 && server_context->log_context.lock_mode == LOCK_UN)
|
krb5_storage_free(sp);
|
||||||
/* Validate the snapshot when operating lock-less-ly */
|
|
||||||
return kadm5_log_snapshot_verify(server_context, &snap);
|
|
||||||
return ret;
|
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 */
|
/* Get the version of the last confirmed entry in the log */
|
||||||
kadm5_ret_t
|
kadm5_ret_t
|
||||||
kadm5_log_get_version(kadm5_server_context *server_context, uint32_t *ver)
|
kadm5_log_get_version(kadm5_server_context *server_context, uint32_t *ver)
|
||||||
@@ -2239,7 +2133,7 @@ kadm5_log_goto_first(kadm5_server_context *server_context, krb5_storage *sp)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
if (op == kadm_nop && len == LOG_UBER_LEN && seek_next(sp) == -1)
|
if (op == kadm_nop && len == LOG_UBER_LEN && seek_next(sp) == -1)
|
||||||
return KADM5_LOG_EMPTY;
|
return KADM5_LOG_CORRUPT;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2609,13 +2503,6 @@ load_entries(kadm5_server_context *context, krb5_data *p,
|
|||||||
/*
|
/*
|
||||||
* Truncate the log, retaining at most `keep' entries and at most `maxbytes'.
|
* Truncate the log, retaining at most `keep' entries and at most `maxbytes'.
|
||||||
* If `maxbytes' is zero, keep at most the default log size limit.
|
* 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_ret_t
|
||||||
kadm5_log_truncate(kadm5_server_context *context, size_t keep, size_t maxbytes)
|
kadm5_log_truncate(kadm5_server_context *context, size_t keep, size_t maxbytes)
|
||||||
@@ -2627,10 +2514,7 @@ kadm5_log_truncate(kadm5_server_context *context, size_t keep, size_t maxbytes)
|
|||||||
krb5_storage *sp;
|
krb5_storage *sp;
|
||||||
ssize_t bytes;
|
ssize_t bytes;
|
||||||
uint64_t sz;
|
uint64_t sz;
|
||||||
off_t off, fsz;
|
off_t off;
|
||||||
|
|
||||||
if ((fsz = lseek(context->log_context.log_fd, 0, SEEK_END)) < 0)
|
|
||||||
return errno;
|
|
||||||
|
|
||||||
if (maxbytes == 0)
|
if (maxbytes == 0)
|
||||||
maxbytes = get_max_log_size(context->context);
|
maxbytes = get_max_log_size(context->context);
|
||||||
@@ -2665,50 +2549,6 @@ kadm5_log_truncate(kadm5_server_context *context, size_t keep, size_t maxbytes)
|
|||||||
return EOVERFLOW; /* caller should ask for fewer entries */
|
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 */
|
/* Truncate to zero size and seek to zero offset */
|
||||||
if (ftruncate(context->log_context.log_fd, 0) < 0 ||
|
if (ftruncate(context->log_context.log_fd, 0) < 0 ||
|
||||||
lseek(context->log_context.log_fd, 0, SEEK_SET) < 0) {
|
lseek(context->log_context.log_fd, 0, SEEK_SET) < 0) {
|
||||||
|
|||||||
@@ -87,8 +87,6 @@ HEIMDAL_KAMD5_SERVER_1.0 {
|
|||||||
kadm5_log_init_sharedlock;
|
kadm5_log_init_sharedlock;
|
||||||
kadm5_log_next;
|
kadm5_log_next;
|
||||||
kadm5_log_nop;
|
kadm5_log_nop;
|
||||||
kadm5_log_snapshot;
|
|
||||||
kadm5_log_snapshot_verify;
|
|
||||||
kadm5_log_truncate;
|
kadm5_log_truncate;
|
||||||
kadm5_log_modify;
|
kadm5_log_modify;
|
||||||
_kadm5_acl_check_permission;
|
_kadm5_acl_check_permission;
|
||||||
|
|||||||
@@ -69,17 +69,22 @@ krb5_expand_hostname (krb5_context context,
|
|||||||
int error;
|
int error;
|
||||||
|
|
||||||
if ((context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) == 0 ||
|
if ((context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) == 0 ||
|
||||||
krb5_config_get_bool(context, NULL, "libdefaults", "block_dns", NULL))
|
krb5_config_get_bool(context, NULL, "libdefaults", "block_dns", NULL)) {
|
||||||
|
krb5_warnx(context, "krb5_expand_hostname: DNS canonicalization disabled or blocked for hostname %s", orig_hostname);
|
||||||
return copy_hostname (context, orig_hostname, new_hostname);
|
return copy_hostname (context, orig_hostname, new_hostname);
|
||||||
|
}
|
||||||
|
|
||||||
memset (&hints, 0, sizeof(hints));
|
memset (&hints, 0, sizeof(hints));
|
||||||
hints.ai_flags = AI_CANONNAME;
|
hints.ai_flags = AI_CANONNAME;
|
||||||
|
|
||||||
error = getaddrinfo (orig_hostname, NULL, &hints, &ai);
|
error = getaddrinfo (orig_hostname, NULL, &hints, &ai);
|
||||||
if (error)
|
if (error) {
|
||||||
|
krb5_warnx(context, "krb5_expand_hostname: getaddrinfo(%s) failed: %s", orig_hostname, gai_strerror(error));
|
||||||
return copy_hostname (context, orig_hostname, new_hostname);
|
return copy_hostname (context, orig_hostname, new_hostname);
|
||||||
|
}
|
||||||
for (a = ai; a != NULL; a = a->ai_next) {
|
for (a = ai; a != NULL; a = a->ai_next) {
|
||||||
if (a->ai_canonname != NULL) {
|
if (a->ai_canonname != NULL) {
|
||||||
|
krb5_warnx(context, "krb5_expand_hostname: canonical name for %s -> %s", orig_hostname, a->ai_canonname);
|
||||||
*new_hostname = strdup (a->ai_canonname);
|
*new_hostname = strdup (a->ai_canonname);
|
||||||
freeaddrinfo (ai);
|
freeaddrinfo (ai);
|
||||||
if (*new_hostname == NULL)
|
if (*new_hostname == NULL)
|
||||||
@@ -89,6 +94,7 @@ krb5_expand_hostname (krb5_context context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
freeaddrinfo (ai);
|
freeaddrinfo (ai);
|
||||||
|
krb5_warnx(context, "krb5_expand_hostname: no canonical name found for %s, falling back to copy_hostname", orig_hostname);
|
||||||
return copy_hostname (context, orig_hostname, new_hostname);
|
return copy_hostname (context, orig_hostname, new_hostname);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,20 +151,25 @@ krb5_expand_hostname_realms (krb5_context context,
|
|||||||
int error;
|
int error;
|
||||||
krb5_error_code ret = 0;
|
krb5_error_code ret = 0;
|
||||||
|
|
||||||
if ((context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) == 0)
|
if ((context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) == 0) {
|
||||||
|
krb5_warnx(context, "krb5_expand_hostname_realms: DNS canonicalization disabled for hostname %s", orig_hostname);
|
||||||
return vanilla_hostname (context, orig_hostname, new_hostname,
|
return vanilla_hostname (context, orig_hostname, new_hostname,
|
||||||
realms);
|
realms);
|
||||||
|
}
|
||||||
|
|
||||||
memset (&hints, 0, sizeof(hints));
|
memset (&hints, 0, sizeof(hints));
|
||||||
hints.ai_flags = AI_CANONNAME;
|
hints.ai_flags = AI_CANONNAME;
|
||||||
|
|
||||||
error = getaddrinfo (orig_hostname, NULL, &hints, &ai);
|
error = getaddrinfo (orig_hostname, NULL, &hints, &ai);
|
||||||
if (error)
|
if (error) {
|
||||||
|
krb5_warnx(context, "krb5_expand_hostname_realms: getaddrinfo(%s) failed: %s", orig_hostname, gai_strerror(error));
|
||||||
return vanilla_hostname (context, orig_hostname, new_hostname,
|
return vanilla_hostname (context, orig_hostname, new_hostname,
|
||||||
realms);
|
realms);
|
||||||
|
}
|
||||||
|
|
||||||
for (a = ai; a != NULL; a = a->ai_next) {
|
for (a = ai; a != NULL; a = a->ai_next) {
|
||||||
if (a->ai_canonname != NULL) {
|
if (a->ai_canonname != NULL) {
|
||||||
|
krb5_warnx(context, "krb5_expand_hostname_realms: canonical name for %s -> %s", orig_hostname, a->ai_canonname);
|
||||||
ret = copy_hostname (context, a->ai_canonname, new_hostname);
|
ret = copy_hostname (context, a->ai_canonname, new_hostname);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
freeaddrinfo (ai);
|
freeaddrinfo (ai);
|
||||||
@@ -168,11 +179,13 @@ krb5_expand_hostname_realms (krb5_context context,
|
|||||||
ret = krb5_get_host_realm (context, *new_hostname, realms);
|
ret = krb5_get_host_realm (context, *new_hostname, realms);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
freeaddrinfo (ai);
|
freeaddrinfo (ai);
|
||||||
|
krb5_warnx(context, "krb5_expand_hostname_realms: host %s mapped to realm %s", *new_hostname, realms && *realms ? (*realms)[0] : "<unknown>");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
free (*new_hostname);
|
free (*new_hostname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
freeaddrinfo(ai);
|
freeaddrinfo(ai);
|
||||||
|
krb5_warnx(context, "krb5_expand_hostname_realms: no canonical name/realm found for %s, falling back to vanilla_hostname", orig_hostname);
|
||||||
return vanilla_hostname (context, orig_hostname, new_hostname, realms);
|
return vanilla_hostname (context, orig_hostname, new_hostname, realms);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,6 +217,31 @@ krb5_kt_resolve(krb5_context context,
|
|||||||
|
|
||||||
residual = keytab_name(name, &type, &type_len);
|
residual = keytab_name(name, &type, &type_len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Aggressive process-context logging:
|
||||||
|
* Print pid/ppid/uid/gid and the raw name string being resolved
|
||||||
|
* so test captures show which process attempted to resolve which
|
||||||
|
* keytab and with what parsed type/residual.
|
||||||
|
*
|
||||||
|
* We intentionally always emit this via krb5_warnx to make sure it
|
||||||
|
* appears in stderr/journal during tests.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
pid_t pid = getpid();
|
||||||
|
pid_t ppid = getppid();
|
||||||
|
uid_t uid = getuid();
|
||||||
|
gid_t gid = getgid();
|
||||||
|
krb5_warnx(context,
|
||||||
|
"krb5_kt_resolve: pid=%d ppid=%d uid=%d gid=%d resolving name=\"%s\" parsed_type=\"%.*s\" residual=\"%s\"",
|
||||||
|
(int)pid,
|
||||||
|
(int)ppid,
|
||||||
|
(int)uid,
|
||||||
|
(int)gid,
|
||||||
|
name ? name : "<null>",
|
||||||
|
(int)type_len, type ? type : "<null>",
|
||||||
|
residual ? residual : "<null>");
|
||||||
|
}
|
||||||
|
|
||||||
for(i = 0; i < context->num_kt_types; i++) {
|
for(i = 0; i < context->num_kt_types; i++) {
|
||||||
if(strncasecmp(type, context->kt_types[i].prefix, type_len) == 0)
|
if(strncasecmp(type, context->kt_types[i].prefix, type_len) == 0)
|
||||||
break;
|
break;
|
||||||
@@ -251,8 +276,15 @@ static const char *default_ktname(krb5_context context)
|
|||||||
const char *tmp = NULL;
|
const char *tmp = NULL;
|
||||||
|
|
||||||
tmp = secure_getenv("KRB5_KTNAME");
|
tmp = secure_getenv("KRB5_KTNAME");
|
||||||
if(tmp != NULL)
|
if(tmp != NULL) {
|
||||||
|
/* Aggressive logging: record environment override */
|
||||||
|
krb5_warnx(context, "default_ktname: using KRB5_KTNAME from env: \"%s\" (pid=%d)", tmp, (int)getpid());
|
||||||
return tmp;
|
return tmp;
|
||||||
|
}
|
||||||
|
/* Aggressive logging: record context default choice as well */
|
||||||
|
krb5_warnx(context, "default_ktname: using context->default_keytab: \"%s\" (pid=%d)",
|
||||||
|
context->default_keytab ? context->default_keytab : "<null>",
|
||||||
|
(int)getpid());
|
||||||
return context->default_keytab;
|
return context->default_keytab;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,6 +629,13 @@ _krb5_kt_principal_not_found(krb5_context context,
|
|||||||
else
|
else
|
||||||
kvno_str[0] = '\0';
|
kvno_str[0] = '\0';
|
||||||
|
|
||||||
|
/* Provide a concise debug line so tests can see what principal/keytab/enctype failed */
|
||||||
|
krb5_warnx(context, "kt_principal_not_found: principal=%s kvno=%s keytab=%s enctype=%s",
|
||||||
|
princ ? princ : "<unknown>",
|
||||||
|
kvno_str[0] ? kvno_str : "<none>",
|
||||||
|
kt_name ? kt_name : "unknown keytab",
|
||||||
|
enctype_str ? enctype_str : "unknown enctype");
|
||||||
|
|
||||||
krb5_set_error_message(context, ret,
|
krb5_set_error_message(context, ret,
|
||||||
N_("Failed to find %s%s in keytab %s (%s)",
|
N_("Failed to find %s%s in keytab %s (%s)",
|
||||||
"principal, kvno, keytab file, enctype"),
|
"principal, kvno, keytab file, enctype"),
|
||||||
@@ -636,27 +675,81 @@ krb5_kt_get_entry_wrapped(krb5_context context,
|
|||||||
|
|
||||||
entry->vno = 0;
|
entry->vno = 0;
|
||||||
while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) {
|
while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) {
|
||||||
|
/* Always-log the entry we're inspecting and what is being requested */
|
||||||
|
{
|
||||||
|
char *entry_unp = NULL;
|
||||||
|
char *want_unp = NULL;
|
||||||
|
|
||||||
|
(void) krb5_unparse_name(context, tmp.principal, &entry_unp);
|
||||||
|
(void) krb5_unparse_name(context, principal, &want_unp);
|
||||||
|
|
||||||
|
krb5_warnx(context, "krb5_kt_get_entry_wrapped: inspecting entry=%s vno=%d enctype=%d against requested=%s kvno=%u enctype=%d",
|
||||||
|
entry_unp ? entry_unp : "<unparsed>",
|
||||||
|
tmp.vno,
|
||||||
|
tmp.keyblock.keytype,
|
||||||
|
want_unp ? want_unp : "<unparsed>",
|
||||||
|
(unsigned)kvno,
|
||||||
|
(int)enctype);
|
||||||
|
|
||||||
|
free(entry_unp);
|
||||||
|
free(want_unp);
|
||||||
|
}
|
||||||
|
|
||||||
if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) {
|
if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) {
|
||||||
/* the file keytab might only store the lower 8 bits of
|
/* the file keytab might only store the lower 8 bits of
|
||||||
the kvno, so only compare those bits */
|
the kvno, so only compare those bits */
|
||||||
if (kvno == tmp.vno
|
if (kvno == tmp.vno
|
||||||
|| (tmp.vno < 256 && kvno % 256 == tmp.vno)) {
|
|| (tmp.vno < 256 && kvno % 256 == tmp.vno)) {
|
||||||
|
/* Matched principal and kvno -> return this entry */
|
||||||
|
{
|
||||||
|
char *matched = NULL;
|
||||||
|
(void) krb5_unparse_name(context, tmp.principal, &matched);
|
||||||
|
krb5_warnx(context, "krb5_kt_get_entry_wrapped: matched entry=%s vno=%d enctype=%d -- returning",
|
||||||
|
matched ? matched : "<unparsed>",
|
||||||
|
tmp.vno,
|
||||||
|
tmp.keyblock.keytype);
|
||||||
|
free(matched);
|
||||||
|
}
|
||||||
krb5_kt_copy_entry_contents (context, &tmp, entry);
|
krb5_kt_copy_entry_contents (context, &tmp, entry);
|
||||||
krb5_kt_free_entry (context, &tmp);
|
krb5_kt_free_entry (context, &tmp);
|
||||||
krb5_kt_end_seq_get(context, id, &cursor);
|
krb5_kt_end_seq_get(context, id, &cursor);
|
||||||
return 0;
|
return 0;
|
||||||
} else if (kvno == 0 && tmp.vno > entry->vno) {
|
} else if (kvno == 0 && tmp.vno > entry->vno) {
|
||||||
|
/* Matched principal but kvno selection logic prefers a higher vno */
|
||||||
|
{
|
||||||
|
char *matched = NULL;
|
||||||
|
(void) krb5_unparse_name(context, tmp.principal, &matched);
|
||||||
|
krb5_warnx(context, "krb5_kt_get_entry_wrapped: matched principal %s but kvno differs (entry.vno=%d tmp.vno=%d); selecting higher vno for now",
|
||||||
|
matched ? matched : "<unparsed>",
|
||||||
|
entry->vno,
|
||||||
|
tmp.vno);
|
||||||
|
free(matched);
|
||||||
|
}
|
||||||
if (entry->vno)
|
if (entry->vno)
|
||||||
krb5_kt_free_entry (context, entry);
|
krb5_kt_free_entry (context, entry);
|
||||||
krb5_kt_copy_entry_contents (context, &tmp, entry);
|
krb5_kt_copy_entry_contents (context, &tmp, entry);
|
||||||
|
} else {
|
||||||
|
/* Matched name but kvno didn't match and we are not in kvno-selection mode; log it. */
|
||||||
|
char *matched = NULL;
|
||||||
|
(void) krb5_unparse_name(context, tmp.principal, &matched);
|
||||||
|
krb5_warnx(context, "krb5_kt_get_entry_wrapped: matched principal %s but kvno mismatch (tmp.vno=%d requested_kvno=%u); continuing iteration",
|
||||||
|
matched ? matched : "<unparsed>",
|
||||||
|
tmp.vno,
|
||||||
|
(unsigned)kvno);
|
||||||
|
free(matched);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
krb5_kt_free_entry(context, &tmp);
|
krb5_kt_free_entry(context, &tmp);
|
||||||
}
|
}
|
||||||
krb5_kt_end_seq_get (context, id, &cursor);
|
krb5_kt_end_seq_get (context, id, &cursor);
|
||||||
if (entry->vno == 0)
|
if (entry->vno == 0) {
|
||||||
|
/* No matching kvno was found in the keytab iteration; log and return */
|
||||||
|
krb5_warnx(context, "krb5_kt_get_entry_wrapped: no matching entry found -> invoking _krb5_kt_principal_not_found");
|
||||||
return _krb5_kt_principal_not_found(context, KRB5_KT_NOTFOUND,
|
return _krb5_kt_principal_not_found(context, KRB5_KT_NOTFOUND,
|
||||||
id, principal, enctype, kvno);
|
id, principal, enctype, kvno);
|
||||||
|
}
|
||||||
|
krb5_warnx(context, "krb5_kt_get_entry_wrapped: returning selected entry vno=%d enctype=%d",
|
||||||
|
entry->vno, entry->keyblock.keytype);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,6 +781,25 @@ krb5_kt_get_entry(krb5_context context,
|
|||||||
krb5_const_principal try_princ;
|
krb5_const_principal try_princ;
|
||||||
krb5_name_canon_iterator name_canon_iter;
|
krb5_name_canon_iterator name_canon_iter;
|
||||||
|
|
||||||
|
/* Debug: record incoming keytab lookup request (always emitted) */
|
||||||
|
{
|
||||||
|
char *want_unp = NULL;
|
||||||
|
char *kt_full = NULL;
|
||||||
|
|
||||||
|
if (principal && krb5_unparse_name(context, principal, &want_unp) != 0)
|
||||||
|
want_unp = NULL;
|
||||||
|
(void) krb5_kt_get_full_name(context, id, &kt_full);
|
||||||
|
|
||||||
|
krb5_warnx(context, "krb5_kt_get_entry: requested principal=%s keytab=%s kvno=%u enctype=%d",
|
||||||
|
want_unp ? want_unp : "<null>",
|
||||||
|
kt_full ? kt_full : "<unknown>",
|
||||||
|
(unsigned)kvno,
|
||||||
|
(int)enctype);
|
||||||
|
|
||||||
|
free(want_unp);
|
||||||
|
free(kt_full);
|
||||||
|
}
|
||||||
|
|
||||||
if (!principal)
|
if (!principal)
|
||||||
/* Use `NULL' instead of `principal' to quiet static analizers */
|
/* Use `NULL' instead of `principal' to quiet static analizers */
|
||||||
return krb5_kt_get_entry_wrapped(context, id, NULL, kvno, enctype,
|
return krb5_kt_get_entry_wrapped(context, id, NULL, kvno, enctype,
|
||||||
|
|||||||
@@ -380,9 +380,19 @@ fkt_start_seq_get_int(krb5_context context,
|
|||||||
d->filename, strerror(ret));
|
d->filename, strerror(ret));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Loud logging: record that we opened the keytab file and process info */
|
||||||
|
krb5_warnx(context, "fkt_start_seq_get_int: opened keytab \"%s\" fd=%d flags=0x%x pid=%d uid=%d",
|
||||||
|
d->filename ? d->filename : "<null>",
|
||||||
|
c->fd,
|
||||||
|
(unsigned)flags,
|
||||||
|
(int)getpid(),
|
||||||
|
(int)getuid());
|
||||||
|
|
||||||
rk_cloexec(c->fd);
|
rk_cloexec(c->fd);
|
||||||
ret = _krb5_xlock(context, c->fd, exclusive, d->filename);
|
ret = _krb5_xlock(context, c->fd, exclusive, d->filename);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
krb5_warnx(context, "fkt_start_seq_get_int: failed to lock keytab \"%s\": ret=%d", d->filename ? d->filename : "<null>", ret);
|
||||||
close(c->fd);
|
close(c->fd);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -394,6 +404,7 @@ fkt_start_seq_get_int(krb5_context context,
|
|||||||
stdio_mode = "wb";
|
stdio_mode = "wb";
|
||||||
c->sp = krb5_storage_stdio_from_fd(c->fd, stdio_mode);
|
c->sp = krb5_storage_stdio_from_fd(c->fd, stdio_mode);
|
||||||
if (c->sp == NULL) {
|
if (c->sp == NULL) {
|
||||||
|
krb5_warnx(context, "fkt_start_seq_get_int: krb5_storage_stdio_from_fd returned NULL for \"%s\"", d->filename ? d->filename : "<null>");
|
||||||
close(c->fd);
|
close(c->fd);
|
||||||
return krb5_enomem(context);
|
return krb5_enomem(context);
|
||||||
}
|
}
|
||||||
@@ -403,12 +414,17 @@ fkt_start_seq_get_int(krb5_context context,
|
|||||||
krb5_storage_free(c->sp);
|
krb5_storage_free(c->sp);
|
||||||
close(c->fd);
|
close(c->fd);
|
||||||
krb5_clear_error_message(context);
|
krb5_clear_error_message(context);
|
||||||
|
krb5_warnx(context, "fkt_start_seq_get_int: failed reading pvno from \"%s\": ret=%d", d->filename ? d->filename : "<null>", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
/* Log the pvno read */
|
||||||
|
krb5_warnx(context, "fkt_start_seq_get_int: keytab \"%s\" pvno=%d", d->filename ? d->filename : "<null>", (int)pvno);
|
||||||
|
|
||||||
if(pvno != 5) {
|
if(pvno != 5) {
|
||||||
krb5_storage_free(c->sp);
|
krb5_storage_free(c->sp);
|
||||||
close(c->fd);
|
close(c->fd);
|
||||||
krb5_clear_error_message (context);
|
krb5_clear_error_message (context);
|
||||||
|
krb5_warnx(context, "fkt_start_seq_get_int: keytab \"%s\" has bad vno (%d)", d->filename ? d->filename : "<null>", (int)pvno);
|
||||||
return KRB5_KEYTAB_BADVNO;
|
return KRB5_KEYTAB_BADVNO;
|
||||||
}
|
}
|
||||||
ret = krb5_ret_int8(c->sp, &tag);
|
ret = krb5_ret_int8(c->sp, &tag);
|
||||||
@@ -416,10 +432,19 @@ fkt_start_seq_get_int(krb5_context context,
|
|||||||
krb5_storage_free(c->sp);
|
krb5_storage_free(c->sp);
|
||||||
close(c->fd);
|
close(c->fd);
|
||||||
krb5_clear_error_message(context);
|
krb5_clear_error_message(context);
|
||||||
|
krb5_warnx(context, "fkt_start_seq_get_int: failed reading tag from \"%s\": ret=%d", d->filename ? d->filename : "<null>", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Log the tag/version we read */
|
||||||
|
krb5_warnx(context, "fkt_start_seq_get_int: keytab \"%s\" header tag=%d", d->filename ? d->filename : "<null>", (int)tag);
|
||||||
|
|
||||||
id->version = tag;
|
id->version = tag;
|
||||||
storage_set_flags(context, c->sp, id->version);
|
storage_set_flags(context, c->sp, id->version);
|
||||||
|
|
||||||
|
/* Final confirmation log */
|
||||||
|
krb5_warnx(context, "fkt_start_seq_get_int: keytab \"%s\" opened successfully (fd=%d, version=%d)", d->filename ? d->filename : "<null>", c->fd, (int)id->version);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,26 +475,50 @@ fkt_next_entry_int(krb5_context context,
|
|||||||
pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
|
pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
|
||||||
loop:
|
loop:
|
||||||
ret = krb5_ret_int32(cursor->sp, &len);
|
ret = krb5_ret_int32(cursor->sp, &len);
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
krb5_warnx(context, "fkt_next_entry_int: krb5_ret_int32 returned %d for keytab \"%s\" at pos=%lld", ret, d->filename ? d->filename : "<null>", (long long)pos);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
if(len < 0) {
|
if(len < 0) {
|
||||||
pos = krb5_storage_seek(cursor->sp, -len, SEEK_CUR);
|
pos = krb5_storage_seek(cursor->sp, -len, SEEK_CUR);
|
||||||
goto loop;
|
goto loop;
|
||||||
}
|
}
|
||||||
ret = krb5_kt_ret_principal (context, d, cursor->sp, &entry->principal);
|
ret = krb5_kt_ret_principal (context, d, cursor->sp, &entry->principal);
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
krb5_warnx(context, "fkt_next_entry_int: krb5_kt_ret_principal failed for keytab \"%s\" at pos=%lld: ret=%d", d->filename ? d->filename : "<null>", (long long)pos, ret);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
ret = krb5_ret_uint32(cursor->sp, &utmp32);
|
ret = krb5_ret_uint32(cursor->sp, &utmp32);
|
||||||
entry->timestamp = utmp32;
|
entry->timestamp = utmp32;
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
krb5_warnx(context, "fkt_next_entry_int: failed reading timestamp for keytab \"%s\" at pos=%lld: ret=%d", d->filename ? d->filename : "<null>", (long long)pos, ret);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
ret = krb5_ret_int8(cursor->sp, &tmp8);
|
ret = krb5_ret_int8(cursor->sp, &tmp8);
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
krb5_warnx(context, "fkt_next_entry_int: failed reading vno (8bit) for keytab \"%s\" at pos=%lld: ret=%d", d->filename ? d->filename : "<null>", (long long)pos, ret);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
entry->vno = tmp8;
|
entry->vno = tmp8;
|
||||||
ret = krb5_kt_ret_keyblock (context, d, cursor->sp, &entry->keyblock);
|
ret = krb5_kt_ret_keyblock (context, d, cursor->sp, &entry->keyblock);
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
krb5_warnx(context, "fkt_next_entry_int: krb5_kt_ret_keyblock failed for keytab \"%s\" at pos=%lld: ret=%d", d->filename ? d->filename : "<null>", (long long)pos, ret);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Log the entry we just read (principal, vno, enctype, timestamp) */
|
||||||
|
{
|
||||||
|
char *princname = NULL;
|
||||||
|
(void) krb5_unparse_name(context, entry->principal, &princname);
|
||||||
|
krb5_warnx(context, "fkt_next_entry_int: read entry principal=%s pos=%lld timestamp=%u vno=%d enctype=%d",
|
||||||
|
princname ? princname : "<unparsed>",
|
||||||
|
(long long)pos,
|
||||||
|
(unsigned)entry->timestamp,
|
||||||
|
entry->vno,
|
||||||
|
entry->keyblock.keytype);
|
||||||
|
free(princname);
|
||||||
|
}
|
||||||
|
|
||||||
/* there might be a 32 bit kvno here
|
/* there might be a 32 bit kvno here
|
||||||
* if it's zero, assume that the 8bit one was right,
|
* if it's zero, assume that the 8bit one was right,
|
||||||
* otherwise trust the new value */
|
* otherwise trust the new value */
|
||||||
@@ -492,8 +541,10 @@ loop:
|
|||||||
if(start) *start = pos;
|
if(start) *start = pos;
|
||||||
if(end) *end = pos + 4 + len;
|
if(end) *end = pos + 4 + len;
|
||||||
out:
|
out:
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
krb5_warnx(context, "fkt_next_entry_int: error reading keytab \"%s\" at pos=%lld: ret=%d", d->filename ? d->filename : "<null>", (long long)pos, ret);
|
||||||
krb5_kt_free_entry(context, entry);
|
krb5_kt_free_entry(context, entry);
|
||||||
|
}
|
||||||
krb5_storage_seek(cursor->sp, pos + 4 + len, SEEK_SET);
|
krb5_storage_seek(cursor->sp, pos + 4 + len, SEEK_SET);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1466,6 +1466,11 @@ krb5_sname_to_principal(krb5_context context,
|
|||||||
for (cp = remote_host; *cp; cp++)
|
for (cp = remote_host; *cp; cp++)
|
||||||
if (isupper((unsigned char) (*cp)))
|
if (isupper((unsigned char) (*cp)))
|
||||||
*cp = tolower((unsigned char) (*cp));
|
*cp = tolower((unsigned char) (*cp));
|
||||||
|
/* Debug-ish: record the input/normalized hostname and service for tracing. */
|
||||||
|
krb5_warnx(context, "krb5_sname_to_principal: remote_host=%s sname=%s type=%d",
|
||||||
|
remote_host ? remote_host : "<null>",
|
||||||
|
sname ? sname : "<null>",
|
||||||
|
(int)type);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there is only one name canon rule and it says to
|
* If there is only one name canon rule and it says to
|
||||||
@@ -1473,7 +1478,7 @@ krb5_sname_to_principal(krb5_context context,
|
|||||||
*/
|
*/
|
||||||
ret = _krb5_get_name_canon_rules(context, &rules);
|
ret = _krb5_get_name_canon_rules(context, &rules);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
_krb5_debug(context, 5, "Failed to get name canon rules: ret = %d",
|
krb5_warnx(context, "Failed to get name canon rules: ret = %d",
|
||||||
ret);
|
ret);
|
||||||
free(remote_host);
|
free(remote_host);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1895,6 +1900,17 @@ apply_name_canon_rule(krb5_context context, krb5_name_canon_rule rules,
|
|||||||
|
|
||||||
_krb5_debug(context, 5, N_("Applying a name rule (type %d) to %s", ""),
|
_krb5_debug(context, 5, N_("Applying a name rule (type %d) to %s", ""),
|
||||||
rule->type, orig_hostname);
|
rule->type, orig_hostname);
|
||||||
|
/* Extra debug: print rule details to help trace name canonicalization. */
|
||||||
|
krb5_warnx(context, "name_canon: rule_idx=%zu type=%d mindots=%u maxdots=%u match_domain=%s match_realm=%s realm=%s domain=%s options=0x%x",
|
||||||
|
rule_idx,
|
||||||
|
(int)rule->type,
|
||||||
|
(unsigned)rule->mindots,
|
||||||
|
(unsigned)rule->maxdots,
|
||||||
|
rule->match_domain ? rule->match_domain : "<null>",
|
||||||
|
rule->match_realm ? rule->match_realm : "<null>",
|
||||||
|
rule->realm ? rule->realm : "<null>",
|
||||||
|
rule->domain ? rule->domain : "<null>",
|
||||||
|
(int)rule->options);
|
||||||
|
|
||||||
if (rule->mindots > 0 || rule->maxdots > 0) {
|
if (rule->mindots > 0 || rule->maxdots > 0) {
|
||||||
for (cp = strchr(orig_hostname, '.'); cp && *cp; cp = strchr(cp + 1, '.'))
|
for (cp = strchr(orig_hostname, '.'); cp && *cp; cp = strchr(cp + 1, '.'))
|
||||||
@@ -1941,23 +1957,25 @@ apply_name_canon_rule(krb5_context context, krb5_name_canon_rule rules,
|
|||||||
orig_hostname, sname,
|
orig_hostname, sname,
|
||||||
KRB5_NT_SRV_HST,
|
KRB5_NT_SRV_HST,
|
||||||
&nss);
|
&nss);
|
||||||
|
/* If subsequent rule exists and we got a hostname/realm error,
|
||||||
|
* treat this rule as inapplicable (unless last rule). */
|
||||||
if (rules[rule_idx + 1].type != KRB5_NCRT_BOGUS &&
|
if (rules[rule_idx + 1].type != KRB5_NCRT_BOGUS &&
|
||||||
(ret == KRB5_ERR_BAD_HOSTNAME ||
|
(ret == KRB5_ERR_BAD_HOSTNAME ||
|
||||||
ret == KRB5_ERR_HOST_REALM_UNKNOWN)) {
|
ret == KRB5_ERR_HOST_REALM_UNKNOWN)) {
|
||||||
/*
|
|
||||||
* Bad hostname / realm unknown -> rule inapplicable if
|
|
||||||
* there's more rules. If it's the last rule then we want
|
|
||||||
* to return all errors from krb5_sname_to_principal_old()
|
|
||||||
* here.
|
|
||||||
*/
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
_krb5_debug(context, 3, "krb5_sname_to_principal_old failed for %s: ret=%d", orig_hostname, ret);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
new_hostname = krb5_principal_get_comp_string(context, nss, 1);
|
new_hostname = krb5_principal_get_comp_string(context, nss, 1);
|
||||||
new_realm = krb5_principal_get_realm(context, nss);
|
new_realm = krb5_principal_get_realm(context, nss);
|
||||||
|
/* Log what nss resolution produced for inspection in tests */
|
||||||
|
_krb5_debug(context, 3, "krb5_sname_to_principal_old: resolved %s -> canonical=%s realm=%s", orig_hostname,
|
||||||
|
new_hostname ? new_hostname : "<null>",
|
||||||
|
new_realm ? new_realm : "<null>");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ libroken_la_OBJS = \
|
|||||||
$(OBJ)\parse_bytes.obj \
|
$(OBJ)\parse_bytes.obj \
|
||||||
$(OBJ)\parse_time.obj \
|
$(OBJ)\parse_time.obj \
|
||||||
$(OBJ)\parse_units.obj \
|
$(OBJ)\parse_units.obj \
|
||||||
$(OBJ)\pread.obj \
|
|
||||||
$(OBJ)\realloc.obj \
|
$(OBJ)\realloc.obj \
|
||||||
$(OBJ)\rename.obj \
|
$(OBJ)\rename.obj \
|
||||||
$(OBJ)\resolve.obj \
|
$(OBJ)\resolve.obj \
|
||||||
|
|||||||
@@ -1,749 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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,12 +860,6 @@ ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL
|
|||||||
readv(int, const struct iovec *, int);
|
readv(int, const struct iovec *, int);
|
||||||
#endif
|
#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
|
#ifdef NO_PIDFILES
|
||||||
#define rk_pidfile(x) ((void) 0)
|
#define rk_pidfile(x) ((void) 0)
|
||||||
#else
|
#else
|
||||||
@@ -896,7 +890,6 @@ ROKEN_LIB_FUNCTION unsigned short ROKEN_LIB_CALL bswap16(unsigned short);
|
|||||||
* - Fallback to system flock() if available
|
* - Fallback to system flock() if available
|
||||||
* - Fallback to POSIX fcntl() locks for NFS and other filesystems
|
* - Fallback to POSIX fcntl() locks for NFS and other filesystems
|
||||||
*/
|
*/
|
||||||
#ifndef HAVE_FLOCK
|
|
||||||
#ifndef LOCK_SH
|
#ifndef LOCK_SH
|
||||||
#define LOCK_SH 1 /* Shared lock */
|
#define LOCK_SH 1 /* Shared lock */
|
||||||
#endif
|
#endif
|
||||||
@@ -909,7 +902,6 @@ ROKEN_LIB_FUNCTION unsigned short ROKEN_LIB_CALL bswap16(unsigned short);
|
|||||||
#ifndef LOCK_UN
|
#ifndef LOCK_UN
|
||||||
#define LOCK_UN 8 /* Unlock */
|
#define LOCK_UN 8 /* Unlock */
|
||||||
#endif
|
#endif
|
||||||
#endif /* !HAVE_FLOCK */
|
|
||||||
|
|
||||||
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_flock(int fd, int operation);
|
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_flock(int fd, int operation);
|
||||||
#define flock(_x,_y) rk_flock(_x,_y)
|
#define flock(_x,_y) rk_flock(_x,_y)
|
||||||
|
|||||||
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
|
||||||
|
|
||||||
@@ -128,8 +128,12 @@ stdenv.mkDerivation {
|
|||||||
];
|
];
|
||||||
|
|
||||||
patches = [
|
patches = [
|
||||||
|
# Proposed @ https://github.com/heimdal/heimdal/pull/1262
|
||||||
|
./0001-Include-db.h-for-nbdb-compat-mode.patch
|
||||||
# Proposed @ https://github.com/heimdal/heimdal/pull/1264
|
# Proposed @ https://github.com/heimdal/heimdal/pull/1264
|
||||||
./0001-Define-HAVE_DB_185_H.patch
|
./0001-Define-HAVE_DB_185_H.patch
|
||||||
|
# Proposed @ https://github.com/heimdal/heimdal/pull/1265
|
||||||
|
./0001-Link-tests-with-libresolv.patch
|
||||||
];
|
];
|
||||||
|
|
||||||
# (check-ldap) slapd resides within ${openldap}/libexec,
|
# (check-ldap) slapd resides within ${openldap}/libexec,
|
||||||
@@ -137,13 +141,16 @@ stdenv.mkDerivation {
|
|||||||
# (check-ldap) prepending ${openldap}/bin to the path to avoid
|
# (check-ldap) prepending ${openldap}/bin to the path to avoid
|
||||||
# using the default installation of openldap on unsandboxed darwin systems,
|
# using the default installation of openldap on unsandboxed darwin systems,
|
||||||
# which does not support the new mdb backend at the moment (2024-01-13).
|
# which does not support the new mdb backend at the moment (2024-01-13).
|
||||||
|
# (check-ldap) the bdb backend got deprecated in favour of mdb in openldap 2.5.0,
|
||||||
|
# but the heimdal tests still seem to expect bdb as the openldap backend.
|
||||||
|
# This might be fixed upstream in a future update.
|
||||||
postPatch = ''
|
postPatch = ''
|
||||||
substituteInPlace tests/ldap/slapd-init.in \
|
substituteInPlace tests/ldap/slapd-init.in \
|
||||||
--replace-fail 'SCHEMA_PATHS="' 'SCHEMA_PATHS="${openldap}/etc/schema '
|
--replace-fail 'SCHEMA_PATHS="' 'SCHEMA_PATHS="${openldap}/etc/schema '
|
||||||
substituteInPlace tests/ldap/check-ldap.in \
|
substituteInPlace tests/ldap/check-ldap.in \
|
||||||
--replace-fail 'PATH=' 'PATH=${openldap}/libexec:${openldap}/bin:'
|
--replace-fail 'PATH=' 'PATH=${openldap}/libexec:${openldap}/bin:'
|
||||||
substituteInPlace tests/ldap/Makefile.am \
|
substituteInPlace tests/ldap/slapd.conf \
|
||||||
--replace-fail 'TESTS = check-ldap' 'TESTS ='
|
--replace-fail 'database bdb' 'database mdb'
|
||||||
substituteInPlace tests/kdc/check-iprop.in \
|
substituteInPlace tests/kdc/check-iprop.in \
|
||||||
--replace-fail '/bin/pwd' 'pwd'
|
--replace-fail '/bin/pwd' 'pwd'
|
||||||
'';
|
'';
|
||||||
@@ -154,7 +161,17 @@ stdenv.mkDerivation {
|
|||||||
export USER=nix-builder
|
export USER=nix-builder
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# We need to build hcrypt for applications like samba
|
||||||
|
postBuild = ''
|
||||||
|
(cd include/hcrypto; make -j $NIX_BUILD_CORES)
|
||||||
|
(cd lib/hcrypto; make -j $NIX_BUILD_CORES)
|
||||||
|
'';
|
||||||
|
|
||||||
postInstall = ''
|
postInstall = ''
|
||||||
|
# Install hcrypto
|
||||||
|
(cd include/hcrypto; make -j $NIX_BUILD_CORES install)
|
||||||
|
(cd lib/hcrypto; make -j $NIX_BUILD_CORES install)
|
||||||
|
|
||||||
mkdir -p $dev/bin
|
mkdir -p $dev/bin
|
||||||
mv $out/bin/krb5-config $dev/bin/
|
mv $out/bin/krb5-config $dev/bin/
|
||||||
|
|
||||||
@@ -159,26 +159,17 @@
|
|||||||
alice_krb_pw = "alice_hunter2"
|
alice_krb_pw = "alice_hunter2"
|
||||||
alice_old_krb_pw = ""
|
alice_old_krb_pw = ""
|
||||||
alice_krb_admin_pw = "alice_admin_hunter2"
|
alice_krb_admin_pw = "alice_admin_hunter2"
|
||||||
bob_krb_pw = "bob_hunter2"
|
|
||||||
|
|
||||||
def random_password():
|
def random_password():
|
||||||
password_chars = string.ascii_letters + string.digits + string.punctuation.replace('"', "")
|
password_chars = string.ascii_letters + string.digits + string.punctuation.replace('"', "")
|
||||||
return "".join(random.choice(password_chars) for _ in range(16))
|
return "".join(random.choice(password_chars) for _ in range(16))
|
||||||
|
|
||||||
def clear_tty(node):
|
|
||||||
node.send_chars("clear\n")
|
|
||||||
ps1 = r"\[alice@\w+:~\]\$ "
|
|
||||||
node.wait_until_tty_matches("1", ps1)
|
|
||||||
|
|
||||||
with subtest("Server: initialize user principals and keytabs"):
|
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(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("kadmin -l ext_keytab --keytab=admin.keytab alice/admin")
|
||||||
|
|
||||||
server.succeed(f'kadmin -p alice/admin add --password="{alice_krb_pw}" --use-defaults alice')
|
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.succeed("kadmin -l ext_keytab --keytab=alice.keytab alice")
|
||||||
|
|
||||||
server.succeed("kadmin -p alice/admin add --password={bob_krb_pw} --use-defaults bob")
|
|
||||||
# server.succeed("kadmin -l ext_keytab --keytab=bob.keytab bob")
|
|
||||||
|
|
||||||
server.wait_for_unit("getty@tty1.service")
|
server.wait_for_unit("getty@tty1.service")
|
||||||
server.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
|
server.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
|
||||||
@@ -196,7 +187,7 @@
|
|||||||
if not "host/server.foo.bar" in ktutil_list:
|
if not "host/server.foo.bar" in ktutil_list:
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
clear_tty(server)
|
server.send_chars("clear\n")
|
||||||
|
|
||||||
client.systemctl("start network-online.target")
|
client.systemctl("start network-online.target")
|
||||||
client.wait_for_unit("network-online.target")
|
client.wait_for_unit("network-online.target")
|
||||||
@@ -221,7 +212,7 @@
|
|||||||
if not "host/client.foo.bar" in ktutil_list:
|
if not "host/client.foo.bar" in ktutil_list:
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
clear_tty(client)
|
client.send_chars("clear\n")
|
||||||
|
|
||||||
with subtest("Client: kinit alice"):
|
with subtest("Client: kinit alice"):
|
||||||
client.succeed(
|
client.succeed(
|
||||||
@@ -230,7 +221,7 @@
|
|||||||
)
|
)
|
||||||
tickets = client.succeed("klist")
|
tickets = client.succeed("klist")
|
||||||
assert "Principal: alice@FOO.BAR" in tickets
|
assert "Principal: alice@FOO.BAR" in tickets
|
||||||
clear_tty(client)
|
client.send_chars("clear\n")
|
||||||
|
|
||||||
with subtest("Client: kpasswd alice"):
|
with subtest("Client: kpasswd alice"):
|
||||||
alice_old_krb_pw = alice_krb_pw
|
alice_old_krb_pw = alice_krb_pw
|
||||||
@@ -245,14 +236,7 @@
|
|||||||
|
|
||||||
client.wait_until_tty_matches("1", "Success : Password changed")
|
client.wait_until_tty_matches("1", "Success : Password changed")
|
||||||
|
|
||||||
clear_tty(client)
|
client.send_chars("clear\n")
|
||||||
|
|
||||||
with subtest("Client: kadmin get bob"):
|
|
||||||
client.send_chars("sudo kadmin -p alice/admin -K admin.keytab get bob\n")
|
|
||||||
client.wait_until_tty_matches("1", "alice/admin@FOO.BAR's Password:")
|
|
||||||
client.send_chars(f"{alice_krb_admin_pw}\n")
|
|
||||||
client.wait_until_tty_matches("1", "Principal: bob@FOO.BAR")
|
|
||||||
clear_tty(client)
|
|
||||||
|
|
||||||
with subtest("Server: kinit alice"):
|
with subtest("Server: kinit alice"):
|
||||||
server.succeed(
|
server.succeed(
|
||||||
@@ -261,7 +245,7 @@
|
|||||||
)
|
)
|
||||||
tickets = client.succeed("klist")
|
tickets = client.succeed("klist")
|
||||||
assert "Principal: alice@FOO.BAR" in tickets
|
assert "Principal: alice@FOO.BAR" in tickets
|
||||||
clear_tty(server)
|
server.send_chars("clear\n")
|
||||||
|
|
||||||
with subtest("Server: kpasswd alice"):
|
with subtest("Server: kpasswd alice"):
|
||||||
alice_old_krb_pw = alice_krb_pw
|
alice_old_krb_pw = alice_krb_pw
|
||||||
@@ -276,14 +260,7 @@
|
|||||||
|
|
||||||
server.wait_until_tty_matches("1", "Success : Password changed")
|
server.wait_until_tty_matches("1", "Success : Password changed")
|
||||||
|
|
||||||
clear_tty(server)
|
server.send_chars("clear\n")
|
||||||
|
|
||||||
with subtest("Server: kadmin get bob"):
|
|
||||||
server.send_chars("sudo kadmin -p alice/admin -K admin.keytab get bob\n")
|
|
||||||
server.wait_until_tty_matches("1", "alice/admin@FOO.BAR's Password:")
|
|
||||||
server.send_chars(f"{alice_krb_admin_pw}\n")
|
|
||||||
server.wait_until_tty_matches("1", "Principal: bob@FOO.BAR")
|
|
||||||
clear_tty(server)
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
meta.maintainers = pkgs.heimdal.meta.maintainers;
|
meta.maintainers = pkgs.heimdal.meta.maintainers;
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ test_run_x() {
|
|||||||
|
|
||||||
# Run with tracing enabled
|
# Run with tracing enabled
|
||||||
(
|
(
|
||||||
set -vx
|
set -x
|
||||||
"$@"
|
"$@"
|
||||||
) > "$cmd_out" 2>"$trace_out"
|
) > "$cmd_out" 2>"$trace_out"
|
||||||
rc=$?
|
rc=$?
|
||||||
|
|||||||
@@ -77,8 +77,6 @@ rm -f mkey.file*
|
|||||||
|
|
||||||
test_init
|
test_init
|
||||||
|
|
||||||
alias test_run=test_run_x
|
|
||||||
|
|
||||||
test_section "Creating database"
|
test_section "Creating database"
|
||||||
# add both lucid and lucid.test.h5l.se to simulate aliases
|
# add both lucid and lucid.test.h5l.se to simulate aliases
|
||||||
# XXX ext should ext aliases too
|
# XXX ext should ext aliases too
|
||||||
|
|||||||
@@ -31,6 +31,9 @@
|
|||||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
# SUCH DAMAGE.
|
# SUCH DAMAGE.
|
||||||
|
|
||||||
|
# Skip until hierarchical iprop deadlock is fixed
|
||||||
|
exit 77
|
||||||
|
|
||||||
top_builddir="@top_builddir@"
|
top_builddir="@top_builddir@"
|
||||||
env_setup="@env_setup@"
|
env_setup="@env_setup@"
|
||||||
objdir="@objdir@"
|
objdir="@objdir@"
|
||||||
@@ -282,14 +285,14 @@ rm -f iprop-slave-status iprop-slave-status2
|
|||||||
|
|
||||||
ipropd_slave2=$ipropd_slave
|
ipropd_slave2=$ipropd_slave
|
||||||
ipropd_master2=$ipropd_master
|
ipropd_master2=$ipropd_master
|
||||||
ipropd_slave="${ipropd_slave} --verbose --status-file=iprop-slave-status --port=$ipropport"
|
ipropd_slave="${ipropd_slave} --status-file=iprop-slave-status --port=$ipropport"
|
||||||
ipropd_slave="${ipropd_slave} --hostname=slave.test.h5l.se -k ${keytab}"
|
ipropd_slave="${ipropd_slave} --hostname=slave.test.h5l.se -k ${keytab}"
|
||||||
ipropd_slave="${ipropd_slave} --detach localhost"
|
ipropd_slave="${ipropd_slave} --detach localhost"
|
||||||
ipropd_master="${ipropd_master} --verbose --hostname=localhost -k ${keytab}"
|
ipropd_master="${ipropd_master} --hostname=localhost -k ${keytab}"
|
||||||
ipropd_master="${ipropd_master} --port=$ipropport"
|
ipropd_master="${ipropd_master} --port=$ipropport"
|
||||||
ipropd_master="${ipropd_master} --database=${objdir}/current-db --detach"
|
ipropd_master="${ipropd_master} --database=${objdir}/current-db --detach"
|
||||||
|
|
||||||
ipropd_slave2="${ipropd_slave2} --verbose --status-file=iprop-slave-status2 --port=$ipropport2"
|
ipropd_slave2="${ipropd_slave2} --status-file=iprop-slave-status2 --port=$ipropport2"
|
||||||
ipropd_slave2="${ipropd_slave2} --hostname=slave.test.h5l.se -k ${keytab}"
|
ipropd_slave2="${ipropd_slave2} --hostname=slave.test.h5l.se -k ${keytab}"
|
||||||
ipropd_slave2="${ipropd_slave2} --pidfile-basename=ipropd-slave2"
|
ipropd_slave2="${ipropd_slave2} --pidfile-basename=ipropd-slave2"
|
||||||
ipropd_slave2="${ipropd_slave2} --detach localhost"
|
ipropd_slave2="${ipropd_slave2} --detach localhost"
|
||||||
@@ -551,7 +554,7 @@ wait_for_master_down
|
|||||||
wait_for "slave to disconnect" \
|
wait_for "slave to disconnect" \
|
||||||
${EGREP} 'disconnected' iprop-slave-status >/dev/null
|
${EGREP} 'disconnected' iprop-slave-status >/dev/null
|
||||||
|
|
||||||
if ! tail -30 messages.log | grep 'disconnected from server' > /dev/null; then
|
if ! tail -30 messages.log | grep 'disconnected for server' > /dev/null; then
|
||||||
echo "client didnt disconnect"
|
echo "client didnt disconnect"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user