ima: Add test suite for IMA namespace

userns_child_exec.c: a program that can create a new IMA namespace
by providing clone() syscall with CLONE_NEWIMA flag.

userns_child_exec_key.c: a program that can create a new IMA namespace
with a key placed on the ima keyring of the namespace.  Path of the key
is specified by -k argument.

loadkey.sh: a script that reads /proc/[pid]/ima_keyring to find out
the keyring name for the namespace and the load the key onto that
keyring.  The key is specified by the -k argument in
userns_child_exec_key program.

Changelog:
* Added imaevm.h
* Added check for missing command line parameters

Signed-off-by: Yuqiong Sun <suny@us.ibm.com>
Signed-off-by: Mehmet Kayaalp <mkayaalp@linux.vnet.ibm.com>
diff --git a/ima_tests/imaevm.h b/ima_tests/imaevm.h
new file mode 100644
index 0000000..486e123
--- /dev/null
+++ b/ima_tests/imaevm.h
@@ -0,0 +1,169 @@
+#ifndef _LIBIMAEVM_H
+#define _LIBIMAEVM_H
+
+#include <stdint.h>
+#include <syslog.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <openssl/rsa.h>
+
+#ifdef USE_FPRINTF
+#define do_log(level, fmt, args...)	\
+	({ if ((level) <= params.verbose) fprintf(stderr, fmt, ##args); })
+#define do_log_dump(level, p, len, cr)	\
+	({ if ((level) <= params.verbose) do_dump(stderr, p, len, cr); })
+#else
+#define do_log(level, fmt, args...)	\
+	syslog(level, fmt, ##args)
+#define do_log_dump(level, p, len, cr)
+#endif
+
+#ifdef DEBUG
+#define log_debug(fmt, args...)		\
+	do_log(LOG_DEBUG, "%s:%d " fmt, __func__, __LINE__, ##args)
+#define log_debug_dump(p, len)		\
+	do_log_dump(LOG_DEBUG, p, len, true)
+#define log_debug_dump_n(p, len)	\
+	do_log_dump(LOG_DEBUG, p, len, false)
+#else
+#define log_debug(fmt, args...)
+#define log_debug_dump(p, len)
+#endif
+
+#define log_dump(p, len)		\
+	do_log_dump(LOG_INFO, p, len, true)
+#define log_dump_n(p, len)		\
+	do_log_dump(LOG_INFO, p, len, false)
+#define log_info(fmt, args...)		\
+	do_log(LOG_INFO, fmt, ##args)
+#define log_err(fmt, args...)		\
+	do_log(LOG_ERR, fmt, ##args)
+#define log_errno(fmt, args...)		\
+	do_log(LOG_ERR, fmt ": errno: %s (%d)\n", \
+	       ##args, strerror(errno), errno)
+
+#define	DATA_SIZE	4096
+#define SHA1_HASH_LEN   20
+
+#define	EXT2_IOC_GETVERSION	_IOR('v', 1, long)
+#define	EXT34_IOC_GETVERSION	_IOR('f', 3, long)
+
+#define	FS_IOC_GETFLAGS		_IOR('f', 1, long)
+#define	FS_IOC_SETFLAGS		_IOW('f', 2, long)
+#define FS_IOC32_GETFLAGS	_IOR('f', 1, int)
+#define FS_IOC32_SETFLAGS	_IOW('f', 2, int)
+
+#define __packed __attribute__((packed))
+
+struct h_misc {
+	unsigned long ino;
+	uint32_t generation;
+	uid_t uid;
+	gid_t gid;
+	unsigned short mode;
+};
+
+struct h_misc_32 {
+	uint32_t ino;
+	uint32_t generation;
+	uid_t uid;
+	gid_t gid;
+	unsigned short mode;
+};
+
+struct h_misc_64 {
+	uint64_t ino;
+	uint32_t generation;
+	uid_t uid;
+	gid_t gid;
+	unsigned short mode;
+};
+
+enum pubkey_algo {
+	PUBKEY_ALGO_RSA,
+	PUBKEY_ALGO_MAX,
+};
+
+enum digest_algo {
+	DIGEST_ALGO_SHA1,
+	DIGEST_ALGO_SHA256,
+	DIGEST_ALGO_MAX
+};
+
+enum digsig_version {
+	DIGSIG_VERSION_1 = 1,
+	DIGSIG_VERSION_2
+};
+
+struct pubkey_hdr {
+	uint8_t version;	/* key format version */
+	uint32_t timestamp;	/* key made, always 0 for now */
+	uint8_t algo;
+	uint8_t nmpi;
+	char mpi[0];
+} __packed;
+
+struct signature_hdr {
+	uint8_t version;	/* signature format version */
+	uint32_t timestamp;	/* signature made */
+	uint8_t algo;
+	uint8_t hash;
+	uint8_t keyid[8];
+	uint8_t nmpi;
+	char mpi[0];
+} __packed;
+
+enum pkey_hash_algo {
+	PKEY_HASH_MD4,
+	PKEY_HASH_MD5,
+	PKEY_HASH_SHA1,
+	PKEY_HASH_RIPE_MD_160,
+	PKEY_HASH_SHA256,
+	PKEY_HASH_SHA384,
+	PKEY_HASH_SHA512,
+	PKEY_HASH_SHA224,
+	PKEY_HASH__LAST
+};
+
+/*
+ * signature format v2 - for using with asymmetric keys
+ */
+struct signature_v2_hdr {
+	uint8_t version;	/* signature format version */
+	uint8_t	hash_algo;	/* Digest algorithm [enum pkey_hash_algo] */
+	uint32_t keyid;		/* IMA key identifier - not X509/PGP specific*/
+	uint16_t sig_size;	/* signature size */
+	uint8_t sig[0];		/* signature payload */
+} __packed;
+
+typedef int (*verify_hash_fn_t)(const unsigned char *hash, int size,
+				unsigned char *sig, int siglen,
+				const char *keyfile);
+
+struct libevm_params {
+	int verbose;
+	const char *hash_algo;
+	char *keyfile;
+};
+
+struct RSA_ASN1_template {
+	const uint8_t *data;
+	size_t size;
+};
+
+extern const struct RSA_ASN1_template RSA_ASN1_templates[PKEY_HASH__LAST];
+extern struct libevm_params params;
+
+void do_dump(FILE *fp, const void *ptr, int len, bool cr);
+void dump(const void *ptr, int len);
+int get_filesize(const char *filename);
+int ima_calc_hash(const char *file, uint8_t *hash);
+int get_hash_algo(const char *algo);
+RSA *read_pub_key(const char *keyfile, int x509);
+
+int verify_hash(const unsigned char *hash, int size,
+		unsigned char *sig, int siglen);
+int ima_verify_signature(const char *file, unsigned char *sig, int siglen);
+
+#endif
diff --git a/ima_tests/loadkey.sh b/ima_tests/loadkey.sh
new file mode 100755
index 0000000..7328a7f
--- /dev/null
+++ b/ima_tests/loadkey.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+pid=$1
+key=$2
+
+ring_name=`cat /proc/$pid/ima_keyring | grep ima | mawk '{print $2}'`
+ring_id=`keyctl show | grep $ring_name | mawk '{print $1}'`
+#echo $ring_id
+
+key_id=`evmctl import $key $ring_id`
diff --git a/ima_tests/userns_child_exec.c b/ima_tests/userns_child_exec.c
new file mode 100644
index 0000000..2259d37
--- /dev/null
+++ b/ima_tests/userns_child_exec.c
@@ -0,0 +1,262 @@
+/* userns_child_exec.c
+ *
+ *  Copyright 2013, Michael Kerrisk
+ *  Licensed under GNU General Public License v2 or later
+ *
+ *  Create a child process that executes a shell command in new
+ *  namespace(s); allow UID and GID mappings to be specified when
+ *  creating a user namespace.
+ */
+
+#define _GNU_SOURCE
+#include <sched.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+/* A simple error-handling function: print an error message based
+ * on the value in 'errno' and terminate the calling process
+ */
+
+#define errExit(msg)				\
+	do { perror(msg); exit(EXIT_FAILURE);	\
+	} while (0)
+
+#define CLONE_NEWIMA 0x00001000
+
+struct child_args {
+	/* Command to be executed by child, with arguments */
+	char **argv;
+
+	/* Pipe used to synchronize parent and child */
+	int pipe_fd[2];
+};
+
+static int verbose;
+
+static void usage(char *pname)
+{
+	fprintf(stderr, "Usage: %s [options] cmd [arg...]\n\n", pname);
+	fprintf(stderr,
+		"Create a child process that executes a shell command\n"
+		"in a new user namespace,\n"
+		"and possibly also other new namespace(s).\n\n");
+	fprintf(stderr, "Options can be:\n\n");
+#define fpe(str) fprintf(stderr, "    %s", str)
+	fpe("-i          New IPC namespace\n");
+	fpe("-m          New mount namespace\n");
+	fpe("-n          New network namespace\n");
+	fpe("-p          New PID namespace\n");
+	fpe("-u          New UTS namespace\n");
+	fpe("-U          New user namespace\n");
+	fpe("-I	     New IMA namespace\n");
+	fpe("-M uid_map  Specify UID map for user namespace\n");
+	fpe("-G gid_map  Specify GID map for user namespace\n");
+	fpe("            If -M or -G is specified, -U is required\n");
+	fpe("-v          Display verbose messages\n");
+	fpe("\n");
+	fpe("Map strings for -M and -G consist of records of the form:\n");
+	fpe("\n");
+	fpe("    ID-inside-ns   ID-outside-ns   len\n");
+	fpe("\n");
+	fpe("A map string can contain multiple records,\n");
+	fpe("separated by commas; the commas are replaced\n");
+	fpe("by newlines before writing to map files.\n");
+
+	exit(EXIT_FAILURE);
+}
+
+/* Update the mapping file 'map_file', with the value provided in
+ * 'mapping', a string that defines a UID or GID mapping. A UID or
+ * GID mapping consists of one or more newline-delimited records
+ * of the form:
+ *
+ *     ID_inside-ns    ID-outside-ns   length
+ *
+ * Requiring the user to supply a string that contains newlines is
+ * of course inconvenient for command-line use. Thus, we permit the
+ * use of commas to delimit records in this string, and replace them
+ * with newlines before writing the string to the file.
+ */
+
+static void update_map(char *mapping, char *map_file)
+{
+	int fd, j;
+	size_t map_len;     /* Length of 'mapping' */
+
+	/* Replace commas in mapping string with newlines */
+
+	map_len = strlen(mapping);
+	for (j = 0; j < map_len; j++)
+		if (mapping[j] == ',')
+			mapping[j] = '\n';
+
+	fd = open(map_file, O_RDWR);
+	if (fd == -1) {
+		fprintf(stderr, "open %s: %s\n",
+			map_file, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	if (write(fd, mapping, map_len) != map_len) {
+		fprintf(stderr, "write %s: %s\n",
+			map_file, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	close(fd);
+}
+
+/* Start function for cloned child */
+static int childFunc(void *arg)
+{
+	struct child_args *args = (struct child_args *)arg;
+	char ch;
+
+	/* Wait until the parent has updated the UID and GID mappings. See
+	 * the comment in main(). We wait for end of file on a pipe that will
+	 * be closed by the parent process once it has updated the mappings.
+	 */
+
+	/* Close our descriptor for the write end
+	 * of the pipe so that we see EOF when
+	 * parent closes its descriptor
+	 */
+	close(args->pipe_fd[1]);
+	if (read(args->pipe_fd[0], &ch, 1) != 0) {
+		fprintf(stderr,
+			"Failure in child: read from pipe returned != 0\n");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Execute a shell command */
+	execvp(args->argv[0], args->argv);
+	errExit("execvp");
+}
+
+#define STACK_SIZE (1024 * 1024)
+
+static char child_stack[STACK_SIZE];    /* Space for child's stack */
+
+int main(int argc, char *argv[])
+{
+	int flags, opt;
+	pid_t child_pid;
+	struct child_args args;
+	char *uid_map, *gid_map;
+	char map_path[PATH_MAX];
+
+	/* Parse command-line options. The initial '+' character in
+	 * the final getopt() argument prevents GNU-style permutation
+	 * of command-line options. That's useful, since sometimes
+	 * the 'command' to be executed by this program itself
+	 * has command-line options. We don't want getopt() to treat
+	 * those as options to this program.
+	 */
+	flags = 0;
+	verbose = 0;
+	gid_map = NULL;
+	uid_map = NULL;
+
+	if (argc < 2)
+		usage(argv[0]);
+
+	while ((opt = getopt(argc, argv, "+imnpuUIM:G:v")) != -1) {
+		switch (opt) {
+		case 'i':
+			flags |= CLONE_NEWIPC;
+			break;
+		case 'm':
+			flags |= CLONE_NEWNS;
+			break;
+		case 'n':
+			flags |= CLONE_NEWNET;
+			break;
+		case 'p':
+			flags |= CLONE_NEWPID;
+			break;
+		case 'u':
+			flags |= CLONE_NEWUTS;
+			break;
+		case 'v':
+			verbose = 1;
+			break;
+		case 'M':
+			uid_map = optarg;
+			break;
+		case 'G':
+			gid_map = optarg;
+			break;
+		case 'U':
+			flags |= CLONE_NEWUSER;
+			break;
+		case 'I':
+			flags |= CLONE_NEWIMA;
+			break;
+		default:
+			usage(argv[0]);
+		}
+	}
+
+	/* -M or -G without -U is nonsensical */
+	if ((!uid_map || !gid_map) && !(flags & CLONE_NEWUSER))
+		usage(argv[0]);
+
+	args.argv = &argv[optind];
+
+	/* We use a pipe to synchronize the parent and child, in order to
+	 * ensure that the parent sets the UID and GID maps before the child
+	 * calls execve(). This ensures that the child maintains its
+	 * capabilities during the execve() in the common case where we
+	 * want to map the child's effective user ID to 0 in the new user
+	 * namespace. Without this synchronization, the child would lose
+	 * its capabilities if it performed an execve() with nonzero
+	 * user IDs (see the capabilities(7) man page for details of the
+	 * transformation of a process's capabilities during execve()).
+	 */
+	if (pipe(args.pipe_fd) == -1)
+		errExit("pipe");
+
+	/* Create the child in new namespace(s) */
+	child_pid = clone(childFunc, child_stack + STACK_SIZE,
+			  flags | SIGCHLD, &args);
+	if (child_pid == -1)
+		errExit("clone");
+
+	/* Parent falls through to here */
+	if (verbose)
+		printf("%s: PID of child created by clone() is %ld\n",
+		       argv[0], (long)child_pid);
+
+	/* Update the UID and GID maps in the child */
+	if (!uid_map) {
+		snprintf(map_path, PATH_MAX, "/proc/%ld/uid_map",
+			 (long)child_pid);
+		update_map(uid_map, map_path);
+	}
+	if (!gid_map) {
+		snprintf(map_path, PATH_MAX, "/proc/%ld/gid_map",
+			 (long)child_pid);
+		update_map(gid_map, map_path);
+	}
+
+	/* Close the write end of the pipe, to signal to the child that we
+	 * have updated the UID and GID maps
+	 */
+	close(args.pipe_fd[1]);
+
+	/* Wait for child */
+	if (waitpid(child_pid, NULL, 0) == -1)
+		errExit("waitpid");
+
+	if (verbose)
+		printf("%s: terminating\n", argv[0]);
+
+	exit(EXIT_SUCCESS);
+}
diff --git a/ima_tests/userns_child_exec_key.c b/ima_tests/userns_child_exec_key.c
new file mode 100644
index 0000000..2b578cd
--- /dev/null
+++ b/ima_tests/userns_child_exec_key.c
@@ -0,0 +1,318 @@
+/* userns_child_exec.c
+ *
+ * Copyright 2013, Michael Kerrisk
+ * Licensed under GNU General Public License v2 or later
+ *
+ * Create a child process that executes a shell command in new
+ * namespace(s); allow UID and GID mappings to be specified when
+ * creating a user namespace.
+ */
+
+#define _GNU_SOURCE
+#include <sched.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <openssl/sha.h>
+#include <openssl/pem.h>
+#include <openssl/hmac.h>
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include "imaevm.h"
+
+/* A simple error-handling function: print an error message based
+ * on the value in 'errno' and terminate the calling process
+ */
+
+#define errExit(msg)				\
+	do { perror(msg); exit(EXIT_FAILURE);	\
+	} while (0)
+
+#define CLONE_NEWIMA 0x02000000
+
+struct child_args {
+	/* Command to be executed by child, with arguments */
+	char **argv;
+
+	/* Pipe used to synchronize parent and child */
+	int pipe_fd[2];
+};
+
+static int verbose;
+
+int get_filesize(const char *filename)
+{
+	struct stat stats;
+	/*  Need to know the file length */
+	stat(filename, &stats);
+	return (int)stats.st_size;
+}
+
+static unsigned char *file2bin(const char *file, const char *ext, int *size)
+{
+	FILE *fp;
+	int len;
+	unsigned char *data;
+	char name[strlen(file) + (ext ? strlen(ext) : 0) + 2];
+
+	if (ext)
+		sprintf(name, "%s.%s", file, ext);
+	else
+		sprintf(name, "%s", file);
+
+	len = get_filesize(name);
+	fp = fopen(name, "r");
+	if (!fp)
+		return NULL;
+	data = malloc(len);
+	if (!fread(data, len, 1, fp))
+		len = 0;
+	fclose(fp);
+
+	*size = len;
+	return data;
+}
+
+static void usage(char *pname)
+{
+	fprintf(stderr, "Usage: %s [options] cmd [arg...]\n\n", pname);
+	fprintf(stderr,
+		"Create a child process that executes a shell command\n"
+		"in a new user namespace,\n"
+		"and possibly also other new namespace(s).\n\n");
+	fprintf(stderr, "Options can be:\n\n");
+#define fpe(str) fprintf(stderr, "    %s", str)
+	fpe("-i          New IPC namespace\n");
+	fpe("-m          New mount namespace\n");
+	fpe("-n          New network namespace\n");
+	fpe("-p          New PID namespace\n");
+	fpe("-u          New UTS namespace\n");
+	fpe("-U          New user namespace\n");
+	fpe("-I	     New IMA namespace\n");
+	fpe("-k	     Key to be loaded for the new namespace\n");
+	fpe("-M uid_map  Specify UID map for user namespace\n");
+	fpe("-G gid_map  Specify GID map for user namespace\n");
+	fpe("            If -M or -G is specified, -U is required\n");
+	fpe("-v          Display verbose messages\n");
+	fpe("\n");
+	fpe("Map strings for -M and -G consist of records of the form:\n");
+	fpe("\n");
+	fpe("    ID-inside-ns   ID-outside-ns   len\n");
+	fpe("\n");
+	fpe("A map string can contain multiple records,\n");
+	fpe("separated by commas; the commas are replaced\n");
+	fpe("by newlines before writing to map files.\n");
+
+	exit(EXIT_FAILURE);
+}
+
+/* Update the mapping file 'map_file', with the value provided in
+ * 'mapping', a string that defines a UID or GID mapping. A UID or
+ * GID mapping consists of one or more newline-delimited records
+ * of the form:
+ *
+ *     ID_inside-ns    ID-outside-ns   length
+ *
+ * Requiring the user to supply a string that contains newlines is
+ * of course inconvenient for command-line use. Thus, we permit the
+ * use of commas to delimit records in this string, and replace them
+ * with newlines before writing the string to the file.
+ */
+
+static void update_map(char *mapping, char *map_file)
+{
+	int fd, j;
+	/* Length of 'mapping' */
+	size_t map_len;
+
+	/* Replace commas in mapping string with newlines */
+	map_len = strlen(mapping);
+	for (j = 0; j < map_len; j++)
+		if (mapping[j] == ',')
+			mapping[j] = '\n';
+
+	fd = open(map_file, O_RDWR);
+	if (fd == -1) {
+		fprintf(stderr, "open %s: %s\n", map_file, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	if (write(fd, mapping, map_len) != map_len) {
+		fprintf(stderr, "write %s: %s\n", map_file, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	close(fd);
+}
+
+/* Start function for cloned child */
+static int childFunc(void *arg)
+{
+	struct child_args *args = (struct child_args *)arg;
+	char ch;
+
+	/* Wait until the parent has updated the UID and GID mappings. See
+	 * the comment in main(). We wait for end of file on a pipe that will
+	 * be closed by the parent process once it has updated the mappings.
+	 */
+
+	/* Close our descriptor for the write end
+	 * of the pipe so that we see EOF when
+	 * parent closes its descriptor
+	 */
+	close(args->pipe_fd[1]);
+	if (read(args->pipe_fd[0], &ch, 1) != 0) {
+		fprintf(stderr,
+			"Failure in child: read from pipe returned != 0\n");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Execute a shell command */
+	execvp(args->argv[0], args->argv);
+	errExit("execvp");
+}
+
+#define STACK_SIZE (1024 * 1024)
+
+static char child_stack[STACK_SIZE];    /* Space for child's stack */
+
+int main(int argc, char *argv[])
+{
+	int flags, opt;
+	pid_t child_pid;
+	struct child_args args;
+	char *uid_map, *gid_map;
+	char map_path[PATH_MAX];
+	char *key, *keyring;
+
+	/* Parse command-line options. The initial '+' character in
+	 * the final getopt() argument prevents GNU-style permutation
+	 * of command-line options. That's useful, since sometimes
+	 * the 'command' to be executed by this program itself
+	 * has command-line options. We don't want getopt() to treat
+	 * those as options to this program.
+	 */
+
+	flags = 0;
+	verbose = 0;
+	gid_map = NULL;
+	uid_map = NULL;
+	while ((opt = getopt(argc, argv, "+imnpuUIM:G:k:v")) != -1) {
+		switch (opt) {
+		case 'i':
+			flags |= CLONE_NEWIPC;
+			break;
+		case 'm':
+			flags |= CLONE_NEWNS;
+			break;
+		case 'n':
+			flags |= CLONE_NEWNET;
+			break;
+		case 'p':
+			flags |= CLONE_NEWPID;
+			break;
+		case 'u':
+			flags |= CLONE_NEWUTS;
+			break;
+		case 'v':
+			verbose = 1;
+			break;
+		case 'M':
+			uid_map = optarg;
+			break;
+		case 'G':
+			gid_map = optarg;
+			break;
+		case 'k':
+			key = optarg;
+			break;
+		case 'U':
+			flags |= CLONE_NEWUSER;
+			break;
+		case 'I':
+			flags |= CLONE_NEWIMA;
+			break;
+		default:
+			usage(argv[0]);
+		}
+	}
+
+	/* -M or -G without -U is nonsensical */
+	if ((!uid_map || !gid_map) && !(flags & CLONE_NEWUSER))
+		usage(argv[0]);
+
+	args.argv = &argv[optind];
+
+	/* We use a pipe to synchronize the parent and child, in order to
+	 * ensure that the parent sets the UID and GID maps before the child
+	 * calls execve(). This ensures that the child maintains its
+	 * capabilities during the execve() in the common case where we
+	 * want to map the child's effective user ID to 0 in the new user
+	 * namespace. Without this synchronization, the child would lose
+	 * its capabilities if it performed an execve() with nonzero
+	 * user IDs (see the capabilities(7) man page for details of the
+	 * transformation of a process's capabilities during execve()).
+	 */
+
+	if (pipe(args.pipe_fd) == -1)
+		errExit("pipe");
+
+	/* Create the child in new namespace(s) */
+	child_pid = clone(childFunc, child_stack + STACK_SIZE,
+			  flags | SIGCHLD, &args);
+	if (child_pid == -1)
+		errExit("clone");
+
+	/* Parent falls through to here */
+	if (verbose)
+		printf("%s: PID of child created by clone() is %ld\n",
+		       argv[0], (long)child_pid);
+
+	/* Update the UID and GID maps in the child */
+	if (!uid_map) {
+		snprintf(map_path, PATH_MAX, "/proc/%ld/uid_map",
+			 (long)child_pid);
+		update_map(uid_map, map_path);
+	}
+	if (!gid_map) {
+		snprintf(map_path, PATH_MAX, "/proc/%ld/gid_map",
+			 (long)child_pid);
+		update_map(gid_map, map_path);
+	}
+
+	if (!key) {
+		char command[100];
+
+		memset(command, '\0', 100);
+		sprintf(command, "./loadkey.sh %ld %s", (long)child_pid, key);
+		printf("Loading key: %s\n", command);
+		system(command);
+	} else {
+		printf("empty key\n");
+	}
+
+	/* Close the write end of the pipe, to signal to the child that we
+	 * have updated the UID and GID maps
+	 */
+
+	close(args.pipe_fd[1]);
+
+	/* Wait for child */
+	if (waitpid(child_pid, NULL, 0) == -1)
+		errExit("waitpid");
+
+	if (verbose)
+		printf("%s: terminating\n", argv[0]);
+
+	exit(EXIT_SUCCESS);
+}