Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,6 @@ tests/t_acquire
tests/t_cred_store
tests/t_impersonate
tests/t_init
tests/t_kt_loader
tests/t_names
tests/t_setcredopt
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ if BUILD_PROXY
gssproxy_SOURCES = \
src/gp_config.c \
src/gp_init.c \
src/gp_keytab.c \
src/gp_mgmt.c \
src/gp_socket.c \
src/gp_workers.c \
Expand Down
4 changes: 4 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ AC_SUBST([GSSRPC_LIBS])

AC_CHECK_FUNCS([__secure_getenv secure_getenv])

# keytab_loader: memfd output, pidfd exit notification
AC_CHECK_HEADERS([sys/pidfd.h])
AC_CHECK_FUNCS([memfd_create pidfd_open close_range])

AC_ARG_ENABLE([only-gss-module],
[AC_HELP_STRING([--enable-only-gss-module],
[Whether to build only the gss module code [no]]
Expand Down
37 changes: 37 additions & 0 deletions man/gssproxy.conf.5.xml
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,43 @@
</listitem>
</varlistentry>

<varlistentry>
<term>keytab_loader (string)</term>
<listitem>
<para>A program that produces the service's keytab,
so that the keys never touch a filesystem.
The service must use an in-memory keytab:
<quote>cred_store = keytab:MEMORY:name</quote>.
</para>
<para>The value is an absolute path followed by
arguments, separated by blanks; no shell is
involved. GSS-Proxy runs the program with an
anonymous memory file (memfd) as file
descriptor 3, and the program writes a keytab
in the MIT file format to it. When the program
exits with status 0, the keys replace the
contents of the MEMORY keytab and the memory
file is closed.
</para>
<para>A program that fails (or runs longer than five
minutes) is retried with exponential backoff,
from 5 seconds up to 10 minutes; the keys
loaded before stay in use meanwhile. SIGHUP
runs every loader again. When a keytab is no
longer used by any service its keys are
dropped.
</para>
<para>Services that share a MEMORY keytab must name
the same keytab_loader. While any loader is
configured, GSS-Proxy marks itself as not
dumpable, keeping the keys out of core dumps.
</para>
<para>Example: keytab_loader =
/usr/libexec/kuipa-unseal --memfd
</para>
</listitem>
</varlistentry>

<varlistentry>
<term>krb5_principal (string)</term>
<listitem>
Expand Down
124 changes: 118 additions & 6 deletions src/gp_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ static void free_str_array(const char ***a, int *count)
safefree(*a);
}

static void free_argv(char ***argv)
{
if (!*argv) {
return;
}
for (int i = 0; (*argv)[i]; i++) {
safefree((*argv)[i]);
}
safefree(*argv);
}

void free_cred_store_elements(gss_key_value_set_desc *cs)
{
if (!cs->elements) return;
Expand All @@ -78,21 +89,25 @@ static void gp_service_free(struct gp_service *svc)
}
free(svc->socket);
free(svc->program);
free_argv(&svc->keytab_loader);
SELINUX_context_free(svc->selinux_ctx);
memset(svc, 0, sizeof(struct gp_service));
}

static int setup_krb5_creds_handle(struct gp_service *svc)
const char *gp_service_get_keytab(struct gp_service *svc)
{
uint32_t ret_maj, ret_min;
const char *keytab = NULL;

for (unsigned i = 0; i < svc->krb5.store.count; i++) {
if (strcmp(svc->krb5.store.elements[i].key, "keytab") == 0) {
keytab = svc->krb5.store.elements[i].value;
break;
return svc->krb5.store.elements[i].value;
}
}
return NULL;
}

static int setup_krb5_creds_handle(struct gp_service *svc)
{
uint32_t ret_maj, ret_min;
const char *keytab = gp_service_get_keytab(svc);

ret_maj = gp_init_creds_handle(&ret_min, svc->name, keytab,
&svc->krb5.creds_handle);
Expand All @@ -103,6 +118,70 @@ static int setup_krb5_creds_handle(struct gp_service *svc)
return 0;
}

/* keytab_loader is a command line, split on blanks; no shell is involved */
static int parse_keytab_loader(struct gp_service *svc, const char *value)
{
#if defined(HAVE_MEMFD_CREATE) && defined(HAVE_PIDFD_OPEN)
const char *keytab;
char **argv = NULL;
char **tmp;
char *vcopy;
char *token;
char *handle;
int argc = 0;
int ret = 0;

keytab = gp_service_get_keytab(svc);
if (!keytab || strncmp(keytab, "MEMORY:", 7) != 0) {
GPERROR("keytab_loader in [service/%s] requires "
"\"cred_store = keytab:MEMORY:<name>\".\n", svc->name);
return EINVAL;
}

vcopy = strdup(value);
if (!vcopy) {
return ENOMEM;
}

for (token = strtok_r(vcopy, " \t", &handle);
token != NULL;
token = strtok_r(NULL, " \t", &handle)) {
tmp = realloc(argv, (argc + 2) * sizeof(char *));
if (!tmp) {
ret = ENOMEM;
goto done;
}
argv = tmp;
argv[argc] = strdup(token);
if (!argv[argc]) {
ret = ENOMEM;
goto done;
}
argc++;
argv[argc] = NULL;
}

if (argc == 0 || argv[0][0] != '/') {
GPERROR("keytab_loader in [service/%s] must start with an "
"absolute path.\n", svc->name);
ret = EINVAL;
goto done;
}

svc->keytab_loader = argv;
argv = NULL;

done:
free_argv(&argv);
safefree(vcopy);
return ret;
#else
GPERROR("keytab_loader in [service/%s] is not supported on this "
"platform.\n", svc->name);
return ENOTSUP;
#endif
}

static int get_krb5_mech_cfg(struct gp_service *svc,
struct gp_ini_context *ctx,
const char *secname)
Expand Down Expand Up @@ -184,6 +263,15 @@ static int get_krb5_mech_cfg(struct gp_service *svc,
ret = 0;
}

if (ret == 0) {
ret = gp_config_get_string(ctx, secname, "keytab_loader", &value);
if (ret == 0) {
ret = parse_keytab_loader(svc, value);
} else if (ret == ENOENT) {
ret = 0;
}
}

if (ret == 0) {
ret = setup_krb5_creds_handle(svc);
}
Expand Down Expand Up @@ -246,6 +334,18 @@ static int parse_flags(const char *value, uint32_t *storage)
return 0;
}

