/* -*-c-*-
*
- * $Id: pixie.c,v 1.3 1999/12/22 22:14:40 mdw Exp $
+ * $Id: pixie.c,v 1.4 2000/06/17 11:50:53 mdw Exp $
*
* Passphrase pixie for Catacomb
*
/*----- Revision history --------------------------------------------------*
*
* $Log: pixie.c,v $
+ * Revision 1.4 2000/06/17 11:50:53 mdw
+ * New pixie protocol allowing application to request passphrases and send
+ * them to the pixie. Use the secure arena interface for the input
+ * buffer. Extend the input buffer. Other minor fixes.
+ *
* Revision 1.3 1999/12/22 22:14:40 mdw
* Only produce initialization message if verbose.
*
#include <mLib/sub.h>
#include <mLib/tv.h>
+#include "arena.h"
#include "lmem.h"
#include "passphrase.h"
#include "pixie.h"
static unsigned flags = 0;
enum {
- F_SYSLOG = 1
+ F_SYSLOG = 1,
+ F_FETCH = 2
};
/*----- Event logging -----------------------------------------------------*/
dstr_destroy(&d);
if (r < 0)
goto fail_0;
- return (0);
+ goto ok;
/* --- Tidy up when things go wrong --- */
dstr_putf(&d, "%s %s: ", msg, tag);
rc = pixie_getpass(d.buf, buf, sz);
dstr_destroy(&d);
- return (rc);
+ if (rc)
+ return (rc);
+ goto ok;
}
+
+ /* --- Sort out the buffer --- *
+ *
+ * Strip leading spaces.
+ */
+
+ok: {
+ char *p = buf;
+ size_t len;
+ while (isspace((unsigned char)*p))
+ p++;
+ len = strlen(p);
+ memmove(buf, p, len);
+ p[len] = 0;
+ }
+
+ /* --- Done --- */
+
+ return (0);
}
/* --- @p_get@ --- *
*
- * Arguments: @const char *tag@ = pointer to tag string
+ * Arguments: @const char **q@ = where to store the result
+ * @const char *tag@ = pointer to tag string
* @unsigned mode@ = reading mode (verify?)
* @time_t exp@ = expiry time suggestion
*
- * Returns: Pointer to passphrase, or zero.
+ * Returns: Zero if successful, @-1@ on a read failure, or @+1@ if the
+ * passphrase is missing and there is no fetcher. (This will
+ * always happen if there is no fetcher and @mode@ is
+ * @PMODE_VERIFY@.
*
* Use: Reads a passphrase from somewhere.
*/
-static const char *p_get(const char *tag, unsigned mode, time_t exp)
+static int p_get(const char **q, const char *tag, unsigned mode, time_t exp)
{
#define LBUFSZ 1024
if (verbose > 1)
log("passphrase `%s' requested", tag);
+ /* --- If there is no fetcher, life is simpler --- */
+
+ if (!(flags & F_FETCH)) {
+ if (mode == PMODE_VERIFY)
+ return (+1);
+ if ((p = p_find(tag)) == 0)
+ return (+1);
+ *q = p->p;
+ return (0);
+ }
+
/* --- Try to find the phrase --- */
- if ((p = p_find(tag)) == 0) {
+ if (mode == PMODE_VERIFY)
+ p_flush(tag);
+ if (mode == PMODE_VERIFY || (p = p_find(tag)) == 0) {
if ((pp = p_alloc(LBUFSZ)) == 0)
goto fail;
- if (p_request("Passphrase", tag, pp, LBUFSZ) < 0)
+ if (p_request(mode == PMODE_READ ? "Passphrase" : "New passphrase",
+ tag, pp, LBUFSZ) < 0)
goto fail;
p = p_add(tag, pp, exp);
if (!p)
memset(pp, 0, LBUFSZ);
l_free(&lm, pp);
}
- return (p->p);
+ *q = p->p;
+ return (0);
/* --- Tidy up if things went wrong --- */
selbuf b;
int fd;
sel_timer timer;
+ unsigned f;
} pixserv;
-#define PIXSERV_TIMEOUT 300
+enum { px_stdin = 1 };
+
+#define PIXSERV_TIMEOUT 30
/* --- @pixserv_expire@ --- *
*
pixserv *px = p;
if (px->fd != px->b.reader.fd)
close(px->fd);
- selbuf_disable(&px->b);
+ selbuf_destroy(&px->b);
close(px->b.reader.fd);
DESTROY(px);
}
/* --- Handle an end-of-file --- */
- sel_rmtimer(&px->timer);
+ if (!(px->f & px_stdin))
+ sel_rmtimer(&px->timer);
if (!s) {
if (px->fd != px->b.reader.fd)
close(px->fd);
- selbuf_disable(&px->b);
+ selbuf_destroy(&px->b);
close(px->b.reader.fd);
return;
}
/* --- Fiddle the timeout --- */
- {
+ if (!(px->f & px_stdin)) {
struct timeval tv;
gettimeofday(&tv, 0);
tv.tv_sec += PIXSERV_TIMEOUT;
INFO PASS tag [expire]\n\
INFO VERIFY tag [expire]\n\
INFO FLUSH [tag]\n\
+INFO SET tag [expire] -- phrase\n\
INFO QUIT\n\
OK\n\
");
(mode = PMODE_VERIFY, strcmp(q, "verify") == 0)) {
unsigned long t;
const char *p;
+ int rc;
if ((q = str_getword(&s)) == 0)
pixserv_write(px, "FAIL missing tag\n");
else if ((t = pixserv_timeout(s)) == 0)
pixserv_write(px, "FAIL bad timeout\n");
- else if ((p = p_get(q, mode, t > timeout ? timeout : t)) == 0)
- pixserv_write(px, "FAIL error reading passphrase\n");
- else
- pixserv_write(px, "OK %s\n", p);
+ else {
+ rc = p_get(&p, q, mode, t > timeout ? timeout : t);
+ switch (rc) {
+ case 0:
+ pixserv_write(px, "OK %s\n", p);
+ break;
+ case -1:
+ pixserv_write(px, "FAIL error reading passphrase\n");
+ break;
+ case +1:
+ pixserv_write(px, "MISSING\n");
+ break;
+ }
+ }
}
/* --- Flush existing passphrases --- */
pixserv_write(px, "OK\n");
}
+ /* --- Set a passphrase --- */
+
+ else if (strcmp(q, "set") == 0) {
+ char *tag;
+ unsigned long t;
+ if ((tag = str_getword(&s)) == 0)
+ pixserv_write(px, "FAIL missing tag\n");
+ else if ((q = str_getword(&s)) == 0)
+ pixserv_write(px, "FAIL no passphrase\n");
+ else {
+ if (strcmp(q, "--") != 0) {
+ t = pixserv_timeout(q);
+ q = str_getword(&s);
+ } else
+ t = pixserv_timeout(0);
+ if (!q)
+ pixserv_write(px, "FAIL no passphrase\n");
+ else if (strcmp(q, "--") != 0)
+ pixserv_write(px, "FAIL rubbish found before passphrase\n");
+ else {
+ p_flush(tag);
+ p_add(tag, s, t);
+ pixserv_write(px, "OK\n");
+ }
+ }
+ }
+
/* --- Shut the server down --- */
else if (strcmp(q, "quit") == 0) {
if (verbose)
- log("client requested shutdown");
+ log("%s client requested shutdown",
+ px->f & px_stdin ? "local" : "remote");
pixserv_write(px, "OK\n");
exit(0);
}
* Arguments: @int fd@ = file descriptor to read from
* @int ofd@ = file descriptor to write to
*
- * Returns: ---
+ * Returns: Pointer to the new connection.
*
* Use: Creates a new Pixie server instance for a new connection.
*/
-static void pixserv_create(int fd, int ofd)
+static pixserv *pixserv_create(int fd, int ofd)
{
pixserv *px = CREATE(pixserv);
struct timeval tv;
fdflags(ofd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
px->fd = ofd;
selbuf_init(&px->b, &sel, fd, pixserv_line, px);
+ px->b.b.a = arena_secure;
+ selbuf_setsize(&px->b, 1024);
gettimeofday(&tv, 0);
tv.tv_sec += PIXSERV_TIMEOUT;
sel_addtimer(&sel, &px->timer, &tv, pixserv_expire, px);
+ px->f = 0;
+ return (px);
}
/* --- @pixserv_accept@ --- *
/* --- Variables --- */
-static selbuf c_server, c_client;;
+static selbuf c_server, c_client;
+static c_flags = 0;
+enum { cf_uclose = 1, cf_sclose = 2 };
/* --- Line handler functions --- */
static void c_uline(char *s, void *p)
{
size_t sz;
- if (!s)
- exit(0);
- sz = strlen(s);
- s[sz++] = '\n';
- write(c_server.reader.fd, s, sz);
+ if (!s) {
+ selbuf_destroy(&c_client);
+ shutdown(c_server.reader.fd, 1);
+ c_flags |= cf_uclose;
+ } else {
+ sz = strlen(s);
+ s[sz++] = '\n';
+ write(c_server.reader.fd, s, sz);
+ }
}
static void c_sline(char *s, void *p)
{
if (!s) {
- if (verbose > 1)
+ selbuf_destroy(&c_server);
+ if (!(c_flags & cf_uclose)) {
moan("server closed the connection");
+ selbuf_destroy(&c_client);
+ }
exit(0);
- }
- puts(s);
+ } else
+ puts(s);
}
/* --- @pix_client@ --- *
-v, --version Emit more log messages.\n\
-s, --socket=FILE Name the pixie's socket.\n\
-c, --command=COMMAND Shell command to read a passphrase.\n\
+-f, --fetch Fetch passphrases from the terminal.\n\
-t, --timeout=TIMEOUT Length of time to retain a passphrase in memory.\n\
-i, --interactive Allow commands to be typed interactively.\n\
+-d, --daemon Fork into the background after initialization.\n\
-l, --syslog Emit log messages to the system log.\n\
\n\
The COMMAND may contain `%m' and `%t' markers which are replaced by a\n\
/* --- Set up the locked memory area --- */
- l_init(&lm, 4096);
+ l_init(&lm, 16384);
setuid(getuid());
/* --- Parse command line arguments --- */
{ "client", 0, 0, 'C' },
{ "socket", OPTF_ARGREQ, 0, 's' },
{ "command", OPTF_ARGREQ, 0, 'c' },
+ { "fetch", 0, 0, 'f' },
{ "timeout", OPTF_ARGREQ, 0, 't' },
{ "interactive", 0, 0, 'i' },
{ "stdin", 0, 0, 'i' },
{ 0, 0, 0, 0 }
};
- int i = mdwopt(argc, argv, "hVuqvCs:c:t:idl", opts, 0, 0, 0);
+ int i = mdwopt(argc, argv, "hVuqvCs:c:ft:idl", opts, 0, 0, 0);
if (i < 0)
break;
break;
case 'c':
command = optarg;
+ flags |= F_FETCH;
+ break;
+ case 'f':
+ flags |= F_FETCH;
break;
case 'i':
f |= f_stdin;
log("couldn't lock passphrase buffer");
}
dstr_destroy(&d);
+ arena_setsecure(&lm.a);
}
/* --- Set signal behaviours --- */
/* --- Set up the server --- */
pix_setup(sun, sz);
- if (f & f_stdin)
- pixserv_create(STDIN_FILENO, STDOUT_FILENO);
+ if (f & f_stdin) {
+ pixserv *px = pixserv_create(STDIN_FILENO, STDOUT_FILENO);
+ sel_rmtimer(&px->timer);
+ px->f |= px_stdin;
+ }
/* --- Fork into the background if requested --- */
if (((f & f_stdin) &&
(isatty(STDIN_FILENO) || isatty(STDOUT_FILENO))) ||
- !command)
+ (!command && (flags & F_FETCH)))
die(1, "can't become a daemon if terminal required");
if ((kid = fork()) < 0)