From 516a7f03d1fca422624b34bcfcab56890141a756 Mon Sep 17 00:00:00 2001 From: "Steffen (Daode) Nurpmeso" Date: Sat, 12 Jul 2014 17:07:30 +0200 Subject: [PATCH] Add *agent-shell-lookupXY* (inspired by Gavin Troy) --- nail.1 | 82 +++++++++++++++++++++++++++++++++++++++++++++---------------- nail.h | 7 ++++++ urlcrecry.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 21 deletions(-) diff --git a/nail.1 b/nail.1 index ac3e0bd8..8c04ddd6 100644 --- a/nail.1 +++ b/nail.1 @@ -942,8 +942,7 @@ or .Va netrc-lookup , search the users .Pa .netrc -file for a `HOST' specific entry which provides a `login' name -(this source may also provide a `password'). +file for a `HOST' specific entry which provides a `login' name. .Pp If after all these steps there is still no `USER' then \*(UA will fall back to the user who is supposed to run \*(UA: @@ -956,14 +955,12 @@ or `getpwuid(getuid())' a.k.a. the current user. The identity of this user has been fixated during \*(UA startup and is known to be a valid user on the current host. .It -Authentication: unless otherwise noted this will first look for +Authentication: unless otherwise noted this will lookup the .Va PROTOCOL-auth-USER@HOST , -then -.Va PROTOCOL-auth-HOST -and finally -.Va PROTOCOL-auth , -which has a protocol-specific default should none of the variables be -set. +.Va PROTOCOL-auth-HOST , +.Va PROTOCOL-auth +variable chain, falling back to a protocol-specific default should this +have no success. .It If no `PASSWORD' has been given in the URL \(en it should be noted once that specifying the password in the URL is only syntactic sugar for the @@ -971,23 +968,31 @@ user, it'll never be part of an URL that \*(UA uses itself \(en, then if the `USER' has been found through .Pa .netrc then that may have provided the password, too. -Otherwise the variables +Otherwise the variable chain .Va password-USER@HOST , -.Va password-HOST -and +.Va password-HOST , .Va password -are looked up. +is looked up. .Pp -The chain is \*(OPionally continued via the described -.Pa .netrc -lookup when enabled via +\*(OP Then if any of the variables of the chain +.Va agent-shell-lookup-USER@HOST , +.Va agent-shell-lookup-HOST +and +.Va agent-shell-lookup +is set the shell command specified therein is run and the output (less +newline characters) will be used as the password; +note that any command failure is treated as a hard error, whereas it is +perfectly valid for such an agent to simply not return any data, in +which case the password lookup is continued somewhere else. +.Pp +The next variable chain that is inspected is the \*(OP +.Pa .netrc : .Va netrc-lookup-USER@HOST , -.Va netrc-lookup-HOST -or +.Va netrc-lookup-HOST , .Va netrc-lookup , -this time looking only for the password (multiple user accounts -for a single machine may exist as well as a fallback entry without user -but with a password). +but this time looking only for the password (multiple user accounts +for a single machine may exist as well as a fallback entry without +user but with a password). .Pp If at that point there is still no password available, but the (protocols') chosen authentication type requires a password, then in @@ -3366,6 +3371,39 @@ also left as an excercise to the user. .\" .Ss "Value options" {{{ .Ss "Value options" .Bl -tag -width ".Va autoprint" +.It Va agent-shell-lookup-USER@HOST , \ + Va agent-shell-lookup-HOST , \ + Va agent-shell-lookup +\*(IN \*(OP Account passwords can be fetched via an external agent +program in order to permit encrypted password storage \(en see +.Sx "URL syntax and credential lookup" +for more on credential lookup. +If this is set then the content is interpreted as a shell command the +output of which (with newline characters removed) is treated as the +account password shall the command succeed; e.g., via +.Xr gpg 1 : +.Bd -literal -offset indent +$ echo PASSWORD > .pass +$ gpg -e .pass +$ eval `gpg-agent --daemon \e + --pinentry-program=/usr/bin/pinentry-curses \e + --max-cache-ttl 99999 --default-cache-ttl 99999` +$ echo 'set agent-shell-lookup="gpg -d .pass.gpg"' \e + >> \*(ur +.Ed +.Pp +A couple of environment variables will be set for the agent: +.Bl -tag -width ".It Ev NAIL_HOST_PORT" +.It Ev NAIL_USER +The user (`USER') for which the password is looked up. +.It Ev NAIL_USER_ENC +The URL percent-encoded variant of +.Ev NAIL_USER . +.It Ev NAIL_HOST +The plain machine hostname of the user account. +.It Ev NAIL_HOST_PORT +The `HOST' (hostname possibly including port) of the user account. +.El .It Va attrlist A sequence of characters to print in the `attribute' column of a header summary, @@ -5351,6 +5389,8 @@ env MAILRC=/dev/null LC_ALL=C password=secret \*(ua -n -Sv15-compat \e .Xr bzip2 1 , .Xr file 1 , .Xr fmt 1 , +.Xr gpg-agent 1 , +.Xr gpg 1 , .Xr gzip 1 , .Xr less 1 , .Xr more 1 , diff --git a/nail.h b/nail.h index 211aef47..36318572 100644 --- a/nail.h +++ b/nail.h @@ -154,6 +154,12 @@ #define NETRC "~/.netrc" #define TMPDIR_FALLBACK "/tmp" +/* Some environment variables for pipe hooks */ +#define AGENT_USER "NAIL_USER" +#define AGENT_USER_ENC "NAIL_USER_ENC" +#define AGENT_HOST "NAIL_HOST" +#define AGENT_HOST_PORT "NAIL_HOST_PORT" + #undef COLOUR #ifdef HAVE_COLOUR # define COLOUR(X) X @@ -840,6 +846,7 @@ enum okeys { ok_b_writebackedited, /* Option keys for values options */ + ok_v_agent_shell_lookup, ok_v_attrlist, ok_v_autobcc, ok_v_autocc, diff --git a/urlcrecry.c b/urlcrecry.c index 8b295775..4069e39c 100644 --- a/urlcrecry.c +++ b/urlcrecry.c @@ -77,6 +77,11 @@ static bool_t __nrc_find_pass(struct url *urlp, bool_t user_match, struct nrc_node const *nrc); #endif /* HAVE_NETRC */ +/* The password can also be gained through external agents */ +#ifdef HAVE_AGENT +static bool_t _agent_shell_lookup(struct url *urlp, char const *comm); +#endif + #ifdef HAVE_SOCKETS static char * _url_last_at_before_slash(char const *sp) @@ -444,6 +449,70 @@ __nrc_find_pass(struct url *urlp, bool_t user_match, struct nrc_node const *nrc) } #endif /* HAVE_NETRC */ +#ifdef HAVE_AGENT +static bool_t +_agent_shell_lookup(struct url *urlp, char const *comm) +{ + char buf[128]; + char const *env_addon[8]; + struct str s; + FILE *pbuf; + union {char const *cp; int c; sighandler_type sht;} u; + size_t cl, l; + bool_t rv = FAL0; + NYD2_ENTER; + + env_addon[0] = str_concat_csvl(&s, AGENT_USER, "=", urlp->url_user.s, + NULL)->s; + env_addon[1] = str_concat_csvl(&s, AGENT_USER_ENC, "=", urlp->url_user_enc.s, + NULL)->s; + env_addon[2] = str_concat_csvl(&s, AGENT_HOST, "=", urlp->url_host.s, + NULL)->s; + env_addon[3] = str_concat_csvl(&s, AGENT_HOST_PORT, "=", urlp->url_h_p.s, + NULL)->s; + env_addon[4] = NULL; + + if ((u.cp = ok_vlook(SHELL)) == NULL) + u.cp = XSHELL; + if ((pbuf = Popen(comm, "r", u.cp, env_addon, -1)) == NULL) { + fprintf(stderr, _("*agent-shell-lookup* startup failed (`%s')\n"), + comm); + goto jleave; + } + + for (s.s = NULL, s.l = cl = l = 0; (u.c = getc(pbuf)) != EOF; ++cl) { + if (u.c == '\n') /* xxx */ + continue; + buf[l++] = u.c; + if (l == sizeof(buf) - 1) { + n_str_add_buf(&s, buf, l); + l = 0; + } + } + if (l > 0) + n_str_add_buf(&s, buf, l); + + if (!Pclose(pbuf, TRU1)) { + if (options & OPT_D_V) + fprintf(stderr, _("*agent-shell-lookup* execution failure (`%s')\n"), + comm); + goto jleave; + } + + /* We are responsible for duplicating this buffer! */ + if (s.s != NULL) + urlp->url_pass.s = savestrbuf(s.s, urlp->url_pass.l = s.l); + else if (cl > 0) + urlp->url_pass.s = UNCONST(""), urlp->url_pass.l = 0; + rv = TRU1; +jleave: + if (s.s != NULL) + free(s.s); + NYD2_LEAVE; + return rv; +} +#endif + FL char * (urlxenc)(char const *cp, bool_t ispath SALLOC_DEBUG_ARGS) { @@ -1059,6 +1128,17 @@ ccred_lookup(struct ccred *ccp, struct url *urlp) if ((s = xok_vlook(password, urlp, OXM_ALL)) != NULL) goto js2pass; +# ifdef HAVE_AGENT + if ((s = xok_vlook(agent_shell_lookup, urlp, OXM_ALL)) != NULL) { + if (!_agent_shell_lookup(urlp, s)) { + ccp = NULL; + goto jleave; + } else if (urlp->url_pass.s != NULL) { + ccp->cc_pass = urlp->url_pass; + goto jleave; + } + } +# endif # ifdef HAVE_NETRC if (xok_blook(netrc_lookup, urlp, OXM_ALL) && _nrc_lookup(urlp, TRU1)) { ccp->cc_pass = urlp->url_pass; -- 2.11.4.GIT