Viewing: l_gssiam_upcall.c

// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2026, Google
 * Userspace upcall for GSSIAM token retrieval.
 *
 * This program is called by the kernel (upcall_cache) when a GSSIAM token
 * is needed for a user.
 *
 * Arguments: [-d] [-p <principal>] [-l <loginuid>] <key>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <getopt.h>
#include <linux/types.h>
#include <linux/lustre/lustre_user.h>
#include <libcfs/util/param.h>
#include "lstddef.h"
#include "err_util.h"

static int send_downcall(const struct gssiam_downcall_data *data,
			 size_t data_size)
{
	glob_t path;
	ssize_t ret;
	int fd;
	int rc;

	rc = cfs_get_param_paths(&path, "sptlrpc/gssiam/gssiam_downcall");
	if (rc) {
		logmsg(LL_ERR, "Failed to get param path for gssiam: %s\n",
		       strerror(errno));
		return EXIT_FAILURE;
	}

	fd = open(path.gl_pathv[0], O_WRONLY);
	if (fd < 0) {
		logmsg(LL_ERR, "Failed to open %s: %s\n", path.gl_pathv[0],
		       strerror(errno));
		cfs_free_param_data(&path);
		return EXIT_FAILURE;
	}

	ret = write(fd, data, data_size);
	if (ret < 0 || (size_t)ret != data_size) {
		logmsg(LL_ERR, "Failed to write downcall data: %s\n",
		       strerror(errno));
		rc = EXIT_FAILURE;
	} else {
		rc = (data->idd_err != 0) ? EXIT_FAILURE : 0;
	}

	close(fd);
	cfs_free_param_data(&path);
	return rc;
}

static int send_error_downcall(__u64 key, int err, __u32 loginuid,
			       const char *principal)
{
	struct gssiam_downcall_data *err_data;
	size_t principal_len = principal ? strlen(principal) + 1 : 0;
	size_t data_size = sizeof(*err_data) + principal_len;
	int rc;

	err_data = calloc(1, data_size);
	if (!err_data)
		return EXIT_FAILURE;

	err_data->idd_magic = GSSIAM_DOWNCALL_MAGIC;
	err_data->idd_err = err;
	err_data->idd_key = key;
	err_data->idd_loginuid = loginuid;
	err_data->idd_token_len = 0;
	err_data->idd_principal_len = principal_len;
	if (principal)
		memcpy(idd_principal(err_data), principal, principal_len);

	rc = send_downcall(err_data, data_size);
	free(err_data);
	return rc;
}

int main(int argc, char **argv)
{
	char *key_str = NULL;
	char *principal = NULL;
	char *loginuid_str = NULL;
	char *endptr = NULL;
	int opt;
	int debug = 0;
	__u64 key = 0;
	__u32 loginuid = (__u32)-1;
	struct gssiam_downcall_data *data;
	int data_size;
	int rc = 0;
	/* Reference implementation: return mock token for testing.
	 * Production deployments should replace this with a lookup in
	 * the local enterprise IAM credential cache.
	 */
	const char *token = "mock_gssiam_token";

	static struct option long_opts[] = {
		{ .val = 'd', .name = "debug",	.has_arg = no_argument },
		{ .val = 'l', .name = "loginuid",
		  .has_arg = required_argument },
		{ .val = 'p', .name = "principal",
		  .has_arg = required_argument },
		{ .name = NULL }
	};

	while ((opt = getopt_long(argc, argv, "dl:p:", long_opts,
				  NULL)) != -1) {
		switch (opt) {
		case 'd':
			debug = 1;
			lgss_set_loglevel(LL_TRACE);
			break;
		case 'l':
			loginuid_str = optarg;
			break;
		case 'p':
			principal = optarg;
			break;
		default:
			logmsg(LL_ERR,
			       "Usage: %s [-d] [-p <principal>] [-l <loginuid>] <key>\n",
			       argv[0]);
			return EXIT_FAILURE;
		}
	}

	if (optind >= argc) {
		logmsg(LL_ERR, "Missing key\n");
		return EXIT_FAILURE;
	}

	key_str = argv[optind];
	errno = 0;
	key = strtoull(key_str, &endptr, 10);
	if (errno != 0 || *endptr != '\0') {
		logmsg(LL_ERR, "Invalid key '%s'\n", key_str);
		return EXIT_FAILURE;
	}

	if (loginuid_str) {
		errno = 0;
		loginuid = strtoul(loginuid_str, &endptr, 10);
		if (errno != 0 || *endptr != '\0') {
			logmsg(LL_ERR, "Invalid loginuid '%s'\n", loginuid_str);
			send_error_downcall(key, -EINVAL, (__u32)-1, principal);
			return EXIT_FAILURE;
		}
	}

	/* Get token from remote GSSIAM server by principal */

	/* Generate mock data */
	data_size = sizeof(*data) + round_up(strlen(token), 8) +
		    (principal ? strlen(principal) + 1 : 0);
	data = malloc(data_size);
	if (!data) {
		logmsg(LL_ERR, "Failed to allocate memory\n");
		send_error_downcall(key, -ENOMEM, loginuid, principal);
		return EXIT_FAILURE;
	}

	memset(data, 0, data_size);
	data->idd_magic = GSSIAM_DOWNCALL_MAGIC;
	data->idd_err = 0;
	data->idd_key = key;
	data->idd_loginuid = loginuid;
	data->idd_token_len = strlen(token);
	data->idd_principal_len = principal ? strlen(principal) + 1 : 0;

	memcpy(idd_token(data), token, data->idd_token_len);
	if (principal)
		memcpy(idd_principal(data), principal, data->idd_principal_len);

	if (debug) {
		fprintf(stdout, "key: %llu\ntoken: %s\n", key, token);
		if (principal)
			fprintf(stdout, "principal: %s\n", principal);
		if (loginuid_str)
			fprintf(stdout, "loginuid: %s\n", loginuid_str);
		goto out_free;
	}

	rc = send_downcall(data, data_size);

out_free:
	free(data);
	return rc;
}