summaryrefslogtreecommitdiff
path: root/request-key.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2010-02-22 15:43:21 +0000
committerDavid Howells <dhowells@redhat.com>2010-02-22 16:02:14 +0000
commit34718dc1040e93c2e2b2838f96f1f6a87564b598 (patch)
tree613369993baf12ea5718807c6039cd58d764a13b /request-key.c
parent9be94079efbccd689ae346184c9600cd6fb4aa9e (diff)
downloadkeyutils-34718dc1040e93c2e2b2838f96f1f6a87564b598.tar.gz
keyutils historical version 0.3-2v0.3-2
- Added timeout keyctl option - request_key auth keys must now be assumed - Fix keyctl argument ordering for debug negate line in request-key.conf
Diffstat (limited to 'request-key.c')
-rw-r--r--request-key.c230
1 files changed, 217 insertions, 13 deletions
diff --git a/request-key.c b/request-key.c
index ef66c05..a6c2825 100644
--- a/request-key.c
+++ b/request-key.c
@@ -1,6 +1,6 @@
/* request-key.c: hand a key request off to the appropriate process
*
- * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
@@ -8,7 +8,7 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * /sbin/request-key <op> <key> <uid> <gid> <threadring> <processring> <sessionring> <info>
+ * /sbin/request-key <op> <key> <uid> <gid> <threadring> <processring> <sessionring> [<info>]
*
* Searches the specified session ring for a key indicating the command to run:
* type: "user"
@@ -24,7 +24,10 @@
#include <syslog.h>
#include <unistd.h>
#include <fcntl.h>
+#include <errno.h>
#include <ctype.h>
+#include <sys/select.h>
+#include <sys/wait.h>
#include "keyutil.h"
@@ -37,6 +40,7 @@ static char *xthread_keyring;
static char *xprocess_keyring;
static char *xsession_keyring;
static int confline;
+static int norecurse;
static void lookup_action(char *op,
key_serial_t key,
@@ -46,12 +50,22 @@ static void lookup_action(char *op,
__attribute__((noreturn));
static void execute_program(char *op,
+ key_serial_t key,
char *ktype,
char *kdesc,
char *callout_info,
char *cmdline)
__attribute__((noreturn));
+static void pipe_to_program(char *op,
+ key_serial_t key,
+ char *ktype,
+ char *kdesc,
+ char *callout_info,
+ char *prog,
+ char **argv)
+ __attribute__((noreturn));
+
static int match(const char *pattern, int plen, const char *datum, int dlen);
static void debug(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
@@ -107,7 +121,7 @@ static void error(const char *fmt, ...)
int main(int argc, char *argv[])
{
key_serial_t key;
- char *ktype, *kdesc, *buf;
+ char *ktype, *kdesc, *buf, *callout_info;
int ret, ntype, dpos, dlen;
for (;;) {
@@ -125,7 +139,7 @@ int main(int argc, char *argv[])
break;
}
- if (argc != 9)
+ if (argc != 8 && argc != 9)
error("Unexpected argument count: %d\n", argc);
xkey = argv[2];
@@ -137,6 +151,11 @@ int main(int argc, char *argv[])
key = atoi(xkey);
+ /* assume authority over the key */
+ ret = keyctl_assume_authority(key);
+ if (ret < 0)
+ error("Failed to assume authority over key %d (%m)\n", key);
+
/* ask the kernel to describe the key to us */
if (xdebug <= 0) {
ret = keyctl_describe_alloc(key, &buf);
@@ -164,12 +183,24 @@ int main(int argc, char *argv[])
debug("Key type: %s\n", ktype);
debug("Key desc: %s\n", kdesc);
+ /* get hold of the callout info */
+ callout_info = argv[8];
+
+ if (!callout_info) {
+ void *tmp;
+
+ if (keyctl_read_alloc(KEY_SPEC_REQKEY_AUTH_KEY, &tmp) < 0)
+ error("Failed to retrieve callout info (%m)\n");
+
+ callout_info = tmp;
+ }
+
/* determine the action to perform */
lookup_action(argv[1], /* op */
key, /* ID of key under construction */
ktype, /* key type */
kdesc, /* key description */
- argv[8] /* call out info */
+ callout_info /* call out information */
);
inaccessible:
@@ -287,7 +318,7 @@ static void lookup_action(char *op,
if (!*p)
goto syntax_error;
- execute_program(op, ktype, kdesc, callout_info, p);
+ execute_program(op, key, ktype, kdesc, callout_info, p);
}
error("/etc/request-key.conf: No matching action\n");
@@ -364,6 +395,7 @@ yes:
* execute a program to deal with a key
*/
static void execute_program(char *op,
+ key_serial_t key,
char *ktype,
char *kdesc,
char *callout_info,
@@ -371,10 +403,20 @@ static void execute_program(char *op,
{
char *argv[256];
char *prog, *p, *q;
- int argc;
+ int argc, pipeit;
debug("execute_program('%s')\n", cmdline);
+ /* if the commandline begins with a bar, then we pipe the callout data into it and read
+ * back the payload data
+ */
+ pipeit = 0;
+
+ if (cmdline[0] == '|') {
+ pipeit = 1;
+ cmdline++;
+ }
+
/* extract the path to the program to run */
prog = p = cmdline;
while (*p && !isspace(*p)) p++;
@@ -501,21 +543,183 @@ static void execute_program(char *op,
argv[argc] = NULL;
- /* become the same UID/GID as the key requesting process */
- //setgid(atoi(xuid));
- //setuid(atoi(xgid));
-
- /* attempt to execute the command */
if (xdebug) {
char **ap;
- debug("Run %s\n", prog);
+ debug("%s %s\n", pipeit ? "Run" : "PipeThru", prog);
for (ap = argv; *ap; ap++)
debug("- argv[%zd] = \"%s\"\n", ap - argv, *ap);
}
+ /* become the same UID/GID as the key requesting process */
+ //setgid(atoi(xuid));
+ //setuid(atoi(xgid));
+
+ /* if the last argument is a single bar, we spawn off the program dangling on the end of
+ * three pipes and read the key material from the program, otherwise we just exec
+ */
+ if (pipeit)
+ pipe_to_program(op, key, ktype, kdesc, callout_info, prog, argv);
+
+ /* attempt to execute the command */
execv(prog, argv);
error("/etc/request-key.conf:%d: Failed to execute '%s': %m\n", confline, prog);
} /* end execute_program() */
+
+/*****************************************************************************/
+/*
+ * pipe the callout information to the specified program and retrieve the payload data over another
+ * pipe
+ */
+static void pipe_to_program(char *op,
+ key_serial_t key,
+ char *ktype,
+ char *kdesc,
+ char *callout_info,
+ char *prog,
+ char **argv)
+{
+ char payload[32768 + 1], *pp, *pc;
+ int ipi[2], opi[2], childpid, ifl, ofl, npay, ninfo, tmp;
+
+ if (pipe(ipi) < 0 || pipe(opi) < 0)
+ error("pipe failed: %m");
+
+ childpid = fork();
+ if (childpid == -1)
+ error("fork failed: %m");
+
+ if (childpid == 0) {
+ /* child process */
+ if (dup2(ipi[0], 0) < 0 ||
+ dup2(opi[1], 1) < 0)
+ error("dup2 failed: %m");
+ close(ipi[0]);
+ close(ipi[1]);
+ close(opi[0]);
+ close(opi[1]);
+
+ execv(prog, argv);
+ error("/etc/request-key.conf:%d: Failed to execute '%s': %m\n", confline, prog);
+ }
+
+ /* parent process */
+ close(ipi[0]);
+ close(opi[1]);
+
+#define TOSTDIN ipi[1]
+#define FROMSTDOUT opi[0]
+
+ ifl = fcntl(TOSTDIN, F_GETFL);
+ ofl = fcntl(FROMSTDOUT, F_GETFL);
+ if (ifl < 0 || ofl < 0)
+ error("fcntl/F_GETFL failed: %m");
+
+ ifl |= O_NONBLOCK;
+ ofl |= O_NONBLOCK;
+
+ if (fcntl(TOSTDIN, F_SETFL, ifl) < 0 ||
+ fcntl(FROMSTDOUT, F_SETFL, ofl) < 0)
+ error("fcntl/F_SETFL failed: %m");
+
+ npay = sizeof(payload);
+ pp = payload;
+
+ ninfo = strlen(callout_info);
+ pc = callout_info;
+
+ do {
+ fd_set rfds, wfds;
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+
+ if (TOSTDIN != -1) {
+ if (npay > 0) {
+ FD_SET(TOSTDIN, &wfds);
+ }
+ else {
+ close(TOSTDIN);
+ TOSTDIN = -1;
+
+ if (FROMSTDOUT == -1)
+ break;
+ }
+ }
+
+ if (FROMSTDOUT)
+ FD_SET(FROMSTDOUT, &rfds);
+
+ tmp = TOSTDIN > FROMSTDOUT ? TOSTDIN : FROMSTDOUT;
+ tmp++;
+
+ tmp = select(tmp, &rfds, &wfds, NULL, NULL);
+ if (tmp < 0)
+ error("select failed: %m");
+
+ if (FD_ISSET(TOSTDIN, &wfds)) {
+ tmp = write(TOSTDIN, pc, ninfo);
+ if (tmp < 0)
+ error("write failed: %m");
+ pc += tmp;
+ ninfo -= tmp;
+ }
+
+ if (FD_ISSET(FROMSTDOUT, &rfds)) {
+ tmp = read(FROMSTDOUT, pp, npay);
+ if (tmp < 0)
+ error("read failed: %m");
+
+ if (tmp == 0) {
+ close(FROMSTDOUT);
+ close(TOSTDIN);
+ FROMSTDOUT = -1;
+ TOSTDIN = -1;
+ }
+ else {
+ pp += tmp;
+ npay -= tmp;
+
+ if (npay == 0)
+ error("Too much data read from query program");
+ }
+ }
+
+ } while (TOSTDIN != -1 && FROMSTDOUT != -1);
+
+ /* wait for the program to exit */
+ if (waitpid(childpid, &tmp, 0) != childpid)
+ error("wait for child failed: %m\n");
+
+ /* if the process exited non-zero or died on a signal, then we call back in to ourself to
+ * decide on negation
+ * - this is not exactly beautiful but the quickest way of having configurable negation
+ * settings
+ */
+ if (WIFEXITED(tmp) && WEXITSTATUS(tmp) != 0) {
+ if (norecurse)
+ error("child exited %d\n", WEXITSTATUS(tmp));
+
+ norecurse = 1;
+ debug("child exited %d\n", WEXITSTATUS(tmp));
+ lookup_action(op, key, ktype, kdesc, "negate");
+ }
+
+ if (WIFSIGNALED(tmp)) {
+ if (norecurse)
+ error("child died on signal %d\n", WTERMSIG(tmp));
+
+ norecurse = 1;
+ debug("child died on signal %d\n", WTERMSIG(tmp));
+ lookup_action(op, key, ktype, kdesc, "negate");
+ }
+
+ /* attempt to instantiate the key */
+ if (tmp < keyctl_instantiate(key, payload, pp - payload, 0))
+ error("instantiate key failed: %m\n");
+
+ exit(0);
+
+} /* end pipe_to_program() */