3 Commits

Author SHA1 Message Date
845fb73ad1 WIP 2026-02-02 08:53:06 +09:00
d0b5821e9a Set up nix tooling 2026-02-01 03:45:20 +09:00
072c1f062f zed: set tab width 2026-02-01 03:45:18 +09:00
22 changed files with 1635 additions and 53 deletions

8
.zed/settings.json Normal file
View 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
View 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
View 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;
};
};
}

View File

@@ -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)

View File

@@ -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);

View File

@@ -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");

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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

View File

@@ -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);
} }

View File

@@ -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,

View File

@@ -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;
} }

View File

@@ -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:

View 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

View 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

View 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
View 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
View 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
View 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 ];
};
};
}

View 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
View 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
View 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"
];
};
}