From 8b61ad4af2770b5280a54101ceaf36a2f477706d Mon Sep 17 00:00:00 2001 From: phk Date: Tue, 3 Mar 2009 11:25:46 +0000 Subject: [PATCH] Add support for authenticating CLI telnet connections git-svn-id: svn+ssh://projects.linpro.no/svn/varnish/trunk@3865 d4fa192b-c00b-0410-8231-f00ffab90ce4 --- varnish-cache/bin/varnishd/mgt.h | 1 + varnish-cache/bin/varnishd/mgt_cli.c | 208 +++++++++++++++++++++----- varnish-cache/bin/varnishd/varnishd.c | 12 +- varnish-cache/include/cli.h | 7 + varnish-cache/include/cli_common.h | 1 + 5 files changed, 187 insertions(+), 42 deletions(-) diff --git a/varnish-cache/bin/varnishd/mgt.h b/varnish-cache/bin/varnishd/mgt.h index a01a5d6a..c71a19d0 100644 --- a/varnish-cache/bin/varnishd/mgt.h +++ b/varnish-cache/bin/varnishd/mgt.h @@ -52,6 +52,7 @@ int mgt_cli_askchild(unsigned *status, char **resp, const char *fmt, ...); void mgt_cli_start_child(int fdi, int fdo); void mgt_cli_stop_child(void); void mgt_cli_telnet(const char *T_arg); +void mgt_cli_secret(const char *S_arg); /* mgt_param.c */ void MCF_ParamSync(void); diff --git a/varnish-cache/bin/varnishd/mgt_cli.c b/varnish-cache/bin/varnishd/mgt_cli.c index a1e64eee..3068395e 100644 --- a/varnish-cache/bin/varnishd/mgt_cli.c +++ b/varnish-cache/bin/varnishd/mgt_cli.c @@ -57,12 +57,14 @@ #include "mgt.h" #include "mgt_cli.h" #include "vev.h" +#include "vsha256.h" #include "shmlog.h" #include "vlu.h" #include "vss.h" static int cli_i = -1, cli_o = -1; +static const char *secret_file; struct telnet { int fd; @@ -70,6 +72,19 @@ struct telnet { VTAILQ_ENTRY(telnet) list; }; +struct cli_port { + unsigned magic; +#define CLI_PORT_MAGIC 0x5791079f + struct vev *ev; + int fdi; + int fdo; + int verbose; + struct vlu *vlu; + struct cli cli[1]; + char *name; + char challenge[34]; +}; + static VTAILQ_HEAD(,telnet) telnets = VTAILQ_HEAD_INITIALIZER(telnets); static void telnet_close_all(void); static void telnet_close_one(int fd); @@ -166,7 +181,6 @@ static struct cli_proto cli_proto[] = { /*--------------------------------------------------------------------*/ - static void mcf_panic(struct cli *cli, const char * const *av, void *priv) { @@ -248,20 +262,94 @@ mgt_cli_stop_child(void) /* XXX: kick any users */ } -/*--------------------------------------------------------------------*/ +/*-------------------------------------------------------------------- + * Validate the authentication + */ -struct cli_port { - unsigned magic; -#define CLI_PORT_MAGIC 0x5791079f - struct vev *ev; - int fdi; - int fdo; - int verbose; - struct vlu *vlu; - struct cli cli[1]; - char *name; +static void +mcf_auth(struct cli *cli, const char *const *av, void *priv) +{ + char buf[1025]; + int i, fd; + struct SHA256Context sha256ctx; + unsigned char digest[SHA256_LEN]; + struct cli_port *cp; + + AN(av[2]); + CAST_OBJ_NOTNULL(cp, cli->priv, CLI_PORT_MAGIC); + (void)priv; + AN(secret_file); + fd = open(secret_file, O_RDONLY); + if (fd < 0) { + cli_out(cli, "Cannot open secret file (%s)\n", + strerror(errno)); + cli_result(cli, CLIS_CANT); + return; + } + i = read(fd, buf, sizeof buf); + if (i == 0) { + cli_out(cli, "Empty secret file"); + cli_result(cli, CLIS_CANT); + return; + } + if (i < 0) { + cli_out(cli, "Read error on secret file (%s)\n", + strerror(errno)); + cli_result(cli, CLIS_CANT); + return; + } + if (i == sizeof buf) { + cli_out(cli, "Secret file too long (> %d)\n", + sizeof buf - 1); + cli_result(cli, CLIS_CANT); + return; + } + buf[i] = '\0'; + AZ(close(fd)); + SHA256_Init(&sha256ctx); + SHA256_Update(&sha256ctx, cp->challenge, strlen(cp->challenge)); + SHA256_Update(&sha256ctx, buf, i); + SHA256_Update(&sha256ctx, cp->challenge, strlen(cp->challenge)); + SHA256_Final(digest, &sha256ctx); + for (i = 0; i < SHA256_LEN; i++) + sprintf(buf + i + i, "%02x", digest[i]); + if (strcasecmp(buf, av[2])) { + cli_result(cli, CLIS_UNKNOWN); + return; + } + cp->challenge[0] = '\0'; + cli_result(cli, CLIS_OK); + if (params->cli_banner) + mcf_banner(cli, av, priv); +} + +static struct cli_proto cli_auth[] = { + { CLI_HELP, mcf_help, cli_auth }, + { CLI_AUTH, mcf_auth, NULL }, + { CLI_QUIT, mcf_close, NULL}, + { NULL } }; +/*-------------------------------------------------------------------- + * Generate a random challenge + */ + +static void +mgt_cli_challenge(struct cli_port *cp) +{ + int i; + + for (i = 0; i + 2 < sizeof cp->challenge; i++) + cp->challenge[i] = (random() % 26) + 'a'; + cp->challenge[i++] = '\n'; + cp->challenge[i] = '\0'; + cli_out(cp->cli, "%s", cp->challenge); + cli_out(cp->cli, "\nAuthentication required.\n"); + cli_result(cp->cli, CLIS_AUTH); +} + +/*--------------------------------------------------------------------*/ + static int mgt_cli_vlu(void *priv, const char *p) { @@ -281,31 +369,39 @@ mgt_cli_vlu(void *priv, const char *p) if (*p == '\0') return (0); - cli_dispatch(cp->cli, cli_proto, p); - if (cp->cli->result == CLIS_UNKNOWN) - cli_dispatch(cp->cli, cli_debug, p); - if (cp->cli->result == CLIS_UNKNOWN) { - /* - * Command not recognized in master, try cacher if it is - * running. - */ - vsb_clear(cp->cli->sb); - cp->cli->result = CLIS_OK; - if (cli_o <= 0) { - cli_result(cp->cli, CLIS_UNKNOWN); - cli_out(cp->cli, - "Unknown request in manager process " - "(child not running).\n" - "Type 'help' for more info."); - } else { - i = write(cli_o, p, strlen(p)); - xxxassert(i == strlen(p)); - i = write(cli_o, "\n", 1); - xxxassert(i == 1); - (void)cli_readres(cli_i, &u, &q, params->cli_timeout); - cli_result(cp->cli, u); - cli_out(cp->cli, "%s", q); - free(q); + if (secret_file != NULL && cp->challenge[0] != '\0') { + /* Authentication not yet passed */ + cli_dispatch(cp->cli, cli_auth, p); + if (cp->cli->result == CLIS_UNKNOWN) + mgt_cli_challenge(cp); + } else { + cli_dispatch(cp->cli, cli_proto, p); + if (cp->cli->result == CLIS_UNKNOWN) + cli_dispatch(cp->cli, cli_debug, p); + if (cp->cli->result == CLIS_UNKNOWN) { + /* + * Command not recognized in master, try cacher if it is + * running. + */ + vsb_clear(cp->cli->sb); + cp->cli->result = CLIS_OK; + if (cli_o <= 0) { + cli_result(cp->cli, CLIS_UNKNOWN); + cli_out(cp->cli, + "Unknown request in manager process " + "(child not running).\n" + "Type 'help' for more info."); + } else { + i = write(cli_o, p, strlen(p)); + xxxassert(i == strlen(p)); + i = write(cli_o, "\n", 1); + xxxassert(i == 1); + (void)cli_readres(cli_i, + &u, &q, params->cli_timeout); + cli_result(cp->cli, u); + cli_out(cp->cli, "%s", q); + free(q); + } } } vsb_finish(cp->cli->sb); @@ -403,8 +499,16 @@ mgt_cli_setup(int fdi, int fdo, int verbose, const char *ident) cp->cli->sb = vsb_newauto(); XXXAN(cp->cli->sb); - - if (params->cli_banner) + cp->cli->priv = cp; + + /* + * If we have a secret file authenticate all CLI connections + * except the stdin/stdout debug port. + */ + if (cp->fdi != 0 && secret_file != NULL) { + mgt_cli_challenge(cp); + (void)VLU_Data("auth -\n", -1, cp->vlu); + } else if (params->cli_banner) (void)VLU_Data("banner\n", -1, cp->vlu); cp->ev = calloc(sizeof *cp->ev, 1); @@ -433,7 +537,6 @@ telnet_close_one(int fd) } } - static void telnet_close_all() { @@ -489,6 +592,31 @@ telnet_accept(const struct vev *ev, int what) return (0); } +void +mgt_cli_secret(const char *S_arg) +{ + int i, fd; + char buf[BUFSIZ]; + + + srandomdev(); + fd = open(S_arg, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Can not open secret-file \"%s\"\n", S_arg); + exit (2); + } + i = read(fd, buf, sizeof buf); + if (i == 0) { + fprintf(stderr, "Empty secret-file \"%s\"\n", S_arg); + exit (2); + } + if (i < 0) { + fprintf(stderr, "Can not read secret-file \"%s\"\n", S_arg); + exit (2); + } + secret_file = S_arg; +} + void mgt_cli_telnet(const char *T_arg) { diff --git a/varnish-cache/bin/varnishd/varnishd.c b/varnish-cache/bin/varnishd/varnishd.c index 52b3a570..07cfd7e1 100644 --- a/varnish-cache/bin/varnishd/varnishd.c +++ b/varnish-cache/bin/varnishd/varnishd.c @@ -207,6 +207,8 @@ usage(void) fprintf(stderr, FMT, "", " -s file,,,"); fprintf(stderr, FMT, "-t", "Default TTL"); + fprintf(stderr, FMT, "-S secret-file", + "Secret file for CLI authentication"); fprintf(stderr, FMT, "-T address:port", "Telnet listen address and port"); fprintf(stderr, FMT, "-V", "version"); @@ -404,6 +406,7 @@ main(int argc, char * const *argv) const char *h_arg = "classic"; const char *n_arg = NULL; const char *P_arg = NULL; + const char *S_arg = NULL; const char *s_arg = "file"; int s_arg_given = 0; const char *T_arg = NULL; @@ -445,7 +448,7 @@ main(int argc, char * const *argv) cli_check(cli); while ((o = getopt(argc, argv, - "a:b:Cdf:Fg:h:l:n:P:p:s:T:t:u:Vw:")) != -1) + "a:b:Cdf:Fg:h:l:n:P:p:S:s:T:t:u:Vw:")) != -1) switch (o) { case 'a': MCF_ParamSet(cli, "listen_address", optarg); @@ -497,6 +500,9 @@ main(int argc, char * const *argv) case 't': MCF_ParamSet(cli, "default_ttl", optarg); break; + case 'S': + S_arg = optarg; + break; case 'T': T_arg = optarg; break; @@ -617,7 +623,9 @@ main(int argc, char * const *argv) if (d_flag) mgt_cli_setup(0, 1, 1, "debug"); - if (T_arg) + if (S_arg != NULL) + mgt_cli_secret(S_arg); + if (T_arg != NULL) mgt_cli_telnet(T_arg); MGT_Run(); diff --git a/varnish-cache/include/cli.h b/varnish-cache/include/cli.h index 8ff92418..f793c979 100644 --- a/varnish-cache/include/cli.h +++ b/varnish-cache/include/cli.h @@ -231,6 +231,12 @@ "\tPrint welcome banner.", \ 0, 0 +#define CLI_AUTH \ + "auth", \ + "auth response", \ + "\tAuthenticate.", \ + 1, 1 + #define CLI_HIDDEN(foo, min_arg, max_arg) \ foo, NULL, NULL, min_arg, max_arg, @@ -245,6 +251,7 @@ enum cli_status_e { CLIS_TOOFEW = 104, CLIS_TOOMANY = 105, CLIS_PARAM = 106, + CLIS_AUTH = 107, CLIS_OK = 200, CLIS_CANT = 300, CLIS_COMMS = 400, diff --git a/varnish-cache/include/cli_common.h b/varnish-cache/include/cli_common.h index 18bed279..1a3a587c 100644 --- a/varnish-cache/include/cli_common.h +++ b/varnish-cache/include/cli_common.h @@ -33,6 +33,7 @@ struct cli { /* XXX: should be MINI_OBJ */ struct vsb *sb; enum cli_status_e result; + void *priv; }; int cli_writeres(int fd, const struct cli *cli); -- 2.39.5