static bool same_argv(char **a, char **b)
{
int i;

for (i = 0; a[i] && b[i]; i++) {
if (strcmp(a[i], b[i]) != 0) {
return false;
}
}
return a[i] == b[i];
}

static int check_services(const struct gp_config *cfg)
{
int i, j;
Expand Down Expand Up @@ -273,6 +373,18 @@ static int check_services(const struct gp_config *cfg)

for (j = 0; j < i; j++) {
jsvc = cfg->svcs[j];

/* services sharing a loaded keytab must share its loader */
if (isvc->keytab_loader && jsvc->keytab_loader &&
strcmp(gp_service_get_keytab(isvc),
gp_service_get_keytab(jsvc)) == 0 &&
!same_argv(isvc->keytab_loader, jsvc->keytab_loader)) {
ret = 1;
GPERROR("%s and %s load keytab %s with different "
"keytab_loader commands!\n",
isvc->name, jsvc->name, gp_service_get_keytab(isvc));
}

jsock = jsvc->socket;
if (!jsock) {
jsock = GP_SOCKET_NAME;
Expand Down
3 changes: 3 additions & 0 deletions src/gp_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ static void hup_handler(verto_ctx *vctx UNUSED, verto_ev *ev)
/* conditionally reload kernel interface */
init_proc_nfsd(gpctx);

/* re-run keytab loaders, start new ones and drop unused keytabs */
gp_keytab_loaders_update(gpctx);

free_config(&old_config);

sd_notifyf(0, "READY=1\n"
Expand Down
Loading
Loading