Compare commits
3 Commits
master
...
kadmin-get
| Author | SHA1 | Date | |
|---|---|---|---|
|
845fb73ad1
|
|||
|
d0b5821e9a
|
|||
|
072c1f062f
|
8
.zed/settings.json
Normal file
8
.zed/settings.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Folder-specific settings
|
||||||
|
//
|
||||||
|
// For a full list of overridable settings, and general information on folder-specific settings,
|
||||||
|
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
|
||||||
|
{
|
||||||
|
"tab_size": 8,
|
||||||
|
"hard_tabs": false
|
||||||
|
}
|
||||||
27
flake.lock
generated
Normal file
27
flake.lock
generated
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1742800061,
|
||||||
|
"narHash": "sha256-oDJGK1UMArK52vcW9S5S2apeec4rbfNELgc50LqiPNs=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "1750f3c1c89488e2ffdd47cab9d05454dddfb734",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
48
flake.nix
Normal file
48
flake.nix
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs }: let
|
||||||
|
inherit (nixpkgs) lib;
|
||||||
|
|
||||||
|
systems = [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
];
|
||||||
|
|
||||||
|
forAllSystems = f: lib.genAttrs systems (system: let
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
overlays = [
|
||||||
|
self.overlays.${system}.default
|
||||||
|
];
|
||||||
|
};
|
||||||
|
in f system pkgs);
|
||||||
|
in {
|
||||||
|
devShells = forAllSystems (system: pkgs: {
|
||||||
|
default = pkgs.callPackage ./nix/shell.nix { };
|
||||||
|
});
|
||||||
|
|
||||||
|
packages = forAllSystems (system: pkgs: with pkgs; {
|
||||||
|
default = self.packages.${system}.heimdal;
|
||||||
|
|
||||||
|
heimdal = pkgs.callPackage ./nix/heimdal {
|
||||||
|
src = lib.cleanSource ./.;
|
||||||
|
inherit (pkgs.darwin.apple_sdk.frameworks) CoreFoundation Security SystemConfiguration;
|
||||||
|
autoreconfHook = pkgs.buildPackages.autoreconfHook269;
|
||||||
|
};
|
||||||
|
|
||||||
|
nixosTest = pkgs.testers.runNixOSTest (import ./nix/nixosTest.nix { inherit nixpkgs; });
|
||||||
|
});
|
||||||
|
|
||||||
|
overlays = forAllSystems (system: pkgs: {
|
||||||
|
default = final: prev: {
|
||||||
|
heimdal = self.packages.${system}.heimdal;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
nixosModules = {
|
||||||
|
default = self.nixosModules.heimdal;
|
||||||
|
heimdal = ./nix/module;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +240,16 @@ 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);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
if (in->service_name && (ctx->service_name = strdup(in->service_name)) == NULL)
|
||||||
|
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;
|
||||||
|
|
||||||
if (in->keytab && (ctx->keytab = strdup(in->keytab)) == NULL)
|
if (in->keytab && (ctx->keytab = strdup(in->keytab)) == NULL)
|
||||||
ret = krb5_enomem(context);
|
ret = krb5_enomem(context);
|
||||||
if (in->ccache) {
|
if (in->ccache) {
|
||||||
@@ -292,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);
|
||||||
@@ -309,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,
|
||||||
@@ -320,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){
|
||||||
@@ -448,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)
|
||||||
@@ -502,14 +584,20 @@ _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;
|
||||||
@@ -522,6 +610,21 @@ _kadm5_c_get_cred_cache(krb5_context context,
|
|||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -545,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;
|
||||||
@@ -658,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;
|
||||||
}
|
}
|
||||||
@@ -704,6 +824,20 @@ 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;
|
||||||
@@ -736,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -922,4 +1069,3 @@ kadm5_init(char *client_name, char *pass,
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
26
nix/heimdal/0001-Define-HAVE_DB_185_H.patch
Normal file
26
nix/heimdal/0001-Define-HAVE_DB_185_H.patch
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
From 08d719e96214f648ae95043acc308deca36e1f7a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Ihar Hrachyshka <ihar.hrachyshka@gmail.com>
|
||||||
|
Date: Tue, 15 Oct 2024 13:52:39 -0400
|
||||||
|
Subject: [PATCH] Define HAVE_DB_185_H
|
||||||
|
|
||||||
|
---
|
||||||
|
cf/db.m4 | 3 +++
|
||||||
|
1 file changed, 3 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/cf/db.m4 b/cf/db.m4
|
||||||
|
index c0b4510b6..c95a9dee9 100644
|
||||||
|
--- a/cf/db.m4
|
||||||
|
+++ b/cf/db.m4
|
||||||
|
@@ -57,6 +57,9 @@ AS_IF([test "x$with_berkeley_db" != xno],
|
||||||
|
db.h \
|
||||||
|
])])
|
||||||
|
|
||||||
|
+dnl detect if compat db_185.h is present
|
||||||
|
+AC_CHECK_HEADERS([db_185.h])
|
||||||
|
+
|
||||||
|
dnl db_create is used by db3 and db4 and db5 and db6
|
||||||
|
|
||||||
|
AC_FIND_FUNC_NO_LIBS(db_create, [$dbheader] db-6 db-5 db4 db3 db, [
|
||||||
|
--
|
||||||
|
2.46.0
|
||||||
|
|
||||||
25
nix/heimdal/0001-Include-db.h-for-nbdb-compat-mode.patch
Normal file
25
nix/heimdal/0001-Include-db.h-for-nbdb-compat-mode.patch
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
From 749d9451293f9d9f8a3f506401cae369003aeebf Mon Sep 17 00:00:00 2001
|
||||||
|
From: Ihar Hrachyshka <ihar.hrachyshka@gmail.com>
|
||||||
|
Date: Sun, 13 Oct 2024 17:16:13 -0400
|
||||||
|
Subject: [PATCH] Include db.h for nbdb compat mode
|
||||||
|
|
||||||
|
---
|
||||||
|
lib/otp/otp_db.c | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/lib/otp/otp_db.c b/lib/otp/otp_db.c
|
||||||
|
index 036359c1d..32c04bc8c 100644
|
||||||
|
--- a/lib/otp/otp_db.c
|
||||||
|
+++ b/lib/otp/otp_db.c
|
||||||
|
@@ -39,7 +39,7 @@ RCSID("$Id$");
|
||||||
|
#include "otp_locl.h"
|
||||||
|
|
||||||
|
#if defined(HAVE_DB_NDBM)
|
||||||
|
-# include <ndbm.h>
|
||||||
|
+# include <db.h>
|
||||||
|
#elif !defined(HAVE_NDBM)
|
||||||
|
# include "ndbm_wrap.h"
|
||||||
|
#endif
|
||||||
|
--
|
||||||
|
2.46.0
|
||||||
|
|
||||||
51
nix/heimdal/0001-Link-tests-with-libresolv.patch
Normal file
51
nix/heimdal/0001-Link-tests-with-libresolv.patch
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
From 862900febaec4a2c70257a39374b81138ee9f168 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Ihar Hrachyshka <ihar.hrachyshka@gmail.com>
|
||||||
|
Date: Tue, 15 Oct 2024 16:06:33 -0400
|
||||||
|
Subject: [PATCH] Link tests with libresolv
|
||||||
|
|
||||||
|
---
|
||||||
|
lib/gssapi/Makefile.am | 1 +
|
||||||
|
lib/krb5/Makefile.am | 2 ++
|
||||||
|
lib/roken/Makefile.am | 1 +
|
||||||
|
3 files changed, 4 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/lib/gssapi/Makefile.am b/lib/gssapi/Makefile.am
|
||||||
|
index 3254866dc..db967e586 100644
|
||||||
|
--- a/lib/gssapi/Makefile.am
|
||||||
|
+++ b/lib/gssapi/Makefile.am
|
||||||
|
@@ -403,6 +403,7 @@ LDADD = libgssapi.la \
|
||||||
|
$(top_builddir)/lib/krb5/libkrb5.la \
|
||||||
|
$(LIB_roken)
|
||||||
|
|
||||||
|
+test_names_LDFLAGS = -lresolv
|
||||||
|
test_names_LDADD = $(LDADD) $(top_builddir)/lib/asn1/libasn1.la
|
||||||
|
test_context_LDADD = $(LDADD) $(top_builddir)/lib/asn1/libasn1.la $(top_builddir)/lib/wind/libwind.la
|
||||||
|
|
||||||
|
diff --git a/lib/krb5/Makefile.am b/lib/krb5/Makefile.am
|
||||||
|
index ecce461dd..e22cfe87c 100644
|
||||||
|
--- a/lib/krb5/Makefile.am
|
||||||
|
+++ b/lib/krb5/Makefile.am
|
||||||
|
@@ -330,6 +330,8 @@ test_rfc3961_LDADD = \
|
||||||
|
$(LIB_hcrypto) \
|
||||||
|
$(LIB_roken)
|
||||||
|
|
||||||
|
+test_plugin_LDFLAGS = -lresolv
|
||||||
|
+
|
||||||
|
if DEVELOPER_MODE
|
||||||
|
headerdeps = $(dist_libkrb5_la_SOURCES)
|
||||||
|
endif
|
||||||
|
diff --git a/lib/roken/Makefile.am b/lib/roken/Makefile.am
|
||||||
|
index 1f530c7ae..8350d7034 100644
|
||||||
|
--- a/lib/roken/Makefile.am
|
||||||
|
+++ b/lib/roken/Makefile.am
|
||||||
|
@@ -54,6 +54,7 @@ libtest_la_CFLAGS = -DTEST_SNPRINTF -DTEST_STRPFTIME
|
||||||
|
|
||||||
|
parse_reply_test_SOURCES = parse_reply-test.c resolve.c
|
||||||
|
parse_reply_test_CFLAGS = -DTEST_RESOLVE
|
||||||
|
+parse_reply_test_LDFLAGS = -lresolv
|
||||||
|
|
||||||
|
test_readenv_SOURCES = test-readenv.c test-mem.c
|
||||||
|
test_auxval_SOURCES = test-auxval.c
|
||||||
|
--
|
||||||
|
2.46.0
|
||||||
|
|
||||||
204
nix/heimdal/default.nix
Normal file
204
nix/heimdal/default.nix
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
{
|
||||||
|
src,
|
||||||
|
|
||||||
|
lib,
|
||||||
|
stdenv,
|
||||||
|
fetchFromGitHub,
|
||||||
|
autoreconfHook,
|
||||||
|
pkg-config,
|
||||||
|
python3,
|
||||||
|
perl,
|
||||||
|
bison,
|
||||||
|
flex,
|
||||||
|
texinfo,
|
||||||
|
perlPackages,
|
||||||
|
|
||||||
|
openldap,
|
||||||
|
libcap_ng,
|
||||||
|
sqlite,
|
||||||
|
openssl,
|
||||||
|
db,
|
||||||
|
libedit,
|
||||||
|
pam,
|
||||||
|
libmicrohttpd,
|
||||||
|
cjson,
|
||||||
|
|
||||||
|
CoreFoundation,
|
||||||
|
Security,
|
||||||
|
SystemConfiguration,
|
||||||
|
|
||||||
|
curl,
|
||||||
|
jdk_headless,
|
||||||
|
unzip,
|
||||||
|
which,
|
||||||
|
|
||||||
|
nixosTests,
|
||||||
|
|
||||||
|
withCJSON ? true,
|
||||||
|
withCapNG ? stdenv.hostPlatform.isLinux,
|
||||||
|
# libmicrohttpd should theoretically work for darwin as well, but something is broken.
|
||||||
|
# It affects tests check-bx509d and check-httpkadmind.
|
||||||
|
withMicroHTTPD ? stdenv.hostPlatform.isLinux,
|
||||||
|
withOpenLDAP ? true,
|
||||||
|
withOpenLDAPAsHDBModule ? false,
|
||||||
|
withOpenSSL ? true,
|
||||||
|
withSQLite3 ? true,
|
||||||
|
}:
|
||||||
|
|
||||||
|
assert lib.assertMsg (withOpenLDAPAsHDBModule -> withOpenLDAP) ''
|
||||||
|
OpenLDAP needs to be enabled in order to build the OpenLDAP HDB Module.
|
||||||
|
'';
|
||||||
|
|
||||||
|
stdenv.mkDerivation {
|
||||||
|
pname = "heimdal";
|
||||||
|
version = "7.8.0-unstable-local";
|
||||||
|
|
||||||
|
inherit src;
|
||||||
|
|
||||||
|
outputs = [
|
||||||
|
"out"
|
||||||
|
"dev"
|
||||||
|
"man"
|
||||||
|
"info"
|
||||||
|
];
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
autoreconfHook
|
||||||
|
pkg-config
|
||||||
|
python3
|
||||||
|
perl
|
||||||
|
bison
|
||||||
|
flex
|
||||||
|
perlPackages.JSON
|
||||||
|
texinfo
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs =
|
||||||
|
[
|
||||||
|
db
|
||||||
|
libedit
|
||||||
|
pam
|
||||||
|
]
|
||||||
|
++ lib.optionals (stdenv.hostPlatform.isDarwin) [
|
||||||
|
CoreFoundation
|
||||||
|
Security
|
||||||
|
SystemConfiguration
|
||||||
|
]
|
||||||
|
++ lib.optionals (withCJSON) [ cjson ]
|
||||||
|
++ lib.optionals (withCapNG) [ libcap_ng ]
|
||||||
|
++ lib.optionals (withMicroHTTPD) [ libmicrohttpd ]
|
||||||
|
++ lib.optionals (withOpenLDAP) [ openldap ]
|
||||||
|
++ lib.optionals (withOpenSSL) [ openssl ]
|
||||||
|
++ lib.optionals (withSQLite3) [ sqlite ];
|
||||||
|
|
||||||
|
doCheck = true;
|
||||||
|
nativeCheckInputs = [
|
||||||
|
curl
|
||||||
|
jdk_headless
|
||||||
|
unzip
|
||||||
|
which
|
||||||
|
];
|
||||||
|
|
||||||
|
configureFlags =
|
||||||
|
[
|
||||||
|
"--with-hdbdir=/var/lib/heimdal"
|
||||||
|
|
||||||
|
"--with-libedit-include=${libedit.dev}/include"
|
||||||
|
"--with-libedit-lib=${libedit}/lib"
|
||||||
|
"--with-berkeley-db-include=${db.dev}/include"
|
||||||
|
"--with-berkeley-db"
|
||||||
|
|
||||||
|
"--without-x"
|
||||||
|
"--disable-afs-string-to-key"
|
||||||
|
]
|
||||||
|
++ lib.optionals (withCapNG) [
|
||||||
|
"--with-capng"
|
||||||
|
]
|
||||||
|
++ lib.optionals (withCJSON) [
|
||||||
|
"--with-cjson=${cjson}"
|
||||||
|
]
|
||||||
|
++ lib.optionals (withOpenLDAP) [
|
||||||
|
"--with-openldap=${openldap.dev}"
|
||||||
|
]
|
||||||
|
++ lib.optionals (withOpenLDAPAsHDBModule) [
|
||||||
|
"--enable-hdb-openldap-module"
|
||||||
|
]
|
||||||
|
++ lib.optionals (withSQLite3) [
|
||||||
|
"--with-sqlite3=${sqlite.dev}"
|
||||||
|
];
|
||||||
|
|
||||||
|
patches = [
|
||||||
|
# Proposed @ https://github.com/heimdal/heimdal/pull/1262
|
||||||
|
./0001-Include-db.h-for-nbdb-compat-mode.patch
|
||||||
|
# Proposed @ https://github.com/heimdal/heimdal/pull/1264
|
||||||
|
./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,
|
||||||
|
# which is not part of $PATH by default.
|
||||||
|
# (check-ldap) prepending ${openldap}/bin to the path to avoid
|
||||||
|
# using the default installation of openldap on unsandboxed darwin systems,
|
||||||
|
# which does not support the new mdb backend at the moment (2024-01-13).
|
||||||
|
# (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 = ''
|
||||||
|
substituteInPlace tests/ldap/slapd-init.in \
|
||||||
|
--replace-fail 'SCHEMA_PATHS="' 'SCHEMA_PATHS="${openldap}/etc/schema '
|
||||||
|
substituteInPlace tests/ldap/check-ldap.in \
|
||||||
|
--replace-fail 'PATH=' 'PATH=${openldap}/libexec:${openldap}/bin:'
|
||||||
|
substituteInPlace tests/ldap/slapd.conf \
|
||||||
|
--replace-fail 'database bdb' 'database mdb'
|
||||||
|
substituteInPlace tests/kdc/check-iprop.in \
|
||||||
|
--replace-fail '/bin/pwd' 'pwd'
|
||||||
|
'';
|
||||||
|
|
||||||
|
# (test_cc) heimdal uses librokens implementation of `secure_getenv` on darwin,
|
||||||
|
# which expects either USER or LOGNAME to be set.
|
||||||
|
preCheck = lib.optionalString (stdenv.hostPlatform.isDarwin) ''
|
||||||
|
export USER=nix-builder
|
||||||
|
'';
|
||||||
|
|
||||||
|
# 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 = ''
|
||||||
|
# Install hcrypto
|
||||||
|
(cd include/hcrypto; make -j $NIX_BUILD_CORES install)
|
||||||
|
(cd lib/hcrypto; make -j $NIX_BUILD_CORES install)
|
||||||
|
|
||||||
|
mkdir -p $dev/bin
|
||||||
|
mv $out/bin/krb5-config $dev/bin/
|
||||||
|
|
||||||
|
# asn1 compilers, move them to $dev
|
||||||
|
mv $out/libexec/heimdal/* $dev/bin
|
||||||
|
rmdir $out/libexec/heimdal
|
||||||
|
|
||||||
|
# compile_et is needed for cross-compiling this package and samba
|
||||||
|
mv lib/com_err/.libs/compile_et $dev/bin
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Issues with hydra
|
||||||
|
# In file included from hxtool.c:34:0:
|
||||||
|
# hx_locl.h:67:25: fatal error: pkcs10_asn1.h: No such file or directory
|
||||||
|
#enableParallelBuilding = true;
|
||||||
|
|
||||||
|
passthru = {
|
||||||
|
implementation = "heimdal";
|
||||||
|
tests.nixos = nixosTests.kerberos.heimdal;
|
||||||
|
};
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
homepage = "https://www.heimdal.software";
|
||||||
|
changelog = "https://github.com/heimdal/heimdal/releases";
|
||||||
|
description = "Implementation of Kerberos 5 (and some more stuff)";
|
||||||
|
license = licenses.bsd3;
|
||||||
|
platforms = platforms.unix;
|
||||||
|
maintainers = with maintainers; [ h7x4 ];
|
||||||
|
};
|
||||||
|
}
|
||||||
69
nix/module/default.nix
Normal file
69
nix/module/default.nix
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (lib) mkOption types;
|
||||||
|
cfg = config.services.kerberos_server;
|
||||||
|
inherit (config.security.krb5) package;
|
||||||
|
|
||||||
|
format = import ./krb5-conf-format.nix { inherit pkgs lib; } {
|
||||||
|
enableKdcACLEntries = true;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
(lib.mkRenamedOptionModule
|
||||||
|
[ "services" "kerberos_server" "realms" ]
|
||||||
|
[ "services" "kerberos_server" "settings" "realms" ]
|
||||||
|
)
|
||||||
|
|
||||||
|
# ./mit.nix
|
||||||
|
./heimdal.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
options = {
|
||||||
|
services.kerberos_server = {
|
||||||
|
enable = lib.mkEnableOption "the kerberos authentication server";
|
||||||
|
|
||||||
|
settings = mkOption {
|
||||||
|
type = format.type;
|
||||||
|
description = ''
|
||||||
|
Settings for the kerberos server of choice.
|
||||||
|
|
||||||
|
See the following documentation:
|
||||||
|
- Heimdal: {manpage}`kdc.conf(5)`
|
||||||
|
- MIT Kerberos: <https://web.mit.edu/kerberos/krb5-1.21/doc/admin/conf_files/kdc_conf.html>
|
||||||
|
'';
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
environment.systemPackages = [ package ];
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = cfg.settings.realms != { };
|
||||||
|
message = "The server needs at least one realm";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = lib.length (lib.attrNames cfg.settings.realms) <= 1;
|
||||||
|
message = "Only one realm per server is currently supported.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.slices.system-kerberos-server = { };
|
||||||
|
systemd.targets.kerberos-server = {
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# meta = {
|
||||||
|
# doc = ./kerberos-server.md;
|
||||||
|
# };
|
||||||
|
}
|
||||||
105
nix/module/heimdal.nix
Normal file
105
nix/module/heimdal.nix
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (lib) mapAttrs;
|
||||||
|
cfg = config.services.kerberos_server;
|
||||||
|
package = config.security.krb5.package;
|
||||||
|
|
||||||
|
aclConfigs = lib.pipe cfg.settings.realms [
|
||||||
|
(mapAttrs (
|
||||||
|
name:
|
||||||
|
{ acl, ... }:
|
||||||
|
lib.concatMapStringsSep "\n" (
|
||||||
|
{
|
||||||
|
principal,
|
||||||
|
access,
|
||||||
|
target,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
"${principal}\t${lib.concatStringsSep "," (lib.toList access)}\t${target}"
|
||||||
|
) acl
|
||||||
|
))
|
||||||
|
(lib.mapAttrsToList (
|
||||||
|
name: text: {
|
||||||
|
dbname = "/var/lib/heimdal/heimdal";
|
||||||
|
acl_file = pkgs.writeText "${name}.acl" text;
|
||||||
|
}
|
||||||
|
))
|
||||||
|
];
|
||||||
|
|
||||||
|
finalConfig = cfg.settings // {
|
||||||
|
realms = mapAttrs (_: v: removeAttrs v [ "acl" ]) (cfg.settings.realms or { });
|
||||||
|
kdc = (cfg.settings.kdc or { }) // {
|
||||||
|
database = aclConfigs;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
format = import ./krb5-conf-format.nix { inherit pkgs lib; } {
|
||||||
|
enableKdcACLEntries = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
kdcConfFile = format.generate "kdc.conf" finalConfig;
|
||||||
|
in
|
||||||
|
|
||||||
|
{
|
||||||
|
config = lib.mkIf (cfg.enable && package.passthru.implementation == "heimdal") {
|
||||||
|
environment.etc."heimdal-kdc/kdc.conf".source = kdcConfFile;
|
||||||
|
|
||||||
|
systemd.tmpfiles.settings."10-heimdal" =
|
||||||
|
let
|
||||||
|
databases = lib.pipe finalConfig.kdc.database [
|
||||||
|
(map (dbAttrs: dbAttrs.dbname or null))
|
||||||
|
(lib.filter (x: x != null))
|
||||||
|
lib.unique
|
||||||
|
];
|
||||||
|
in
|
||||||
|
lib.genAttrs databases (_: {
|
||||||
|
d = {
|
||||||
|
user = "root";
|
||||||
|
group = "root";
|
||||||
|
mode = "0700";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
systemd.services.kadmind = {
|
||||||
|
description = "Kerberos Administration Daemon";
|
||||||
|
partOf = [ "kerberos-server.target" ];
|
||||||
|
wantedBy = [ "kerberos-server.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "${package}/libexec/kadmind --config-file=/etc/heimdal-kdc/kdc.conf";
|
||||||
|
Slice = "system-kerberos-server.slice";
|
||||||
|
StateDirectory = "heimdal";
|
||||||
|
};
|
||||||
|
restartTriggers = [ kdcConfFile ];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.kdc = {
|
||||||
|
description = "Key Distribution Center daemon";
|
||||||
|
partOf = [ "kerberos-server.target" ];
|
||||||
|
wantedBy = [ "kerberos-server.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "${package}/libexec/kdc --config-file=/etc/heimdal-kdc/kdc.conf";
|
||||||
|
Slice = "system-kerberos-server.slice";
|
||||||
|
StateDirectory = "heimdal";
|
||||||
|
};
|
||||||
|
restartTriggers = [ kdcConfFile ];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.kpasswdd = {
|
||||||
|
description = "Kerberos Password Changing daemon";
|
||||||
|
partOf = [ "kerberos-server.target" ];
|
||||||
|
wantedBy = [ "kerberos-server.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "${package}/libexec/kpasswdd";
|
||||||
|
Slice = "system-kerberos-server.slice";
|
||||||
|
StateDirectory = "heimdal";
|
||||||
|
};
|
||||||
|
restartTriggers = [ kdcConfFile ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
204
nix/module/krb5-conf-format.nix
Normal file
204
nix/module/krb5-conf-format.nix
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
{ pkgs, lib, ... }:
|
||||||
|
|
||||||
|
# Based on
|
||||||
|
# - https://web.mit.edu/kerberos/krb5-1.12/doc/admin/conf_files/krb5_conf.html
|
||||||
|
# - https://manpages.debian.org/unstable/heimdal-docs/krb5.conf.5heimdal.en.html
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (lib)
|
||||||
|
boolToString
|
||||||
|
concatMapStringsSep
|
||||||
|
concatStringsSep
|
||||||
|
filter
|
||||||
|
isAttrs
|
||||||
|
isBool
|
||||||
|
isList
|
||||||
|
mapAttrsToList
|
||||||
|
mkOption
|
||||||
|
singleton
|
||||||
|
splitString
|
||||||
|
;
|
||||||
|
inherit (lib.types)
|
||||||
|
attrsOf
|
||||||
|
bool
|
||||||
|
coercedTo
|
||||||
|
either
|
||||||
|
enum
|
||||||
|
int
|
||||||
|
listOf
|
||||||
|
oneOf
|
||||||
|
path
|
||||||
|
str
|
||||||
|
submodule
|
||||||
|
;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
enableKdcACLEntries ? false,
|
||||||
|
}:
|
||||||
|
rec {
|
||||||
|
sectionType =
|
||||||
|
let
|
||||||
|
relation = oneOf [
|
||||||
|
(listOf (attrsOf value))
|
||||||
|
(attrsOf value)
|
||||||
|
value
|
||||||
|
];
|
||||||
|
value = either (listOf atom) atom;
|
||||||
|
atom = oneOf [
|
||||||
|
int
|
||||||
|
str
|
||||||
|
bool
|
||||||
|
];
|
||||||
|
in
|
||||||
|
attrsOf relation;
|
||||||
|
|
||||||
|
type =
|
||||||
|
let
|
||||||
|
aclEntry = submodule {
|
||||||
|
options = {
|
||||||
|
principal = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Which principal the rule applies to";
|
||||||
|
};
|
||||||
|
access = mkOption {
|
||||||
|
type = either (listOf (enum [
|
||||||
|
"add"
|
||||||
|
"cpw"
|
||||||
|
"delete"
|
||||||
|
"get"
|
||||||
|
"list"
|
||||||
|
"modify"
|
||||||
|
])) (enum [ "all" ]);
|
||||||
|
default = "all";
|
||||||
|
description = "The changes the principal is allowed to make.";
|
||||||
|
};
|
||||||
|
target = mkOption {
|
||||||
|
type = str;
|
||||||
|
default = "*";
|
||||||
|
description = "The principals that 'access' applies to.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
realm = submodule (
|
||||||
|
{ name, ... }:
|
||||||
|
{
|
||||||
|
freeformType = sectionType;
|
||||||
|
options = {
|
||||||
|
acl = mkOption {
|
||||||
|
type = listOf aclEntry;
|
||||||
|
default = [
|
||||||
|
{
|
||||||
|
principal = "*/admin";
|
||||||
|
access = "all";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
principal = "admin";
|
||||||
|
access = "all";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
description = ''
|
||||||
|
The privileges granted to a user.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
in
|
||||||
|
submodule {
|
||||||
|
freeformType = attrsOf sectionType;
|
||||||
|
options =
|
||||||
|
{
|
||||||
|
include = mkOption {
|
||||||
|
default = [ ];
|
||||||
|
description = ''
|
||||||
|
Files to include in the Kerberos configuration.
|
||||||
|
'';
|
||||||
|
type = coercedTo path singleton (listOf path);
|
||||||
|
};
|
||||||
|
includedir = mkOption {
|
||||||
|
default = [ ];
|
||||||
|
description = ''
|
||||||
|
Directories containing files to include in the Kerberos configuration.
|
||||||
|
'';
|
||||||
|
type = coercedTo path singleton (listOf path);
|
||||||
|
};
|
||||||
|
module = mkOption {
|
||||||
|
default = [ ];
|
||||||
|
description = ''
|
||||||
|
Modules to obtain Kerberos configuration from.
|
||||||
|
'';
|
||||||
|
type = coercedTo path singleton (listOf path);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
// (lib.optionalAttrs enableKdcACLEntries {
|
||||||
|
realms = mkOption {
|
||||||
|
type = attrsOf realm;
|
||||||
|
description = ''
|
||||||
|
The realm(s) to serve keys for.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
generate =
|
||||||
|
let
|
||||||
|
indent = str: concatMapStringsSep "\n" (line: " " + line) (splitString "\n" str);
|
||||||
|
|
||||||
|
formatToplevel =
|
||||||
|
args@{
|
||||||
|
include ? [ ],
|
||||||
|
includedir ? [ ],
|
||||||
|
module ? [ ],
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
sections = removeAttrs args [
|
||||||
|
"include"
|
||||||
|
"includedir"
|
||||||
|
"module"
|
||||||
|
];
|
||||||
|
in
|
||||||
|
concatStringsSep "\n" (
|
||||||
|
filter (x: x != "") [
|
||||||
|
(concatStringsSep "\n" (mapAttrsToList formatSection sections))
|
||||||
|
(concatMapStringsSep "\n" (m: "module ${m}") module)
|
||||||
|
(concatMapStringsSep "\n" (i: "include ${i}") include)
|
||||||
|
(concatMapStringsSep "\n" (i: "includedir ${i}") includedir)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
formatSection = name: section: ''
|
||||||
|
[${name}]
|
||||||
|
${indent (concatStringsSep "\n" (mapAttrsToList formatRelation section))}
|
||||||
|
'';
|
||||||
|
|
||||||
|
formatRelation =
|
||||||
|
name: relation:
|
||||||
|
if isAttrs relation then
|
||||||
|
''
|
||||||
|
${name} = {
|
||||||
|
${indent (concatStringsSep "\n" (mapAttrsToList formatValue relation))}
|
||||||
|
}''
|
||||||
|
else if isList relation then
|
||||||
|
concatMapStringsSep "\n" (formatRelation name) relation
|
||||||
|
else
|
||||||
|
formatValue name relation;
|
||||||
|
|
||||||
|
formatValue =
|
||||||
|
name: value:
|
||||||
|
if isList value then concatMapStringsSep "\n" (formatAtom name) value else formatAtom name value;
|
||||||
|
|
||||||
|
formatAtom =
|
||||||
|
name: atom:
|
||||||
|
let
|
||||||
|
v = if isBool atom then boolToString atom else toString atom;
|
||||||
|
in
|
||||||
|
"${name} = ${v}";
|
||||||
|
in
|
||||||
|
name: value:
|
||||||
|
pkgs.writeText name ''
|
||||||
|
${formatToplevel value}
|
||||||
|
'';
|
||||||
|
}
|
||||||
268
nix/nixosTest.nix
Normal file
268
nix/nixosTest.nix
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
{ nixpkgs }:
|
||||||
|
(
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
name = "kerberos_server-heimdal";
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
server =
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
{
|
||||||
|
disabledModules = [ "services/system/kerberos/default.nix" ];
|
||||||
|
imports = [
|
||||||
|
"${nixpkgs}/nixos/tests/common/user-account.nix"
|
||||||
|
./module
|
||||||
|
];
|
||||||
|
|
||||||
|
users.users.alice.extraGroups = [ "wheel" ];
|
||||||
|
|
||||||
|
services.getty.autologinUser = "alice";
|
||||||
|
|
||||||
|
virtualisation.vlans = [ 1 ];
|
||||||
|
|
||||||
|
time.timeZone = "Etc/UTC";
|
||||||
|
|
||||||
|
networking = {
|
||||||
|
domain = "foo.bar";
|
||||||
|
useDHCP = false;
|
||||||
|
firewall.enable = false;
|
||||||
|
hosts."10.0.0.1" = [ "server.foo.bar" ];
|
||||||
|
hosts."10.0.0.2" = [ "client.foo.bar" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.network.networks."01-eth1" = {
|
||||||
|
name = "eth1";
|
||||||
|
networkConfig.Address = "10.0.0.1/24";
|
||||||
|
};
|
||||||
|
|
||||||
|
security.krb5 = {
|
||||||
|
enable = true;
|
||||||
|
package = pkgs.heimdal;
|
||||||
|
settings = {
|
||||||
|
libdefaults.default_realm = "FOO.BAR";
|
||||||
|
|
||||||
|
# Enable extra debug output
|
||||||
|
logging = {
|
||||||
|
admin_server = "SYSLOG:DEBUG:AUTH";
|
||||||
|
default = "SYSLOG:DEBUG:AUTH";
|
||||||
|
kdc = "SYSLOG:DEBUG:AUTH";
|
||||||
|
};
|
||||||
|
|
||||||
|
realms = {
|
||||||
|
"FOO.BAR" = {
|
||||||
|
admin_server = "server.foo.bar";
|
||||||
|
kpasswd_server = "server.foo.bar";
|
||||||
|
kdc = [ "server.foo.bar" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.kerberos_server = {
|
||||||
|
enable = true;
|
||||||
|
settings.realms = {
|
||||||
|
"FOO.BAR" = {
|
||||||
|
acl = [
|
||||||
|
{
|
||||||
|
principal = "kadmin/admin@FOO.BAR";
|
||||||
|
access = "all";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
principal = "alice/admin@FOO.BAR";
|
||||||
|
access = [
|
||||||
|
"add"
|
||||||
|
"cpw"
|
||||||
|
"delete"
|
||||||
|
"get"
|
||||||
|
"list"
|
||||||
|
"modify"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
client =
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
{
|
||||||
|
disabledModules = [ "services/system/kerberos/default.nix" ];
|
||||||
|
imports = [
|
||||||
|
"${nixpkgs}/nixos/tests/common/user-account.nix"
|
||||||
|
./module
|
||||||
|
];
|
||||||
|
|
||||||
|
users.users.alice.extraGroups = [ "wheel" ];
|
||||||
|
|
||||||
|
services.getty.autologinUser = "alice";
|
||||||
|
|
||||||
|
virtualisation.vlans = [ 1 ];
|
||||||
|
|
||||||
|
time.timeZone = "Etc/UTC";
|
||||||
|
|
||||||
|
networking = {
|
||||||
|
domain = "foo.bar";
|
||||||
|
useDHCP = false;
|
||||||
|
hosts."10.0.0.1" = [ "server.foo.bar" ];
|
||||||
|
hosts."10.0.0.2" = [ "client.foo.bar" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.network.networks."01-eth1" = {
|
||||||
|
name = "eth1";
|
||||||
|
networkConfig.Address = "10.0.0.2/24";
|
||||||
|
};
|
||||||
|
|
||||||
|
security.krb5 = {
|
||||||
|
enable = true;
|
||||||
|
package = pkgs.heimdal;
|
||||||
|
settings = {
|
||||||
|
libdefaults.default_realm = "FOO.BAR";
|
||||||
|
|
||||||
|
logging = {
|
||||||
|
admin_server = "SYSLOG:DEBUG:AUTH";
|
||||||
|
default = "SYSLOG:DEBUG:AUTH";
|
||||||
|
kdc = "SYSLOG:DEBUG:AUTH";
|
||||||
|
};
|
||||||
|
|
||||||
|
realms = {
|
||||||
|
"FOO.BAR" = {
|
||||||
|
admin_server = "server.foo.bar";
|
||||||
|
kpasswd_server = "server.foo.bar";
|
||||||
|
kdc = [ "server.foo.bar" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript =
|
||||||
|
{ nodes, ... }:
|
||||||
|
''
|
||||||
|
import string
|
||||||
|
import random
|
||||||
|
random.seed(0)
|
||||||
|
|
||||||
|
start_all()
|
||||||
|
|
||||||
|
with subtest("Server: initialize realm"):
|
||||||
|
# for unit in ["kadmind.service", "kdc.socket", "kpasswdd.socket"]:
|
||||||
|
for unit in ["kadmind.service", "kdc.service", "kpasswdd.service"]:
|
||||||
|
server.wait_for_unit(unit)
|
||||||
|
|
||||||
|
server.succeed("kadmin -l init --realm-max-ticket-life='8 day' --realm-max-renewable-life='10 day' FOO.BAR")
|
||||||
|
|
||||||
|
for unit in ["kadmind.service", "kdc.service", "kpasswdd.service"]:
|
||||||
|
server.systemctl(f"restart {unit}")
|
||||||
|
|
||||||
|
alice_krb_pw = "alice_hunter2"
|
||||||
|
alice_old_krb_pw = ""
|
||||||
|
alice_krb_admin_pw = "alice_admin_hunter2"
|
||||||
|
|
||||||
|
def random_password():
|
||||||
|
password_chars = string.ascii_letters + string.digits + string.punctuation.replace('"', "")
|
||||||
|
return "".join(random.choice(password_chars) for _ in range(16))
|
||||||
|
|
||||||
|
with subtest("Server: initialize user principals and keytabs"):
|
||||||
|
server.succeed(f'kadmin -l add --password="{alice_krb_admin_pw}" --use-defaults alice/admin')
|
||||||
|
server.succeed("kadmin -l ext_keytab --keytab=admin.keytab alice/admin")
|
||||||
|
|
||||||
|
server.succeed(f'kadmin -p alice/admin -K admin.keytab add --password="{alice_krb_pw}" --use-defaults alice')
|
||||||
|
server.succeed("kadmin -l ext_keytab --keytab=alice.keytab alice")
|
||||||
|
|
||||||
|
server.wait_for_unit("getty@tty1.service")
|
||||||
|
server.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
|
||||||
|
server.wait_for_unit("default.target")
|
||||||
|
|
||||||
|
with subtest("Server: initialize host principal with keytab"):
|
||||||
|
server.send_chars("sudo ktutil get -p alice/admin host/server.foo.bar\n")
|
||||||
|
server.wait_until_tty_matches("1", "password for alice:")
|
||||||
|
server.send_chars("${nodes.server.config.users.users.alice.password}\n")
|
||||||
|
server.wait_until_tty_matches("1", "alice/admin@FOO.BAR's Password:")
|
||||||
|
server.send_chars(f'{alice_krb_admin_pw}\n')
|
||||||
|
server.wait_for_file("/etc/krb5.keytab")
|
||||||
|
|
||||||
|
ktutil_list = server.succeed("sudo ktutil list")
|
||||||
|
if not "host/server.foo.bar" in ktutil_list:
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
server.send_chars("clear\n")
|
||||||
|
|
||||||
|
client.systemctl("start network-online.target")
|
||||||
|
client.wait_for_unit("network-online.target")
|
||||||
|
client.wait_for_unit("getty@tty1.service")
|
||||||
|
client.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
|
||||||
|
client.wait_for_unit("default.target")
|
||||||
|
|
||||||
|
with subtest("Client: initialize host principal with keytab"):
|
||||||
|
client.succeed(
|
||||||
|
f'echo "{alice_krb_admin_pw}" > pw.txt',
|
||||||
|
"kinit -p --password-file=pw.txt alice/admin",
|
||||||
|
)
|
||||||
|
|
||||||
|
client.send_chars("sudo ktutil get -p alice/admin host/client.foo.bar\n")
|
||||||
|
client.wait_until_tty_matches("1", "password for alice:")
|
||||||
|
client.send_chars("${nodes.client.config.users.users.alice.password}\n")
|
||||||
|
client.wait_until_tty_matches("1", "alice/admin@FOO.BAR's Password:")
|
||||||
|
client.send_chars(f"{alice_krb_admin_pw}\n")
|
||||||
|
client.wait_for_file("/etc/krb5.keytab")
|
||||||
|
|
||||||
|
ktutil_list = client.succeed("sudo ktutil list")
|
||||||
|
if not "host/client.foo.bar" in ktutil_list:
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
client.send_chars("clear\n")
|
||||||
|
|
||||||
|
with subtest("Client: kinit alice"):
|
||||||
|
client.succeed(
|
||||||
|
f"echo '{alice_krb_pw}' > pw.txt",
|
||||||
|
"kinit -p --password-file=pw.txt alice",
|
||||||
|
)
|
||||||
|
tickets = client.succeed("klist")
|
||||||
|
assert "Principal: alice@FOO.BAR" in tickets
|
||||||
|
client.send_chars("clear\n")
|
||||||
|
|
||||||
|
with subtest("Client: kpasswd alice"):
|
||||||
|
alice_old_krb_pw = alice_krb_pw
|
||||||
|
alice_krb_pw = random_password()
|
||||||
|
client.send_chars("kpasswd\n")
|
||||||
|
client.wait_until_tty_matches("1", "alice@FOO.BAR's Password:")
|
||||||
|
client.send_chars(f"{alice_old_krb_pw}\n", 0.1)
|
||||||
|
client.wait_until_tty_matches("1", "New password:")
|
||||||
|
client.send_chars(f"{alice_krb_pw}\n", 0.1)
|
||||||
|
client.wait_until_tty_matches("1", "Verify password - New password:")
|
||||||
|
client.send_chars(f"{alice_krb_pw}\n", 0.1)
|
||||||
|
|
||||||
|
client.wait_until_tty_matches("1", "Success : Password changed")
|
||||||
|
|
||||||
|
client.send_chars("clear\n")
|
||||||
|
|
||||||
|
with subtest("Server: kinit alice"):
|
||||||
|
server.succeed(
|
||||||
|
"echo 'alice_pw_2' > pw.txt"
|
||||||
|
"kinit -p --password-file=pw.txt alice",
|
||||||
|
)
|
||||||
|
tickets = client.succeed("klist")
|
||||||
|
assert "Principal: alice@FOO.BAR" in tickets
|
||||||
|
server.send_chars("clear\n")
|
||||||
|
|
||||||
|
with subtest("Server: kpasswd alice"):
|
||||||
|
alice_old_krb_pw = alice_krb_pw
|
||||||
|
alice_krb_pw = random_password()
|
||||||
|
server.send_chars("kpasswd\n")
|
||||||
|
server.wait_until_tty_matches("1", "alice@FOO.BAR's Password:")
|
||||||
|
server.send_chars(f"{alice_old_krb_pw}\n", 0.1)
|
||||||
|
server.wait_until_tty_matches("1", "New password:")
|
||||||
|
server.send_chars(f"{alice_krb_pw}\n", 0.1)
|
||||||
|
server.wait_until_tty_matches("1", "Verify password - New password:")
|
||||||
|
server.send_chars(f"{alice_krb_pw}\n", 0.1)
|
||||||
|
|
||||||
|
server.wait_until_tty_matches("1", "Success : Password changed")
|
||||||
|
|
||||||
|
server.send_chars("clear\n")
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta.maintainers = pkgs.heimdal.meta.maintainers;
|
||||||
|
}
|
||||||
|
)
|
||||||
51
nix/shell.nix
Normal file
51
nix/shell.nix
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
{ pkgs, lib }:
|
||||||
|
pkgs.mkShell {
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
autoconf
|
||||||
|
automake
|
||||||
|
autogen
|
||||||
|
autoreconfHook
|
||||||
|
|
||||||
|
pkg-config
|
||||||
|
python3
|
||||||
|
perl
|
||||||
|
bison
|
||||||
|
flex
|
||||||
|
perlPackages.JSON
|
||||||
|
texinfo
|
||||||
|
|
||||||
|
# check inputs
|
||||||
|
curl
|
||||||
|
jdk_headless
|
||||||
|
unzip
|
||||||
|
which
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
db
|
||||||
|
libedit
|
||||||
|
pam
|
||||||
|
cjson
|
||||||
|
libcap_ng
|
||||||
|
libmicrohttpd
|
||||||
|
openldap
|
||||||
|
openssl
|
||||||
|
sqlite
|
||||||
|
];
|
||||||
|
|
||||||
|
env = {
|
||||||
|
CFLAGS = lib.concatStringsSep " " [
|
||||||
|
# From github workflows
|
||||||
|
"-Wno-error=shadow"
|
||||||
|
"-Wno-error=bad-function-cast"
|
||||||
|
"-Wno-error=unused-function"
|
||||||
|
"-Wno-error=unused-result"
|
||||||
|
"-Wno-error=deprecated-declarations"
|
||||||
|
|
||||||
|
# idk, but it complained about these during compilation.
|
||||||
|
# maybe they come from the nix environment, or too new compiler?
|
||||||
|
"-Wno-error=maybe-uninitialized"
|
||||||
|
"-Wno-error=format-overflow"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user