aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFredrik Thulin <fredrik@thulin.net>2016-05-16 11:09:30 +0200
committerFredrik Thulin <fredrik@thulin.net>2016-05-16 11:09:30 +0200
commit1a5727c568e36b927ef2088b2b02bae4c84933f3 (patch)
treedf86b7ba3ebfc53a1d444a17e7b6de97339b002e
parent5ad2af0a9c1a3fab20a3809cc2fdce66876ff456 (diff)
Update to work in our embedded ARM environment.
The main areas of change are: 1) No dynamic memory allocations 2) Not socket oriented anymore There are some areas that might need revisiting (such as string formatting), and I think we ought to fuzz this code to make sure I did not break it. There are surely some bugs in there, like it seems one has to press CTRL+A twice to jump to the beginning of lines.
-rw-r--r--Makefile14
-rw-r--r--clitest.c99
-rw-r--r--libcli.c2328
-rw-r--r--libcli.h61
4 files changed, 995 insertions, 1507 deletions
diff --git a/Makefile b/Makefile
index 3062635..c4d9425 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,9 @@
# Build dynamic library by default
-DYNAMIC_LIB ?= 1
+DYNAMIC_LIB ?= 0
# Build static library by default
STATIC_LIB ?= 1
# Run tests by default
-TESTS ?= 1
+TESTS ?= 0
UNAME = $(shell sh -c 'uname -s 2>/dev/null || echo not')
DESTDIR =
@@ -15,7 +15,13 @@ REVISION = 7
LIB = libcli.so
LIB_STATIC = libcli.a
-CC = gcc
+CC = arm-none-eabi-gcc
+CFLAGS += -mcpu=cortex-m4 -mthumb -mlittle-endian -mthumb-interwork
+CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
+CFLAGS += -ffunction-sections -fdata-sections
+CFLAGS += -DCRYPTECH_NO_FDOPEN -DCRYPTECH_NO_SELECT
+CFLAGS += -ggdb -O2 -Wall -Wextra -Warray-bounds -Wl,--gc-sections -specs=nosys.specs -g
+#CC = gcc
AR = ar
ARFLAGS = rcs
DEBUG = -g
@@ -28,7 +34,7 @@ ifeq ($(UNAME),Darwin)
override LDFLAGS += -Wl,-install_name,$(LIB).$(MAJOR).$(MINOR)
else
override LDFLAGS += -Wl,-soname,$(LIB).$(MAJOR).$(MINOR)
-LIBS = -lcrypt
+#LIBS = -lcrypt
endif
ifeq (1,$(DYNAMIC_LIB))
diff --git a/clitest.c b/clitest.c
index ace87bc..b6260f7 100644
--- a/clitest.c
+++ b/clitest.c
@@ -202,8 +202,39 @@ void pc(UNUSED(struct cli_def *cli), const char *string)
int main()
{
- struct cli_command *c;
struct cli_def *cli;
+ struct cli_command cmd_test_s = {(char *) "test", cmd_test, 0, NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL, NULL, NULL};
+ struct cli_command cmd_simple_s = {(char *) "simple", NULL, 0, NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL, NULL, NULL};
+ struct cli_command cmd_simon_s = {(char *) "simon", NULL, 0, NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL, NULL, NULL};
+ struct cli_command cmd_set_s = {(char *) "set", NULL, 0, NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL, NULL, NULL};
+
+ struct cli_command cmd_show_s = {(char *) "show", NULL, 0, NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL, NULL, NULL};
+ struct cli_command cmd_show_regular_s = {(char *) "regular", cmd_show_regular, 0,
+ (char *) "Show the how many times cli_regular has run",
+ PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL, NULL, NULL};
+ struct cli_command cmd_show_counters_s = {(char *) "counters", cmd_test, 0,
+ (char *) "Show the counters that the system uses",
+ PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL, NULL, NULL};
+ struct cli_command cmd_show_junk_s = {(char *) "junk", cmd_test, 0, NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL, NULL, NULL};
+
+ struct cli_command cmd_config_int_s = {(char *) "interface", cmd_config_int, 0,
+ (char *) "Configure an interface",
+ PRIVILEGE_PRIVILEGED, MODE_CONFIG, NULL, NULL, NULL};
+ struct cli_command cmd_config_int_exit_s = {(char *) "exit", cmd_config_int_exit, 0,
+ (char *) "Exit from interface configuration",
+ PRIVILEGE_PRIVILEGED, MODE_CONFIG_INT, NULL, NULL, NULL};
+ struct cli_command cmd_config_int_address_s = {(char *) "address", cmd_test, 0,
+ (char *) "Set IP address",
+ PRIVILEGE_PRIVILEGED, MODE_CONFIG_INT, NULL, NULL, NULL};
+ struct cli_command cmd_debug_s = {(char *) "debug", NULL, 0, NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL, NULL, NULL};
+ struct cli_command cmd_debug_regular_s = {(char *) "regular", cmd_debug_regular, 0,
+ (char *) "Enable cli_regular() callback debugging",
+ PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL, NULL, NULL};
+ struct cli_command cmd_context_s = {(char *) "context", cmd_context, 0,
+ (char *) "Test a user-specified context",
+ PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL, NULL, NULL};
+
+
int s, x;
struct sockaddr_in addr;
int on = 1;
@@ -219,57 +250,51 @@ int main()
#endif
// Prepare a small user context
- char mymessage[] = "I contain user data!";
+ static char mymessage[] = "I contain user data!";
+ static char banner[] = "libcli test environment";
+ static char hostname[] = "router";
struct my_context myctx;
myctx.value = 5;
myctx.message = mymessage;
- cli = cli_init();
- cli_set_banner(cli, "libcli test environment");
- cli_set_hostname(cli, "router");
+ if (!(cli = calloc(sizeof(struct cli_def), 1)))
+ return 0;
+
+ if (! cli_init(cli)) {
+ printf("Error initialising CLI\n");
+ return 1;
+ }
+ cli_set_banner(cli, banner);
+ cli_set_hostname(cli, hostname);
cli_telnet_protocol(cli, 1);
- cli_regular(cli, regular_callback);
- cli_regular_interval(cli, 5); // Defaults to 1 second
+ //cli_regular(cli, regular_callback);
+ //cli_regular_interval(cli, 5); // Defaults to 1 second
cli_set_idle_timeout_callback(cli, 60, idle_timeout); // 60 second idle timeout
- cli_register_command(cli, NULL, "test", cmd_test, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL);
-
- cli_register_command(cli, NULL, "simple", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL);
-
- cli_register_command(cli, NULL, "simon", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL);
-
- cli_register_command(cli, NULL, "set", cmd_set, PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL);
+ cli_register_command2(cli, &cmd_test_s, NULL);
+ cli_register_command2(cli, &cmd_simple_s, NULL);
+ cli_register_command2(cli, &cmd_simon_s, NULL);
+ cli_register_command2(cli, &cmd_set_s, NULL);
- c = cli_register_command(cli, NULL, "show", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL);
+ cli_register_command2(cli, &cmd_show_s, NULL);
+ cli_register_command2(cli, &cmd_show_regular_s, &cmd_show_s);
+ cli_register_command2(cli, &cmd_show_counters_s, &cmd_show_s);
+ cli_register_command2(cli, &cmd_show_junk_s, &cmd_show_s);
- cli_register_command(cli, c, "regular", cmd_show_regular, PRIVILEGE_UNPRIVILEGED, MODE_EXEC,
- "Show the how many times cli_regular has run");
+ cli_register_command2(cli, &cmd_config_int_s, NULL);
+ cli_register_command2(cli, &cmd_config_int_exit_s, NULL);
+ cli_register_command2(cli, &cmd_config_int_address_s, NULL);
- cli_register_command(cli, c, "counters", cmd_test, PRIVILEGE_UNPRIVILEGED, MODE_EXEC,
- "Show the counters that the system uses");
-
- cli_register_command(cli, c, "junk", cmd_test, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL);
-
- cli_register_command(cli, NULL, "interface", cmd_config_int, PRIVILEGE_PRIVILEGED, MODE_CONFIG,
- "Configure an interface");
-
- cli_register_command(cli, NULL, "exit", cmd_config_int_exit, PRIVILEGE_PRIVILEGED, MODE_CONFIG_INT,
- "Exit from interface configuration");
-
- cli_register_command(cli, NULL, "address", cmd_test, PRIVILEGE_PRIVILEGED, MODE_CONFIG_INT, "Set IP address");
-
- c = cli_register_command(cli, NULL, "debug", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL);
-
- cli_register_command(cli, c, "regular", cmd_debug_regular, PRIVILEGE_UNPRIVILEGED, MODE_EXEC,
- "Enable cli_regular() callback debugging");
+ cli_register_command2(cli, &cmd_debug_s, NULL);
+ cli_register_command2(cli, &cmd_debug_regular_s, &cmd_debug_s);
// Set user context and its command
cli_set_context(cli, (void*)&myctx);
- cli_register_command(cli, NULL, "context", cmd_context, PRIVILEGE_UNPRIVILEGED, MODE_EXEC,
- "Test a user-specified context");
+ cli_register_command2(cli, &cmd_context_s, NULL);
cli_set_auth_callback(cli, check_auth);
cli_set_enable_callback(cli, check_enable);
// Test reading from a file
+ /*
{
FILE *fh;
@@ -282,7 +307,7 @@ int main()
fclose(fh);
}
}
-
+ */
if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
diff --git a/libcli.c b/libcli.c
index 3893b2a..3e33bd2 100644
--- a/libcli.c
+++ b/libcli.c
@@ -3,20 +3,23 @@
#include <windows.h>
#endif
+#define CRYPTECH_NO_CRYPT
+#define CRYPTECH_NO_MEMORY_H
+//#define CRYPTECH_NO_FDOPEN
+//#define CRYPTECH_NO_SELECT
+#define CRYPTECH_NO_REGEXP
+
#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
+#ifndef CRYPTECH_NO_MEMORY_H
#include <memory.h>
-#if !defined(__APPLE__) && !defined(__FreeBSD__)
-#include <malloc.h>
-#endif
+#endif /* CRYPTECH_NO_MEMORY_H */
#include <string.h>
-#include <ctype.h>
#include <unistd.h>
-#include <time.h>
-#ifndef WIN32
+#if !defined(WIN32) && !defined(CRYPTECH_NO_REGEXP)
#include <regex.h>
#endif
#include "libcli.h"
@@ -32,64 +35,11 @@
#define MATCH_REGEX 1
#define MATCH_INVERT 2
-#ifdef WIN32
+#ifdef CRYPTECH_NO_REGEXP
/*
- * Stupid windows has multiple namespaces for filedescriptors, with different
- * read/write functions required for each ..
+ * Dummy definitions to allow compilation on Cryptech STM32 MCU
*/
-int read(int fd, void *buf, unsigned int count) {
- return recv(fd, buf, count, 0);
-}
-
-int write(int fd, const void *buf, unsigned int count) {
- return send(fd, buf, count, 0);
-}
-
-int vasprintf(char **strp, const char *fmt, va_list args) {
- int size;
-
- size = vsnprintf(NULL, 0, fmt, args);
- if ((*strp = malloc(size + 1)) == NULL) {
- return -1;
- }
-
- size = vsnprintf(*strp, size + 1, fmt, args);
- return size;
-}
-
-int asprintf(char **strp, const char *fmt, ...) {
- va_list args;
- int size;
-
- va_start(args, fmt);
- size = vasprintf(strp, fmt, args);
-
- va_end(args);
- return size;
-}
-
-int fprintf(FILE *stream, const char *fmt, ...) {
- va_list args;
- int size;
- char *buf;
-
- va_start(args, fmt);
- size = vasprintf(&buf, fmt, args);
- if (size < 0) {
- goto out;
- }
- size = write(stream->_file, buf, size);
- free(buf);
-
-out:
- va_end(args);
- return size;
-}
-
-/*
- * Dummy definitions to allow compilation on Windows
- */
-int regex_dummy() {return 0;};
+int regex_dummy() {return 0;}
#define regfree(...) regex_dummy()
#define regexec(...) regex_dummy()
#define regcomp(...) regex_dummy()
@@ -97,15 +47,7 @@ int regex_dummy() {return 0;};
#define REG_NOSUB 0
#define REG_EXTENDED 0
#define REG_ICASE 0
-#endif
-
-enum cli_states {
- STATE_LOGIN,
- STATE_PASSWORD,
- STATE_NORMAL,
- STATE_ENABLE_PASSWORD,
- STATE_ENABLE
-};
+#endif /* CRYPTECH_NO_REGEXP */
struct unp {
char *username;
@@ -119,35 +61,32 @@ struct cli_filter_cmds
const char *help;
};
-/* free and zero (to avoid double-free) */
-#define free_z(p) do { if (p) { free(p); (p) = 0; } } while (0)
-
-int cli_match_filter_init(struct cli_def *cli, int argc, char **argv, struct cli_filter *filt);
-int cli_range_filter_init(struct cli_def *cli, int argc, char **argv, struct cli_filter *filt);
-int cli_count_filter_init(struct cli_def *cli, int argc, char **argv, struct cli_filter *filt);
-int cli_match_filter(struct cli_def *cli, const char *string, void *data);
-int cli_range_filter(struct cli_def *cli, const char *string, void *data);
-int cli_count_filter(struct cli_def *cli, const char *string, void *data);
-
static struct cli_filter_cmds filter_cmds[] =
{
- { "begin", "Begin with lines that match" },
- { "between", "Between lines that match" },
- { "count", "Count of lines" },
- { "exclude", "Exclude lines that match" },
- { "include", "Include lines that match" },
- { "grep", "Include lines that match regex (options: -v, -i, -e)" },
- { "egrep", "Include lines that match extended regex" },
+ /* cryptech: removed all filters, was using dynamic memory and seemed an unneccessarily big attack surface */
{ NULL, NULL}
};
-static ssize_t _write(int fd, const void *buf, size_t count)
+static int isspace(int c)
+{
+ /* isspace() not provided with gcc-arm-none-eabi 4.9.3 */
+ return (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v');
+}
+
+static ssize_t _write(struct cli_def *cli, struct cli_loop_ctx *ctx, const void *buf, size_t count)
{
size_t written = 0;
- ssize_t thisTime =0;
+ ssize_t thisTime = 0;
+
+ if (! cli->write_callback && ! ctx->sockfd)
+ return -1;
+
while (count != written)
{
- thisTime = write(fd, (char*)buf + written, count - written);
+ if (cli->write_callback)
+ thisTime = cli->write_callback(cli, (char*)buf + written, count - written);
+ else
+ thisTime = write(ctx->sockfd, (char*)buf + written, count - written);
if (thisTime == -1)
{
if (errno == EINTR)
@@ -159,29 +98,34 @@ static ssize_t _write(int fd, const void *buf, size_t count)
}
return written;
}
+
+/* cryptech: made this function use a static buffer instead of dynamically allocated memory
+ for command "show counters" this function is called on command "counters", so the string
+ is built backwards while there still are parent nodes.
+ */
char *cli_command_name(struct cli_def *cli, struct cli_command *command)
{
- char *name = cli->commandname;
- char *o;
-
- if (name) free(name);
- if (!(name = calloc(1, 1)))
- return NULL;
+ static char buf[CLI_MAX_CMD_NAME_LEN];
+ buf[CLI_MAX_CMD_NAME_LEN - 1] = 0;
+ int idx = CLI_MAX_CMD_NAME_LEN - 1;
while (command)
{
- o = name;
- if (asprintf(&name, "%s%s%s", command->command, *o ? " " : "", o) == -1)
+ /* XXX what to do if there is no room left in buf? */
+ if (idx > (int) strlen(command->command) + 1)
{
- fprintf(stderr, "Couldn't allocate memory for command_name: %s", strerror(errno));
- free(o);
- return NULL;
+ if (buf[idx])
+ {
+ /* this is not the first command, need to add a space */
+ buf[--idx] = ' ';
+ }
+ idx -= strlen(command->command);
+ memcpy(buf + idx, command->command, strlen(command->command));
}
command = command->parent;
- free(o);
}
- cli->commandname = name;
- return name;
+
+ return buf + idx;
}
void cli_set_auth_callback(struct cli_def *cli, int (*auth_callback)(const char *, const char *))
@@ -194,88 +138,24 @@ void cli_set_enable_callback(struct cli_def *cli, int (*enable_callback)(const c
cli->enable_callback = enable_callback;
}
-void cli_allow_user(struct cli_def *cli, const char *username, const char *password)
-{
- struct unp *u, *n;
- if (!(n = malloc(sizeof(struct unp))))
- {
- fprintf(stderr, "Couldn't allocate memory for user: %s", strerror(errno));
- return;
- }
- if (!(n->username = strdup(username)))
- {
- fprintf(stderr, "Couldn't allocate memory for username: %s", strerror(errno));
- free(n);
- return;
- }
- if (!(n->password = strdup(password)))
- {
- fprintf(stderr, "Couldn't allocate memory for password: %s", strerror(errno));
- free(n->username);
- free(n);
- return;
- }
- n->next = NULL;
- if (!cli->users)
- {
- cli->users = n;
- }
- else
- {
- for (u = cli->users; u && u->next; u = u->next);
- if (u) u->next = n;
- }
-}
+/* cryptech: removed unused function: cli_allow_user */
+/* cryptech: removed unused function: cli_allow_enable */
+/* cryptech: removed unused function: cli_deny_user */
-void cli_allow_enable(struct cli_def *cli, const char *password)
+void cli_set_banner(struct cli_def *cli, char *banner)
{
- free_z(cli->enable_password);
- if (!(cli->enable_password = strdup(password)))
- {
- fprintf(stderr, "Couldn't allocate memory for enable password: %s", strerror(errno));
- }
+ cli->banner = banner;
}
-void cli_deny_user(struct cli_def *cli, const char *username)
+void cli_set_hostname(struct cli_def *cli, char *hostname)
{
- struct unp *u, *p = NULL;
- if (!cli->users) return;
- for (u = cli->users; u; u = u->next)
- {
- if (strcmp(username, u->username) == 0)
- {
- if (p)
- p->next = u->next;
- else
- cli->users = u->next;
- free(u->username);
- free(u->password);
- free(u);
- break;
- }
- p = u;
- }
-}
-
-void cli_set_banner(struct cli_def *cli, const char *banner)
-{
- free_z(cli->banner);
- if (banner && *banner)
- cli->banner = strdup(banner);
-}
-
-void cli_set_hostname(struct cli_def *cli, const char *hostname)
-{
- free_z(cli->hostname);
- if (hostname && *hostname)
- cli->hostname = strdup(hostname);
+ cli->hostname = hostname;
}
-void cli_set_promptchar(struct cli_def *cli, const char *promptchar)
+void cli_set_promptchar(struct cli_def *cli, char *promptchar)
{
- free_z(cli->promptchar);
- cli->promptchar = strdup(promptchar);
+ cli->promptchar = promptchar;
}
static int cli_build_shortest(struct cli_def *cli, struct cli_command *commands)
@@ -324,23 +204,23 @@ int cli_set_privilege(struct cli_def *cli, int priv)
if (priv != old)
{
- cli_set_promptchar(cli, priv == PRIVILEGE_PRIVILEGED ? "# " : "> ");
+ static char priv_prompt[] = "# ", nopriv_prompt[] = "> ";
+ cli_set_promptchar(cli, priv == PRIVILEGE_PRIVILEGED ? priv_prompt : nopriv_prompt);
cli_build_shortest(cli, cli->commands);
}
return old;
}
-void cli_set_modestring(struct cli_def *cli, const char *modestring)
+void cli_set_modestring(struct cli_def *cli, char *modestring)
{
- free_z(cli->modestring);
- if (modestring)
- cli->modestring = strdup(modestring);
+ cli->modestring = modestring;
}
int cli_set_configmode(struct cli_def *cli, int mode, const char *config_desc)
{
int old = cli->mode;
+ static char string[64];
cli->mode = mode;
if (mode != old)
@@ -352,13 +232,13 @@ int cli_set_configmode(struct cli_def *cli, int mode, const char *config_desc)
}
else if (config_desc && *config_desc)
{
- char string[64];
snprintf(string, sizeof(string), "(config-%s)", config_desc);
cli_set_modestring(cli, string);
}
else
{
- cli_set_modestring(cli, "(config)");
+ snprintf(string, sizeof(string), "(config)");
+ cli_set_modestring(cli, string);
}
cli_build_shortest(cli, cli->commands);
@@ -367,67 +247,38 @@ int cli_set_configmode(struct cli_def *cli, int mode, const char *config_desc)
return old;
}
-struct cli_command *cli_register_command(struct cli_def *cli, struct cli_command *parent, const char *command, int
- (*callback)(struct cli_def *cli, const char *, char **, int), int privilege,
- int mode, const char *help)
+void cli_register_command2(struct cli_def *cli, struct cli_command *cmd, struct cli_command *parent)
{
- struct cli_command *c, *p;
-
- if (!command) return NULL;
- if (!(c = calloc(sizeof(struct cli_command), 1))) return NULL;
-
- c->callback = callback;
- c->next = NULL;
- if (!(c->command = strdup(command)))
- return NULL;
- c->parent = parent;
- c->privilege = privilege;
- c->mode = mode;
- if (help && !(c->help = strdup(help)))
- return NULL;
+ struct cli_command *p;
if (parent)
{
+ cmd->parent = parent;
if (!parent->children)
{
- parent->children = c;
+ parent->children = cmd;
}
else
{
for (p = parent->children; p && p->next; p = p->next);
- if (p) p->next = c;
+ if (p) p->next = cmd;
}
}
else
{
if (!cli->commands)
{
- cli->commands = c;
+ cli->commands = cmd;
}
else
{
- for (p = cli->commands; p && p->next; p = p->next);
- if (p) p->next = c;
+ for (p = cli->commands; p && p->next; p = p->next);
+ if (p) p->next = cmd;
}
}
- return c;
}
-static void cli_free_command(struct cli_command *cmd)
-{
- struct cli_command *c, *p;
-
- for (c = cmd->children; c;)
- {
- p = c->next;
- cli_free_command(c);
- c = p;
- }
-
- free(cmd->command);
- if (cmd->help) free(cmd->help);
- free(cmd);
-}
+/* cryptech: removed unused function cli_free_command */
int cli_unregister_command(struct cli_def *cli, const char *command)
{
@@ -445,7 +296,6 @@ int cli_unregister_command(struct cli_def *cli, const char *command)
else
cli->commands = c->next;
- cli_free_command(c);
return CLI_OK;
}
p = c;
@@ -487,7 +337,7 @@ int cli_int_enable(struct cli_def *cli, UNUSED(const char *command), UNUSED(char
else
{
/* require password entry */
- cli->state = STATE_ENABLE_PASSWORD;
+ cli->state = CLI_STATE_ENABLE_PASSWORD;
}
return CLI_OK;
@@ -514,7 +364,7 @@ int cli_int_history(struct cli_def *cli, UNUSED(const char *command), UNUSED(cha
cli_error(cli, "\nCommand history:");
for (i = 0; i < MAX_HISTORY; i++)
{
- if (cli->history[i])
+ if (cli->history[i][0])
cli_error(cli, "%3d. %s", i, cli->history[i]);
}
@@ -554,101 +404,60 @@ int cli_int_configure_terminal(struct cli_def *cli, UNUSED(const char *command),
return CLI_OK;
}
-struct cli_def *cli_init()
+int cli_init(struct cli_def *cli)
{
- struct cli_def *cli;
- struct cli_command *c;
+ static struct cli_command cmd_int_help_s = {(char *) "help", cli_int_help, 0,
+ (char *) "Show available commands",
+ PRIVILEGE_UNPRIVILEGED, MODE_ANY, NULL, NULL, NULL};
+ static struct cli_command cmd_int_quit_s = {(char *) "quit", cli_int_quit, 0,
+ (char *) "Disconnect",
+ PRIVILEGE_UNPRIVILEGED, MODE_ANY, NULL, NULL, NULL};
+ static struct cli_command cmd_int_logout_s = {(char *) "logout", cli_int_quit, 0,
+ (char *) "Disconnect",
+ PRIVILEGE_UNPRIVILEGED, MODE_ANY, NULL, NULL, NULL};
+ static struct cli_command cmd_int_exit_s = {(char *) "exit", cli_int_exit, 0,
+ (char *) "Exit from current mode",
+ PRIVILEGE_UNPRIVILEGED, MODE_ANY, NULL, NULL, NULL};
+ static struct cli_command cmd_int_history_s = {(char *) "history", cli_int_history, 0,
+ (char *) "Show a list of previously run commands",
+ PRIVILEGE_UNPRIVILEGED, MODE_ANY, NULL, NULL, NULL};
+ static struct cli_command cmd_int_enable_s = {(char *) "enable", cli_int_enable, 0,
+ (char *) "Turn on privileged commands",
+ PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL, NULL, NULL};
+ static struct cli_command cmd_int_disable_s = {(char *) "disable", cli_int_disable, 0,
+ (char *) "Turn off privileged commands",
+ PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL, NULL, NULL};
+ static struct cli_command cmd_int_configure_s = {(char *) "configure", NULL, 0,
+ (char *) "Enter configuration mode",
+ PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL, NULL, NULL};
+ static struct cli_command cmd_int_configure_terminal_s = {(char *) "terminal", cli_int_configure_terminal, 0,
+ (char *) "Configure from the terminal",
+ PRIVILEGE_PRIVILEGED, MODE_EXEC, NULL, NULL, NULL};
- if (!(cli = calloc(sizeof(struct cli_def), 1)))
- return 0;
-
- cli->buf_size = 1024;
- if (!(cli->buffer = calloc(cli->buf_size, 1)))
- {
- free_z(cli);
- return 0;
- }
cli->telnet_protocol = 1;
- cli_register_command(cli, 0, "help", cli_int_help, PRIVILEGE_UNPRIVILEGED, MODE_ANY, "Show available commands");
- cli_register_command(cli, 0, "quit", cli_int_quit, PRIVILEGE_UNPRIVILEGED, MODE_ANY, "Disconnect");
- cli_register_command(cli, 0, "logout", cli_int_quit, PRIVILEGE_UNPRIVILEGED, MODE_ANY, "Disconnect");
- cli_register_command(cli, 0, "exit", cli_int_exit, PRIVILEGE_UNPRIVILEGED, MODE_ANY, "Exit from current mode");
- cli_register_command(cli, 0, "history", cli_int_history, PRIVILEGE_UNPRIVILEGED, MODE_ANY,
- "Show a list of previously run commands");
- cli_register_command(cli, 0, "enable", cli_int_enable, PRIVILEGE_UNPRIVILEGED, MODE_EXEC,
- "Turn on privileged commands");
- cli_register_command(cli, 0, "disable", cli_int_disable, PRIVILEGE_PRIVILEGED, MODE_EXEC,
- "Turn off privileged commands");
-
- c = cli_register_command(cli, 0, "configure", 0, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Enter configuration mode");
- cli_register_command(cli, c, "terminal", cli_int_configure_terminal, PRIVILEGE_PRIVILEGED, MODE_EXEC,
- "Configure from the terminal");
+ cli_register_command2(cli, &cmd_int_help_s, NULL);
+ cli_register_command2(cli, &cmd_int_quit_s, NULL);
+ cli_register_command2(cli, &cmd_int_logout_s, NULL);
+ cli_register_command2(cli, &cmd_int_exit_s, NULL);
+ cli_register_command2(cli, &cmd_int_history_s, NULL);
+ cli_register_command2(cli, &cmd_int_enable_s, NULL);
+ cli_register_command2(cli, &cmd_int_disable_s, NULL);
+
+ cli_register_command2(cli, &cmd_int_configure_s, NULL);
+ cli_register_command2(cli, &cmd_int_configure_terminal_s, &cmd_int_configure_s);
cli->privilege = cli->mode = -1;
cli_set_privilege(cli, PRIVILEGE_UNPRIVILEGED);
cli_set_configmode(cli, MODE_EXEC, 0);
- // Default to 1 second timeout intervals
- cli->timeout_tm.tv_sec = 1;
- cli->timeout_tm.tv_usec = 0;
-
- // Set default idle timeout callback, but no timeout
- cli_set_idle_timeout_callback(cli, 0, cli_int_idle_timeout);
- return cli;
+ return 1;
}
-void cli_unregister_all(struct cli_def *cli, struct cli_command *command)
-{
- struct cli_command *c, *p = NULL;
-
- if (!command) command = cli->commands;
- if (!command) return;
-
- for (c = command; c; )
- {
- p = c->next;
-
- // Unregister all child commands
- if (c->children)
- cli_unregister_all(cli, c->children);
-
- if (c->command) free(c->command);
- if (c->help) free(c->help);
- free(c);
-
- c = p;
- }
-}
+/* cryptech: removed unused function cli_unregister_all */
int cli_done(struct cli_def *cli)
{
- struct unp *u = cli->users, *n;
-
- if (!cli) return CLI_OK;
- cli_free_history(cli);
-
- // Free all users
- while (u)
- {
- if (u->username) free(u->username);
- if (u->password) free(u->password);
- n = u->next;
- free(u);
- u = n;
- }
-
- /* free all commands */
- cli_unregister_all(cli, 0);
-
- free_z(cli->commandname);
- free_z(cli->modestring);
- free_z(cli->banner);
- free_z(cli->promptchar);
- free_z(cli->hostname);
- free_z(cli->buffer);
- free_z(cli);
-
return CLI_OK;
}
@@ -657,32 +466,25 @@ static int cli_add_history(struct cli_def *cli, const char *cmd)
int i;
for (i = 0; i < MAX_HISTORY; i++)
{
- if (!cli->history[i])
+ if (!cli->history[i][0])
{
if (i == 0 || strcasecmp(cli->history[i-1], cmd))
- if (!(cli->history[i] = strdup(cmd)))
- return CLI_ERROR;
+ {
+ strncpy(cli->history[i], cmd, HISTORY_CMD_LEN - 1);
+ cli->history[i][HISTORY_CMD_LEN - 1] = 0;
+ }
return CLI_OK;
}
}
// No space found, drop one off the beginning of the list
- free(cli->history[0]);
for (i = 0; i < MAX_HISTORY-1; i++)
- cli->history[i] = cli->history[i+1];
- if (!(cli->history[MAX_HISTORY - 1] = strdup(cmd)))
- return CLI_ERROR;
+ memcpy(cli->history[i], cli->history[i+1], HISTORY_CMD_LEN);
+ strncpy(cli->history[MAX_HISTORY-1], cmd, HISTORY_CMD_LEN - 1);
+ cli->history[MAX_HISTORY-1][HISTORY_CMD_LEN - 1] = 0;
return CLI_OK;
}
-void cli_free_history(struct cli_def *cli)
-{
- int i;
- for (i = 0; i < MAX_HISTORY; i++)
- {
- if (cli->history[i])
- free_z(cli->history[i]);
- }
-}
+/* cryptech: removed unused cli_free_history */
static int cli_parse_line(const char *line, char *words[], int max_words)
{
@@ -690,10 +492,12 @@ static int cli_parse_line(const char *line, char *words[], int max_words)
const char *p = line;
const char *word_start = 0;
int inquote = 0;
+ static char buf[CLI_MAX_LINE_LENGTH];
+ char *ptr = buf;
while (*p)
{
- if (!isspace(*p))
+ if (!isspace((unsigned char) *p))
{
word_start = p;
break;
@@ -701,16 +505,19 @@ static int cli_parse_line(const char *line, char *words[], int max_words)
p++;
}
+ memset(buf, 0, sizeof(buf));
+
while (nwords < max_words - 1)
{
- if (!*p || *p == inquote || (word_start && !inquote && (isspace(*p) || *p == '|')))
+ if (!*p || *p == inquote || (word_start && !inquote && (isspace((unsigned char) *p) || *p == '|')))
{
if (word_start)
{
int len = p - word_start;
- memcpy(words[nwords] = malloc(len + 1), word_start, len);
- words[nwords++][len] = 0;
+ memcpy(ptr, word_start, len);
+ words[nwords++] = ptr;
+ ptr += len + 1;
}
if (!*p)
@@ -736,7 +543,7 @@ static int cli_parse_line(const char *line, char *words[], int max_words)
if (!(words[nwords++] = strdup("|")))
return 0;
}
- else if (!isspace(*p))
+ else if (!isspace((unsigned char) *p))
word_start = p;
}
@@ -747,33 +554,7 @@ static int cli_parse_line(const char *line, char *words[], int max_words)
return nwords;
}
-static char *join_words(int argc, char **argv)
-{
- char *p;
- int len = 0;
- int i;
-
- for (i = 0; i < argc; i++)
- {
- if (i)
- len += 1;
-
- len += strlen(argv[i]);
- }
-
- p = malloc(len + 1);
- p[0] = 0;
-
- for (i = 0; i < argc; i++)
- {
- if (i)
- strcat(p, " ");
-
- strcat(p, argv[i]);
- }
-
- return p;
-}
+/* cryptech: removed unused function join_words */
static int cli_find_command(struct cli_def *cli, struct cli_command *commands, int num_words, char *words[],
int start_word, int filters[])
@@ -823,8 +604,6 @@ static int cli_find_command(struct cli_def *cli, struct cli_command *commands, i
if (c->mode == cli->mode || (c->mode == MODE_ANY && again_any != NULL))
{
int rc = CLI_OK;
- int f;
- struct cli_filter **filt = &cli->filters;
// Found a word!
if (!c->children)
@@ -863,95 +642,20 @@ static int cli_find_command(struct cli_def *cli, struct cli_command *commands, i
return rc;
}
+ CORRECT_CHECKS:
+
if (!c->callback)
{
cli_error(cli, "Internal server error processing \"%s\"", cli_command_name(cli, c));
return CLI_ERROR;
}
- CORRECT_CHECKS:
- for (f = 0; rc == CLI_OK && filters[f]; f++)
- {
- int n = num_words;
- char **argv;
- int argc;
- int len;
-
- if (filters[f+1])
- n = filters[f+1];
-
- if (filters[f] == n - 1)
- {
- cli_error(cli, "Missing filter");
- return CLI_ERROR;
- }
-
- argv = words + filters[f] + 1;
- argc = n - (filters[f] + 1);
- len = strlen(argv[0]);
- if (argv[argc - 1][strlen(argv[argc - 1]) - 1] == '?')
- {
- if (argc == 1)
- {
- int i;
- for (i = 0; filter_cmds[i].cmd; i++)
- cli_error(cli, " %-20s %s", filter_cmds[i].cmd, filter_cmds[i].help );
- }
- else
- {
- if (argv[0][0] != 'c') // count
- cli_error(cli, " WORD");
-
- if (argc > 2 || argv[0][0] == 'c') // count
- cli_error(cli, " <cr>");
- }
-
- return CLI_OK;
- }
-
- if (argv[0][0] == 'b' && len < 3) // [beg]in, [bet]ween
- {
- cli_error(cli, "Ambiguous filter \"%s\" (begin, between)", argv[0]);
- return CLI_ERROR;
- }
- *filt = calloc(sizeof(struct cli_filter), 1);
-
- if (!strncmp("include", argv[0], len) || !strncmp("exclude", argv[0], len) ||
- !strncmp("grep", argv[0], len) || !strncmp("egrep", argv[0], len))
- rc = cli_match_filter_init(cli, argc, argv, *filt);
- else if (!strncmp("begin", argv[0], len) || !strncmp("between", argv[0], len))
- rc = cli_range_filter_init(cli, argc, argv, *filt);
- else if (!strncmp("count", argv[0], len))
- rc = cli_count_filter_init(cli, argc, argv, *filt);
- else
- {
- cli_error(cli, "Invalid filter \"%s\"", argv[0]);
- rc = CLI_ERROR;
- }
-
- if (rc == CLI_OK)
- {
- filt = &(*filt)->next;
- }
- else
- {
- free(*filt);
- *filt = 0;
- }
- }
+ /* cryptech: removed filter checking here */
if (rc == CLI_OK)
rc = c->callback(cli, cli_command_name(cli, c), words + start_word + 1, c_words - start_word - 1);
- while (cli->filters)
- {
- struct cli_filter *filt = cli->filters;
-
- // call one last time to clean up
- filt->filter(cli, NULL, filt->data);
- cli->filters = filt->next;
- free(filt);
- }
+ /* cryptech: removed filter cleanup here */
return rc;
}
@@ -996,8 +700,9 @@ int cli_run_command(struct cli_def *cli, const char *command)
int filters[CLI_MAX_LINE_WORDS] = {0};
if (!command) return CLI_ERROR;
- while (isspace(*command))
+ while (isspace((int) *command)) {
command++;
+ }
if (!*command) return CLI_OK;
@@ -1015,9 +720,6 @@ int cli_run_command(struct cli_def *cli, const char *command)
else
r = CLI_ERROR;
- for (i = 0; i < num_words; i++)
- free(words[i]);
-
if (r == CLI_QUIT)
return r;
@@ -1028,15 +730,15 @@ static int cli_get_completions(struct cli_def *cli, const char *command, char **
{
struct cli_command *c;
struct cli_command *n;
- int num_words, save_words, i, k=0;
+ int num_words, i, k=0;
char *words[CLI_MAX_LINE_WORDS] = {0};
int filter = 0;
if (!command) return 0;
- while (isspace(*command))
+ while (isspace((unsigned char) *command))
command++;
- save_words = num_words = cli_parse_line(command, words, sizeof(words)/sizeof(words[0]));
+ num_words = cli_parse_line(command, words, sizeof(words)/sizeof(words[0]));
if (!command[0] || command[strlen(command)-1] == ' ')
num_words++;
@@ -1096,19 +798,17 @@ static int cli_get_completions(struct cli_def *cli, const char *command, char **
}
out:
- for (i = 0; i < save_words; i++)
- free(words[i]);
return k;
}
-static void cli_clear_line(int sockfd, char *cmd, int l, int cursor)
+static void cli_clear_line(struct cli_def *cli, struct cli_loop_ctx *ctx, char *cmd, int l, int cursor)
{
int i;
if (cursor < l)
{
for (i = 0; i < (l - cursor); i++)
- _write(sockfd, " ", 1);
+ _write(cli, ctx, " ", 1);
}
for (i = 0; i < l; i++)
cmd[i] = '\b';
@@ -1116,7 +816,7 @@ static void cli_clear_line(int sockfd, char *cmd, int l, int cursor)
cmd[i] = ' ';
for (; i < l * 3; i++)
cmd[i] = '\b';
- _write(sockfd, cmd, i);
+ _write(cli, ctx, cmd, i);
memset((char *)cmd, 0, i);
l = cursor = 0;
}
@@ -1127,66 +827,40 @@ void cli_reprompt(struct cli_def *cli)
cli->showprompt = 1;
}
-void cli_regular(struct cli_def *cli, int (*callback)(struct cli_def *cli))
-{
- if (!cli) return;
- cli->regular_callback = callback;
-}
-
-void cli_regular_interval(struct cli_def *cli, int seconds)
-{
- if (seconds < 1) seconds = 1;
- cli->timeout_tm.tv_sec = seconds;
- cli->timeout_tm.tv_usec = 0;
-}
-
-#define DES_PREFIX "{crypt}" /* to distinguish clear text from DES crypted */
-#define MD5_PREFIX "$1$"
-
static int pass_matches(const char *pass, const char *try)
{
- int des;
- if ((des = !strncasecmp(pass, DES_PREFIX, sizeof(DES_PREFIX)-1)))
- pass += sizeof(DES_PREFIX)-1;
-
-#ifndef WIN32
- /*
- * TODO - find a small crypt(3) function for use on windows
- */
- if (des || !strncmp(pass, MD5_PREFIX, sizeof(MD5_PREFIX)-1))
- try = crypt(try, pass);
-#endif
-
+ /* cryptech: removed DES mode here */
return !strcmp(pass, try);
}
#define CTRL(c) (c - '@')
-static int show_prompt(struct cli_def *cli, int sockfd)
+static int show_prompt(struct cli_def *cli, struct cli_loop_ctx *ctx)
{
int len = 0;
if (cli->hostname)
- len += write(sockfd, cli->hostname, strlen(cli->hostname));
+ len += _write(cli, ctx, cli->hostname, strlen(cli->hostname));
if (cli->modestring)
- len += write(sockfd, cli->modestring, strlen(cli->modestring));
+ len += _write(cli, ctx, cli->modestring, strlen(cli->modestring));
- return len + write(sockfd, cli->promptchar, strlen(cli->promptchar));
+ return len + _write(cli, ctx, cli->promptchar, strlen(cli->promptchar));
}
int cli_loop(struct cli_def *cli, int sockfd)
{
unsigned char c;
- int n, l, oldl = 0, is_telnet_option = 0, skip = 0, esc = 0;
- int cursor = 0, insertmode = 1;
- char *cmd = NULL, *oldcmd = 0;
- char *username = NULL, *password = NULL;
+ int n = 0;
+ struct cli_loop_ctx ctx;
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.insertmode = 1;
+ ctx.sockfd = sockfd;
cli_build_shortest(cli, cli->commands);
- cli->state = STATE_LOGIN;
+ cli->state = CLI_STATE_LOGIN;
- cli_free_history(cli);
if (cli->telnet_protocol)
{
static const char *negotiate =
@@ -1194,12 +868,10 @@ int cli_loop(struct cli_def *cli, int sockfd)
"\xFF\xFB\x01"
"\xFF\xFD\x03"
"\xFF\xFD\x01";
- _write(sockfd, negotiate, strlen(negotiate));
+ _write(cli, &ctx, negotiate, strlen(negotiate));
}
- if ((cmd = malloc(CLI_MAX_LINE_LENGTH)) == NULL)
- return CLI_ERROR;
-
+#ifndef CRYPTECH_NO_FDOPEN
#ifdef WIN32
/*
* OMG, HACK
@@ -1211,774 +883,783 @@ int cli_loop(struct cli_def *cli, int sockfd)
if (!(cli->client = fdopen(sockfd, "w+")))
return CLI_ERROR;
#endif
+#endif /* CRYPTECH_NO_FDOPEN */
setbuf(cli->client, NULL);
if (cli->banner)
cli_error(cli, "%s", cli->banner);
- // Set the last action now so we don't time immediately
- if (cli->idle_timeout)
- time(&cli->last_action);
-
/* start off in unprivileged mode */
cli_set_privilege(cli, PRIVILEGE_UNPRIVILEGED);
cli_set_configmode(cli, MODE_EXEC, NULL);
/* no auth required? */
if (!cli->users && !cli->auth_callback)
- cli->state = STATE_NORMAL;
+ cli->state = CLI_STATE_NORMAL;
while (1)
{
- signed int in_history = 0;
- int lastchar = 0;
- struct timeval tm;
-
- cli->showprompt = 1;
-
- if (oldcmd)
- {
- l = cursor = oldl;
- oldcmd[l] = 0;
- cli->showprompt = 1;
- oldcmd = NULL;
- oldl = 0;
- }
- else
- {
- memset(cmd, 0, CLI_MAX_LINE_LENGTH);
- l = 0;
- cursor = 0;
- }
-
- memcpy(&tm, &cli->timeout_tm, sizeof(tm));
+ cli_loop_start_new_command(cli, &ctx);
while (1)
{
- int sr;
- fd_set r;
- if (cli->showprompt)
- {
- if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
- _write(sockfd, "\r\n", 2);
-
- switch (cli->state)
- {
- case STATE_LOGIN:
- _write(sockfd, "Username: ", strlen("Username: "));
- break;
-
- case STATE_PASSWORD:
- _write(sockfd, "Password: ", strlen("Password: "));
- break;
-
- case STATE_NORMAL:
- case STATE_ENABLE:
- show_prompt(cli, sockfd);
- _write(sockfd, cmd, l);
- if (cursor < l)
- {
- int n = l - cursor;
- while (n--)
- _write(sockfd, "\b", 1);
- }
- break;
-
- case STATE_ENABLE_PASSWORD:
- _write(sockfd, "Password: ", strlen("Password: "));
- break;
-
- }
-
- cli->showprompt = 0;
- }
-
- FD_ZERO(&r);
- FD_SET(sockfd, &r);
-
- if ((sr = select(sockfd + 1, &r, NULL, NULL, &tm)) < 0)
- {
- /* select error */
- if (errno == EINTR)
- continue;
-
- perror("select");
- l = -1;
- break;
- }
-
- if (sr == 0)
- {
- /* timeout every second */
- if (cli->regular_callback && cli->regular_callback(cli) != CLI_OK)
- {
- l = -1;
- break;
- }
-
- if (cli->idle_timeout)
- {
- if (time(NULL) - cli->last_action >= cli->idle_timeout)
- {
- if (cli->idle_timeout_callback)
- {
- // Call the callback and continue on if successful
- if (cli->idle_timeout_callback(cli) == CLI_OK)
- {
- // Reset the idle timeout counter
- time(&cli->last_action);
- continue;
- }
- }
- // Otherwise, break out of the main loop
- l = -1;
- break;
- }
- }
-
- memcpy(&tm, &cli->timeout_tm, sizeof(tm));
- continue;
- }
-
- if ((n = read(sockfd, &c, 1)) < 0)
- {
- if (errno == EINTR)
- continue;
-
- perror("read");
- l = -1;
- break;
- }
-
- if (cli->idle_timeout)
- time(&cli->last_action);
-
- if (n == 0)
- {
- l = -1;
- break;
- }
-
- if (skip)
- {
- skip--;
- continue;
- }
-
- if (c == 255 && !is_telnet_option)
- {
- is_telnet_option++;
- continue;
- }
-
- if (is_telnet_option)
- {
- if (c >= 251 && c <= 254)
- {
- is_telnet_option = c;
- continue;
- }
-
- if (c != 255)
- {
- is_telnet_option = 0;
- continue;
- }
-
- is_telnet_option = 0;
- }
-
- /* handle ANSI arrows */
- if (esc)
- {
- if (esc == '[')
- {
- /* remap to readline control codes */
- switch (c)
- {
- case 'A': /* Up */
- c = CTRL('P');
- break;
-
- case 'B': /* Down */
- c = CTRL('N');
- break;
-
- case 'C': /* Right */
- c = CTRL('F');
- break;
-
- case 'D': /* Left */
- c = CTRL('B');
- break;
-
- default:
- c = 0;
- }
-
- esc = 0;
- }
- else
- {
- esc = (c == '[') ? c : 0;
- continue;
- }
- }
-
- if (c == 0) continue;
- if (c == '\n') continue;
-
- if (c == '\r')
- {
- if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
- _write(sockfd, "\r\n", 2);
- break;
- }
-
- if (c == 27)
- {
- esc = 1;
- continue;
- }
-
- if (c == CTRL('C'))
- {
- _write(sockfd, "\a", 1);
- continue;
- }
-
- /* back word, backspace/delete */
- if (c == CTRL('W') || c == CTRL('H') || c == 0x7f)
- {
- int back = 0;
-
- if (c == CTRL('W')) /* word */
- {
- int nc = cursor;
-
- if (l == 0 || cursor == 0)
- continue;
-
- while (nc && cmd[nc - 1] == ' ')
- {
- nc--;
- back++;
- }
-
- while (nc && cmd[nc - 1] != ' ')
- {
- nc--;
- back++;
- }
- }
- else /* char */
- {
- if (l == 0 || cursor == 0)
- {
- _write(sockfd, "\a", 1);
- continue;
- }
-
- back = 1;
- }
-
- if (back)
- {
- while (back--)
- {
- if (l == cursor)
- {
- cmd[--cursor] = 0;
- if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
- _write(sockfd, "\b \b", 3);
- }
- else
- {
- int i;
- cursor--;
- if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
- {
- for (i = cursor; i <= l; i++) cmd[i] = cmd[i+1];
- _write(sockfd, "\b", 1);
- _write(sockfd, cmd + cursor, strlen(cmd + cursor));
- _write(sockfd, " ", 1);
- for (i = 0; i <= (int)strlen(cmd + cursor); i++)
- _write(sockfd, "\b", 1);
- }
- }
- l--;
- }
-
- continue;
- }
- }
-
- /* redraw */
- if (c == CTRL('L'))
- {
- int i;
- int cursorback = l - cursor;
-
- if (cli->state == STATE_PASSWORD || cli->state == STATE_ENABLE_PASSWORD)
- continue;
+ cli_loop_show_prompt(cli, &ctx);
+
+ n = cli_loop_read_next_char(cli, &ctx, &c);
+ if (n == CLI_LOOP_CTRL_BREAK)
+ break;
+ if (n == CLI_LOOP_CTRL_CONTINUE)
+ continue;
+
+ n = cli_loop_process_char(cli, &ctx, c);
+ if (n == CLI_LOOP_CTRL_BREAK)
+ break;
+ if (n == CLI_LOOP_CTRL_CONTINUE)
+ continue;
+ }
- _write(sockfd, "\r\n", 2);
- show_prompt(cli, sockfd);
- _write(sockfd, cmd, l);
- for (i = 0; i < cursorback; i++)
- _write(sockfd, "\b", 1);
+ if (ctx.l < 0) break;
- continue;
- }
-
- /* clear line */
- if (c == CTRL('U'))
- {
- if (cli->state == STATE_PASSWORD || cli->state == STATE_ENABLE_PASSWORD)
- memset(cmd, 0, l);
- else
- cli_clear_line(sockfd, cmd, l, cursor);
+ n = cli_loop_process_cmd(cli, &ctx);
+ if (n == CLI_LOOP_CTRL_BREAK)
+ break;
+ }
- l = cursor = 0;
- continue;
- }
+ fclose(cli->client);
+ cli->client = 0;
+ return CLI_OK;
+}
- /* kill to EOL */
- if (c == CTRL('K'))
- {
- if (cursor == l)
- continue;
+void cli_loop_start_new_command(struct cli_def *cli, struct cli_loop_ctx *ctx)
+{
+ ctx->in_history = 0;
+ ctx->lastchar = 0;
- if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
- {
- int c;
- for (c = cursor; c < l; c++)
- _write(sockfd, " ", 1);
+ cli->showprompt = 1;
- for (c = cursor; c < l; c++)
- _write(sockfd, "\b", 1);
- }
+ if (ctx->restore_cmd_l > 0)
+ {
+ ctx->l = ctx->cursor = ctx->restore_cmd_l;
+ ctx->cmd[ctx->l] = 0;
+ ctx->restore_cmd_l = 0;
+ }
+ else
+ {
+ memset(ctx->cmd, 0, CLI_MAX_LINE_LENGTH);
+ ctx->l = 0;
+ ctx->cursor = 0;
+ }
- memset(cmd + cursor, 0, l - cursor);
- l = cursor;
- continue;
- }
+}
- /* EOT */
- if (c == CTRL('D'))
- {
- if (cli->state == STATE_PASSWORD || cli->state == STATE_ENABLE_PASSWORD)
- break;
+int cli_loop_read_next_char(struct cli_def *cli, struct cli_loop_ctx *ctx, unsigned char *c)
+{
+ int n;
+#ifndef CRYPTECH_NO_SELECT
+ struct timeval tm;
+ int sr;
+ fd_set r;
- if (l)
- continue;
+ FD_ZERO(&r);
+ FD_SET(ctx->sockfd, &r);
+ memcpy(&tm, &cli->timeout_tm, sizeof(tm));
- l = -1;
- break;
- }
+ if ((sr = select(ctx->sockfd + 1, &r, NULL, NULL, &tm)) < 0)
+ {
+ /* select error */
+ if (errno == EINTR)
+ return CLI_LOOP_CTRL_CONTINUE;
- /* disable */
- if (c == CTRL('Z'))
- {
- if (cli->mode != MODE_EXEC)
- {
- cli_clear_line(sockfd, cmd, l, cursor);
- cli_set_configmode(cli, MODE_EXEC, NULL);
- cli->showprompt = 1;
- }
+ perror("select");
+ ctx->l = -1;
+ return CLI_LOOP_CTRL_BREAK;
+ }
- continue;
- }
+ if (sr == 0)
+ {
+ /* timeout every second */
+ if (cli->regular_callback && cli->regular_callback(cli) != CLI_OK)
+ {
+ ctx->l = -1;
+ return CLI_LOOP_CTRL_BREAK;
+ }
+
+ if (cli->idle_timeout)
+ {
+ if (time(NULL) - cli->last_action >= cli->idle_timeout)
+ {
+ if (cli->idle_timeout_callback)
+ {
+ // Call the callback and continue on if successful
+ if (cli->idle_timeout_callback(cli) == CLI_OK)
+ {
+ // Reset the idle timeout counter
+ time(&cli->last_action);
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
+ }
+ // Otherwise, break out of the main loop
+ ctx->l = -1;
+ return CLI_LOOP_CTRL_BREAK;
+ }
+ }
+
+ memcpy(&tm, &cli->timeout_tm, sizeof(tm));
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
+#endif /* CRYPTECH_NO_SELECT */
- /* TAB completion */
- if (c == CTRL('I'))
- {
- char *completions[CLI_MAX_LINE_WORDS];
- int num_completions = 0;
+ if (cli->read_callback)
+ {
+ if ((n = cli->read_callback(cli, c, (size_t) 1)) < 0)
+ {
+ perror("read_callback");
+ ctx->l = -1;
+ return CLI_LOOP_CTRL_BREAK;
+ }
+ }
+ else
+ {
+ if ((n = read(ctx->sockfd, c, 1)) < 0)
+ {
+ if (errno == EINTR)
+ return CLI_LOOP_CTRL_CONTINUE;
+
+ perror("read");
+ ctx->l = -1;
+ return CLI_LOOP_CTRL_BREAK;
+ }
+ }
- if (cli->state == STATE_LOGIN || cli->state == STATE_PASSWORD || cli->state == STATE_ENABLE_PASSWORD)
- continue;
+ if (n == 0)
+ {
+ ctx->l = -1;
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
- if (cursor != l) continue;
+ return 0;
+}
- num_completions = cli_get_completions(cli, cmd, completions, CLI_MAX_LINE_WORDS);
- if (num_completions == 0)
- {
- _write(sockfd, "\a", 1);
- }
- else if (num_completions == 1)
- {
- // Single completion
- for (; l > 0; l--, cursor--)
- {
- if (cmd[l-1] == ' ' || cmd[l-1] == '|')
- break;
- _write(sockfd, "\b", 1);
- }
- strcpy((cmd + l), completions[0]);
- l += strlen(completions[0]);
- cmd[l++] = ' ';
- cursor = l;
- _write(sockfd, completions[0], strlen(completions[0]));
- _write(sockfd, " ", 1);
- }
- else if (lastchar == CTRL('I'))
- {
- // double tab
- int i;
- _write(sockfd, "\r\n", 2);
- for (i = 0; i < num_completions; i++)
- {
- _write(sockfd, completions[i], strlen(completions[i]));
- if (i % 4 == 3)
- _write(sockfd, "\r\n", 2);
- else
- _write(sockfd, " ", 1);
- }
- if (i % 4 != 3) _write(sockfd, "\r\n", 2);
- cli->showprompt = 1;
- }
- else
- {
- // More than one completion
- lastchar = c;
- _write(sockfd, "\a", 1);
- }
- continue;
- }
- /* history */
- if (c == CTRL('P') || c == CTRL('N'))
- {
- int history_found = 0;
+void cli_loop_show_prompt(struct cli_def *cli, struct cli_loop_ctx *ctx)
+{
+ if (cli->showprompt)
+ {
+ if (cli->state != CLI_STATE_PASSWORD && cli->state != CLI_STATE_ENABLE_PASSWORD)
+ _write(cli, ctx, "\r\n", 2);
+
+ switch (cli->state)
+ {
+ case CLI_STATE_LOGIN:
+ _write(cli, ctx, "Username: ", strlen("Username: "));
+ break;
+
+ case CLI_STATE_PASSWORD:
+ _write(cli, ctx, "Password: ", strlen("Password: "));
+ break;
+
+ case CLI_STATE_NORMAL:
+ case CLI_STATE_ENABLE:
+ show_prompt(cli, ctx);
+ _write(cli, ctx, ctx->cmd, ctx->l);
+ if (ctx->cursor < ctx->l)
+ {
+ int n = ctx->l - ctx->cursor;
+ while (n--)
+ _write(cli, ctx, "\b", 1);
+ }
+ break;
+
+ case CLI_STATE_ENABLE_PASSWORD:
+ _write(cli, ctx, "Password: ", strlen("Password: "));
+ break;
+
+ }
+
+ cli->showprompt = 0;
+ }
+}
- if (cli->state == STATE_LOGIN || cli->state == STATE_PASSWORD || cli->state == STATE_ENABLE_PASSWORD)
- continue;
- if (c == CTRL('P')) // Up
- {
- in_history--;
- if (in_history < 0)
- {
- for (in_history = MAX_HISTORY-1; in_history >= 0; in_history--)
- {
- if (cli->history[in_history])
- {
- history_found = 1;
- break;
- }
- }
- }
- else
- {
- if (cli->history[in_history]) history_found = 1;
- }
- }
- else // Down
- {
- in_history++;
- if (in_history >= MAX_HISTORY || !cli->history[in_history])
- {
- int i = 0;
- for (i = 0; i < MAX_HISTORY; i++)
- {
- if (cli->history[i])
- {
- in_history = i;
- history_found = 1;
- break;
- }
- }
- }
- else
- {
- if (cli->history[in_history]) history_found = 1;
- }
- }
- if (history_found && cli->history[in_history])
- {
- // Show history item
- cli_clear_line(sockfd, cmd, l, cursor);
- memset(cmd, 0, CLI_MAX_LINE_LENGTH);
- strncpy(cmd, cli->history[in_history], CLI_MAX_LINE_LENGTH - 1);
- l = cursor = strlen(cmd);
- _write(sockfd, cmd, l);
- }
+/*
+ * This function should be called once for every character received from the user.
+ *
+ * It will return CLI_LOOP_CTRL_BREAK if the command is now ready to be processed (or the
+ * session should be terminated, and CLI_LOOP_CTRL_CONTINUE if it should be called again
+ * for the next character received.
+ */
+int cli_loop_process_char(struct cli_def *cli, struct cli_loop_ctx *ctx, unsigned char c)
+{
+ if (ctx->skip)
+ {
+ ctx->skip--;
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
- continue;
- }
+ if (c == 255 && !ctx->is_telnet_option)
+ {
+ ctx->is_telnet_option++;
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
- /* left/right cursor motion */
- if (c == CTRL('B') || c == CTRL('F'))
- {
- if (c == CTRL('B')) /* Left */
- {
- if (cursor)
- {
- if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
- _write(sockfd, "\b", 1);
+ if (ctx->is_telnet_option)
+ {
+ if (c >= 251 && c <= 254)
+ {
+ ctx->is_telnet_option = c;
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
+
+ if (c != 255)
+ {
+ ctx->is_telnet_option = 0;
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
+
+ ctx->is_telnet_option = 0;
+ }
- cursor--;
- }
- }
- else /* Right */
- {
- if (cursor < l)
- {
- if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
- _write(sockfd, &cmd[cursor], 1);
+ /* handle ANSI arrows */
+ if (ctx->esc)
+ {
+ if (ctx->esc == '[')
+ {
+ /* remap to readline control codes */
+ switch (c)
+ {
+ case 'A': /* Up */
+ c = CTRL('P');
+ break;
+
+ case 'B': /* Down */
+ c = CTRL('N');
+ break;
+
+ case 'C': /* Right */
+ c = CTRL('F');
+ break;
+
+ case 'D': /* Left */
+ c = CTRL('B');
+ break;
+
+ default:
+ c = 0;
+ }
+
+ ctx->esc = 0;
+ }
+ else
+ {
+ ctx->esc = (c == '[') ? c : 0;
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
+ }
- cursor++;
- }
- }
+ if (c == 0) return CLI_LOOP_CTRL_CONTINUE;
+ if (c == '\n') return CLI_LOOP_CTRL_CONTINUE;
- continue;
- }
+ if (c == '\r')
+ {
+ if (cli->state != CLI_STATE_PASSWORD && cli->state != CLI_STATE_ENABLE_PASSWORD)
+ _write(cli, ctx, "\r\n", 2);
+ return CLI_LOOP_CTRL_BREAK;
+ }
- /* start of line */
- if (c == CTRL('A'))
- {
- if (cursor)
- {
- if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
- {
- _write(sockfd, "\r", 1);
- show_prompt(cli, sockfd);
- }
+ if (c == 27)
+ {
+ ctx->esc = 1;
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
- cursor = 0;
- }
+ if (c == CTRL('C'))
+ {
+ _write(cli, ctx, "\a", 1);
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
- continue;
- }
+ /* back word, backspace/delete */
+ if (c == CTRL('W') || c == CTRL('H') || c == 0x7f)
+ {
+ int back = 0;
+
+ if (c == CTRL('W')) /* word */
+ {
+ int nc = ctx->cursor;
+
+ if (ctx->l == 0 || ctx->cursor == 0)
+ return CLI_LOOP_CTRL_CONTINUE;
+
+ while (nc && ctx->cmd[nc - 1] == ' ')
+ {
+ nc--;
+ back++;
+ }
+
+ while (nc && ctx->cmd[nc - 1] != ' ')
+ {
+ nc--;
+ back++;
+ }
+ }
+ else /* char */
+ {
+ if (ctx->l == 0 || ctx->cursor == 0)
+ {
+ _write(cli, ctx, "\a", 1);
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
+
+ back = 1;
+ }
+
+ if (back)
+ {
+ while (back--)
+ {
+ if (ctx->l == ctx->cursor)
+ {
+ ctx->cmd[--ctx->cursor] = 0;
+ if (cli->state != CLI_STATE_PASSWORD && cli->state != CLI_STATE_ENABLE_PASSWORD)
+ _write(cli, ctx, "\b \b", 3);
+ }
+ else
+ {
+ int i;
+ ctx->cursor--;
+ if (cli->state != CLI_STATE_PASSWORD && cli->state != CLI_STATE_ENABLE_PASSWORD)
+ {
+ for (i = ctx->cursor; i <= ctx->l; i++) ctx->cmd[i] = ctx->cmd[i+1];
+ _write(cli, ctx, "\b", 1);
+ _write(cli, ctx, ctx->cmd + ctx->cursor, strlen(ctx->cmd + ctx->cursor));
+ _write(cli, ctx, " ", 1);
+ for (i = 0; i <= (int)strlen(ctx->cmd + ctx->cursor); i++)
+ _write(cli, ctx, "\b", 1);
+ }
+ }
+ ctx->l--;
+ }
+
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
+ }
- /* end of line */
- if (c == CTRL('E'))
- {
- if (cursor < l)
- {
- if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
- _write(sockfd, &cmd[cursor], l - cursor);
+ /* redraw */
+ if (c == CTRL('L'))
+ {
+ int i;
+ int cursorback = ctx->l - ctx->cursor;
- cursor = l;
- }
+ if (cli->state == CLI_STATE_PASSWORD || cli->state == CLI_STATE_ENABLE_PASSWORD)
+ return CLI_LOOP_CTRL_CONTINUE;
- continue;
- }
+ _write(cli, ctx, "\r\n", 2);
+ show_prompt(cli, ctx);
+ _write(cli, ctx, ctx->cmd, ctx->l);
- /* normal character typed */
- if (cursor == l)
- {
- /* append to end of line */
- cmd[cursor] = c;
- if (l < CLI_MAX_LINE_LENGTH - 1)
- {
- l++;
- cursor++;
- }
- else
- {
- _write(sockfd, "\a", 1);
- continue;
- }
- }
- else
- {
- // Middle of text
- if (insertmode)
- {
- int i;
- // Move everything one character to the right
- if (l >= CLI_MAX_LINE_LENGTH - 2) l--;
- for (i = l; i >= cursor; i--)
- cmd[i + 1] = cmd[i];
- // Write what we've just added
- cmd[cursor] = c;
-
- _write(sockfd, &cmd[cursor], l - cursor + 1);
- for (i = 0; i < (l - cursor + 1); i++)
- _write(sockfd, "\b", 1);
- l++;
- }
- else
- {
- cmd[cursor] = c;
- }
- cursor++;
- }
+ for (i = 0; i < cursorback; i++)
+ _write(cli, ctx, "\b", 1);
- if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
- {
- if (c == '?' && cursor == l)
- {
- _write(sockfd, "\r\n", 2);
- oldcmd = cmd;
- oldl = cursor = l - 1;
- break;
- }
- _write(sockfd, &c, 1);
- }
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
- oldcmd = 0;
- oldl = 0;
- lastchar = c;
- }
+ /* clear line */
+ if (c == CTRL('U'))
+ {
+ if (cli->state == CLI_STATE_PASSWORD || cli->state == CLI_STATE_ENABLE_PASSWORD)
+ memset(ctx->cmd, 0, ctx->l);
+ else
+ cli_clear_line(cli, ctx, ctx->cmd, ctx->l, ctx->cursor);
- if (l < 0) break;
+ ctx->l = ctx->cursor = 0;
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
- if (cli->state == STATE_LOGIN)
- {
- if (l == 0) continue;
-
- /* require login */
- free_z(username);
- if (!(username = strdup(cmd)))
- return 0;
- cli->state = STATE_PASSWORD;
- cli->showprompt = 1;
- }
- else if (cli->state == STATE_PASSWORD)
- {
- /* require password */
- int allowed = 0;
+ /* kill to EOL */
+ if (c == CTRL('K'))
+ {
+ if (ctx->cursor == ctx->l)
+ return CLI_LOOP_CTRL_CONTINUE;
+
+ if (cli->state != CLI_STATE_PASSWORD && cli->state != CLI_STATE_ENABLE_PASSWORD)
+ {
+ int c;
+ for (c = ctx->cursor; c < ctx->l; c++)
+ _write(cli, ctx, " ", 1);
+
+ for (c = ctx->cursor; c < ctx->l; c++)
+ _write(cli, ctx, "\b", 1);
+ }
+
+ memset(ctx->cmd + ctx->cursor, 0, ctx->l - ctx->cursor);
+ ctx->l = ctx->cursor;
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
- free_z(password);
- if (!(password = strdup(cmd)))
- return 0;
- if (cli->auth_callback)
- {
- if (cli->auth_callback(username, password) == CLI_OK)
- allowed++;
- }
+ /* EOT */
+ if (c == CTRL('D'))
+ {
+ if (cli->state == CLI_STATE_PASSWORD || cli->state == CLI_STATE_ENABLE_PASSWORD)
+ return CLI_LOOP_CTRL_BREAK;
- if (!allowed)
- {
- struct unp *u;
- for (u = cli->users; u; u = u->next)
- {
- if (!strcmp(u->username, username) && pass_matches(u->password, password))
- {
- allowed++;
- break;
- }
- }
- }
+ if (ctx->l)
+ return CLI_LOOP_CTRL_CONTINUE;
- if (allowed)
- {
- cli_error(cli, " ");
- cli->state = STATE_NORMAL;
- }
- else
- {
- cli_error(cli, "\n\nAccess denied");
- free_z(username);
- free_z(password);
- cli->state = STATE_LOGIN;
- }
+ ctx->l = -1;
+ return CLI_LOOP_CTRL_BREAK;
+ }
- cli->showprompt = 1;
- }
- else if (cli->state == STATE_ENABLE_PASSWORD)
- {
- int allowed = 0;
- if (cli->enable_password)
- {
- /* check stored static enable password */
- if (pass_matches(cli->enable_password, cmd))
- allowed++;
- }
+ /* disable */
+ if (c == CTRL('Z'))
+ {
+ if (cli->mode != MODE_EXEC)
+ {
+ cli_clear_line(cli, ctx, ctx->cmd, ctx->l, ctx->cursor);
+ cli_set_configmode(cli, MODE_EXEC, NULL);
+ cli->showprompt = 1;
+ }
+
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
- if (!allowed && cli->enable_callback)
- {
- /* check callback */
- if (cli->enable_callback(cmd))
- allowed++;
- }
+ /* TAB completion */
+ if (c == CTRL('I'))
+ {
+ char *completions[CLI_MAX_LINE_WORDS];
+ int num_completions = 0;
+
+ if (cli->state == CLI_STATE_LOGIN || cli->state == CLI_STATE_PASSWORD || cli->state == CLI_STATE_ENABLE_PASSWORD)
+ return CLI_LOOP_CTRL_CONTINUE;
+
+ if (ctx->cursor != ctx->l) return CLI_LOOP_CTRL_CONTINUE;
+
+ num_completions = cli_get_completions(cli, ctx->cmd, completions, CLI_MAX_LINE_WORDS);
+ if (num_completions == 0)
+ {
+ _write(cli, ctx, "\a", 1);
+ }
+ else if (num_completions == 1)
+ {
+ // Single completion
+ for (; ctx->l > 0; ctx->l--, ctx->cursor--)
+ {
+ if (ctx->cmd[ctx->l-1] == ' ' || ctx->cmd[ctx->l-1] == '|')
+ break;
+ _write(cli, ctx, "\b", 1);
+ }
+ strcpy((ctx->cmd + ctx->l), completions[0]);
+ ctx->l += strlen(completions[0]);
+ ctx->cmd[ctx->l++] = ' ';
+ ctx->cursor = ctx->l;
+ _write(cli, ctx, completions[0], strlen(completions[0]));
+ _write(cli, ctx, " ", 1);
+ }
+ else if (ctx->lastchar == CTRL('I'))
+ {
+ // double tab
+ int i;
+ _write(cli, ctx, "\r\n", 2);
+ for (i = 0; i < num_completions; i++)
+ {
+ _write(cli, ctx, completions[i], strlen(completions[i]));
+ if (i % 4 == 3)
+ _write(cli, ctx, "\r\n", 2);
+ else
+ _write(cli, ctx, " ", 1);
+ }
+ if (i % 4 != 3) _write(cli, ctx, "\r\n", 2);
+ cli->showprompt = 1;
+ }
+ else
+ {
+ // More than one completion
+ ctx->lastchar = c;
+ _write(cli, ctx, "\a", 1);
+ }
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
- if (allowed)
- {
- cli->state = STATE_ENABLE;
- cli_set_privilege(cli, PRIVILEGE_PRIVILEGED);
- }
- else
- {
- cli_error(cli, "\n\nAccess denied");
- cli->state = STATE_NORMAL;
- }
- }
- else
- {
- if (l == 0) continue;
- if (cmd[l - 1] != '?' && strcasecmp(cmd, "history") != 0)
- cli_add_history(cli, cmd);
+ /* history */
+ if (c == CTRL('P') || c == CTRL('N'))
+ {
+ int history_found = 0;
+
+ if (cli->state == CLI_STATE_LOGIN || cli->state == CLI_STATE_PASSWORD || cli->state == CLI_STATE_ENABLE_PASSWORD)
+ return CLI_LOOP_CTRL_CONTINUE;
+
+ if (c == CTRL('P')) // Up
+ {
+ ctx->in_history--;
+ if (ctx->in_history < 0)
+ {
+ for (ctx->in_history = MAX_HISTORY-1; ctx->in_history >= 0; ctx->in_history--)
+ {
+ if (cli->history[ctx->in_history][0])
+ {
+ history_found = 1;
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (cli->history[ctx->in_history]) history_found = 1;
+ }
+ }
+ else // Down
+ {
+ ctx->in_history++;
+ if (ctx->in_history >= MAX_HISTORY || !cli->history[ctx->in_history])
+ {
+ int i = 0;
+ for (i = 0; i < MAX_HISTORY; i++)
+ {
+ if (cli->history[i])
+ {
+ ctx->in_history = i;
+ history_found = 1;
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (cli->history[ctx->in_history]) history_found = 1;
+ }
+ }
+ if (history_found && cli->history[ctx->in_history])
+ {
+ // Show history item
+ cli_clear_line(cli, ctx, ctx->cmd, ctx->l, ctx->cursor);
+ memset(ctx->cmd, 0, CLI_MAX_LINE_LENGTH);
+ strncpy(ctx->cmd, cli->history[ctx->in_history], CLI_MAX_LINE_LENGTH - 1);
+ /* cryptech: not sure if needed, but ensure we don't disclose memory after buf */
+ ctx->cmd[CLI_MAX_LINE_LENGTH - 1] = 0;
+ ctx->l = ctx->cursor = strlen(ctx->cmd);
+ _write(cli, ctx, ctx->cmd, ctx->l);
+ }
+
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
- if (cli_run_command(cli, cmd) == CLI_QUIT)
- break;
- }
+ /* left/right cursor motion */
+ if (c == CTRL('B') || c == CTRL('F'))
+ {
+ if (c == CTRL('B')) /* Left */
+ {
+ if (ctx->cursor)
+ {
+ if (cli->state != CLI_STATE_PASSWORD && cli->state != CLI_STATE_ENABLE_PASSWORD)
+ _write(cli, ctx, "\b", 1);
+
+ ctx->cursor--;
+ }
+ }
+ else /* Right */
+ {
+ if (ctx->cursor < ctx->l)
+ {
+ if (cli->state != CLI_STATE_PASSWORD && cli->state != CLI_STATE_ENABLE_PASSWORD)
+ _write(cli, ctx, &ctx->cmd[ctx->cursor], 1);
+
+ ctx->cursor++;
+ }
+ }
+
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
- // Update the last_action time now as the last command run could take a
- // long time to return
- if (cli->idle_timeout)
- time(&cli->last_action);
+ /* start of line */
+ if (c == CTRL('A'))
+ {
+ if (ctx->cursor)
+ {
+ if (cli->state != CLI_STATE_PASSWORD && cli->state != CLI_STATE_ENABLE_PASSWORD)
+ {
+ _write(cli, ctx, "\r", 1);
+ show_prompt(cli, ctx);
+ }
+
+ ctx->cursor = 0;
+ }
+
+ return CLI_LOOP_CTRL_CONTINUE;
}
- cli_free_history(cli);
- free_z(username);
- free_z(password);
- free_z(cmd);
+ /* end of line */
+ if (c == CTRL('E'))
+ {
+ if (ctx->cursor < ctx->l)
+ {
+ if (cli->state != CLI_STATE_PASSWORD && cli->state != CLI_STATE_ENABLE_PASSWORD)
+ _write(cli, ctx, &ctx->cmd[ctx->cursor], ctx->l - ctx->cursor);
- fclose(cli->client);
- cli->client = 0;
- return CLI_OK;
-}
+ ctx->cursor = ctx->l;
+ }
-int cli_file(struct cli_def *cli, FILE *fh, int privilege, int mode)
-{
- int oldpriv = cli_set_privilege(cli, privilege);
- int oldmode = cli_set_configmode(cli, mode, NULL);
- char buf[CLI_MAX_LINE_LENGTH];
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
- while (1)
+ /* normal character typed */
+ if (ctx->cursor == ctx->l)
{
- char *p;
- char *cmd;
- char *end;
-
- if (fgets(buf, CLI_MAX_LINE_LENGTH - 1, fh) == NULL)
- break; /* end of file */
+ /* append to end of line */
+ ctx->cmd[ctx->cursor] = c;
+ if (ctx->l < CLI_MAX_LINE_LENGTH - 1)
+ {
+ ctx->l++;
+ ctx->cursor++;
+ }
+ else
+ {
+ _write(cli, ctx, "\a", 1);
+ return CLI_LOOP_CTRL_CONTINUE;
+ }
+ }
+ else
+ {
+ // Middle of text
+ if (ctx->insertmode)
+ {
+ int i;
+ // Move everything one character to the right
+ if (ctx->l >= CLI_MAX_LINE_LENGTH - 2) ctx->l--;
+ for (i = ctx->l; i >= ctx->cursor; i--)
+ ctx->cmd[i + 1] = ctx->cmd[i];
+ // Write what we've just added
+ ctx->cmd[ctx->cursor] = c;
+
+ _write(cli, ctx, &ctx->cmd[ctx->cursor], ctx->l - ctx->cursor + 1);
+ for (i = 0; i < (ctx->l - ctx->cursor + 1); i++)
+ _write(cli, ctx, "\b", 1);
+ ctx->l++;
+ }
+ else
+ {
+ ctx->cmd[ctx->cursor] = c;
+ }
+ ctx->cursor++;
+ }
- if ((p = strpbrk(buf, "#\r\n")))
- *p = 0;
+ if (cli->state != CLI_STATE_PASSWORD && cli->state != CLI_STATE_ENABLE_PASSWORD)
+ {
+ if (c == '?' && ctx->cursor == ctx->l)
+ {
+ _write(cli, ctx, "\r\n", 2);
+ ctx->restore_cmd_l = ctx->l -1;
+ return CLI_LOOP_CTRL_BREAK;
+ }
+ _write(cli, ctx, &c, 1);
+ }
- cmd = buf;
- while (isspace(*cmd))
- cmd++;
+ ctx->restore_cmd_l = 0;
+ ctx->lastchar = c;
- if (!*cmd)
- continue;
+ return 0;
+}
- for (p = end = cmd; *p; p++)
- if (!isspace(*p))
- end = p;
- *++end = 0;
- if (strcasecmp(cmd, "quit") == 0)
- break;
- if (cli_run_command(cli, cmd) == CLI_QUIT)
- break;
+int cli_loop_process_cmd(struct cli_def *cli, struct cli_loop_ctx *ctx)
+{
+ if (cli->state == CLI_STATE_LOGIN)
+ {
+ if (ctx->l == 0) return 0;
+
+ /* require login */
+ if (strlen(ctx->cmd) > sizeof(ctx->username))
+ return CLI_LOOP_CTRL_BREAK;
+ strncpy(ctx->username, ctx->cmd, sizeof(ctx->username) - 1);
+ cli->state = CLI_STATE_PASSWORD;
+ cli->showprompt = 1;
}
+ else if (cli->state == CLI_STATE_PASSWORD)
+ {
+ /* require password */
+ int allowed = 0;
+
+ if (cli->auth_callback)
+ {
+ if (cli->auth_callback(ctx->username, ctx->cmd) == CLI_OK)
+ allowed++;
+ }
+
+ if (!allowed)
+ {
+ struct unp *u;
+ for (u = cli->users; u; u = u->next)
+ {
+ if (!strcmp(u->username, ctx->username) && pass_matches(u->password, ctx->cmd))
+ {
+ allowed++;
+ break;
+ }
+ }
+ }
+ memset(ctx->cmd, 0, sizeof(ctx->cmd)); // XXX verify this sizeof
+
+ if (allowed)
+ {
+ cli_error(cli, " ");
+ cli->state = CLI_STATE_NORMAL;
+ }
+ else
+ {
+ cli_error(cli, "\n\nAccess denied");
+ cli->state = CLI_STATE_LOGIN;
+ }
+
+ cli->showprompt = 1;
+ }
+ else if (cli->state == CLI_STATE_ENABLE_PASSWORD)
+ {
+ int allowed = 0;
+ if (cli->enable_password)
+ {
+ /* check stored static enable password */
+ if (pass_matches(cli->enable_password, ctx->cmd))
+ allowed++;
+ }
+
+ if (!allowed && cli->enable_callback)
+ {
+ /* check callback */
+ if (cli->enable_callback(ctx->cmd))
+ allowed++;
+ }
+
+ if (allowed)
+ {
+ cli->state = CLI_STATE_ENABLE;
+ cli_set_privilege(cli, PRIVILEGE_PRIVILEGED);
+ }
+ else
+ {
+ cli_error(cli, "\n\nAccess denied");
+ cli->state = CLI_STATE_NORMAL;
+ }
+ }
+ else
+ {
+ if (ctx->l == 0) return 0;
+ /* XXX also don't add to history if command equals the last history entry? */
+ if (ctx->cmd[ctx->l - 1] != '?' && strcasecmp(ctx->cmd, "history") != 0)
+ cli_add_history(cli, ctx->cmd);
- cli_set_privilege(cli, oldpriv);
- cli_set_configmode(cli, oldmode, NULL /* didn't save desc */);
+ if (cli_run_command(cli, ctx->cmd) == CLI_QUIT)
+ return CLI_LOOP_CTRL_BREAK;
+ }
- return CLI_OK;
+ return 0;
}
+
+/* cryptech: removed unused file mode */
+
static void _print(struct cli_def *cli, int print_mode, const char *format, va_list ap)
{
+ static char buf[1024];
va_list aq;
int n;
char *p;
@@ -1988,57 +1669,34 @@ static void _print(struct cli_def *cli, int print_mode, const char *format, va_l
while (1)
{
va_copy(aq, ap);
- if ((n = vsnprintf(cli->buffer, cli->buf_size, format, ap)) == -1)
+ if ((n = vsnprintf(buf, sizeof(buf), format, ap)) == -1)
return;
- if ((unsigned)n >= cli->buf_size)
+ if ((unsigned)n >= sizeof(buf))
{
- cli->buf_size = n + 1;
- cli->buffer = realloc(cli->buffer, cli->buf_size);
- if (!cli->buffer)
- return;
- va_end(ap);
- va_copy(ap, aq);
- continue;
+ strncpy(buf, "_print buffer would have overflown", sizeof(buf));
+ return;
}
break;
}
-
- p = cli->buffer;
+ p = buf;
do
{
char *next = strchr(p, '\n');
- struct cli_filter *f = (print_mode & PRINT_FILTERED) ? cli->filters : 0;
- int print = 1;
if (next)
*next++ = 0;
else if (print_mode & PRINT_BUFFERED)
break;
- while (print && f)
- {
- print = (f->filter(cli, p, f->data) == CLI_OK);
- f = f->next;
- }
- if (print)
- {
- if (cli->print_callback)
- cli->print_callback(cli, p);
- else if (cli->client)
- fprintf(cli->client, "%s\r\n", p);
- }
+ if (cli->print_callback)
+ cli->print_callback(cli, p);
+ else if (cli->client)
+ fprintf(cli->client, "%s\r\n", p);
p = next;
} while (p);
-
- if (p && *p)
- {
- if (p != cli->buffer)
- memmove(cli->buffer, p, strlen(p));
- }
- else *cli->buffer = 0;
}
void cli_bufprint(struct cli_def *cli, const char *format, ...)
@@ -2082,244 +1740,7 @@ struct cli_match_filter_state
} match;
};
-int cli_match_filter_init(struct cli_def *cli, int argc, char **argv, struct cli_filter *filt)
-{
- struct cli_match_filter_state *state;
- int rflags;
- int i;
- char *p;
-
- if (argc < 2)
- {
- if (cli->client)
- fprintf(cli->client, "Match filter requires an argument\r\n");
-
- return CLI_ERROR;
- }
-
- filt->filter = cli_match_filter;
- filt->data = state = calloc(sizeof(struct cli_match_filter_state), 1);
-
- if (argv[0][0] == 'i' || (argv[0][0] == 'e' && argv[0][1] == 'x')) // include/exclude
- {
- if (argv[0][0] == 'e')
- state->flags = MATCH_INVERT;
-
- state->match.string = join_words(argc-1, argv+1);
- return CLI_OK;
- }
-
-#ifdef WIN32
- /*
- * No regex functions in windows, so return an error
- */
- return CLI_ERROR;
-#endif
-
- state->flags = MATCH_REGEX;
-
- // grep/egrep
- rflags = REG_NOSUB;
- if (argv[0][0] == 'e') // egrep
- rflags |= REG_EXTENDED;
-
- i = 1;
- while (i < argc - 1 && argv[i][0] == '-' && argv[i][1])
- {
- int last = 0;
- p = &argv[i][1];
-
- if (strspn(p, "vie") != strlen(p))
- break;
-
- while (*p)
- {
- switch (*p++)
- {
- case 'v':
- state->flags |= MATCH_INVERT;
- break;
-
- case 'i':
- rflags |= REG_ICASE;
- break;
-
- case 'e':
- last++;
- break;
- }
- }
-
- i++;
- if (last)
- break;
- }
-
- p = join_words(argc-i, argv+i);
- if ((i = regcomp(&state->match.re, p, rflags)))
- {
- if (cli->client)
- fprintf(cli->client, "Invalid pattern \"%s\"\r\n", p);
-
- free_z(p);
- return CLI_ERROR;
- }
-
- free_z(p);
- return CLI_OK;
-}
-
-int cli_match_filter(UNUSED(struct cli_def *cli), const char *string, void *data)
-{
- struct cli_match_filter_state *state = data;
- int r = CLI_ERROR;
-
- if (!string) // clean up
- {
- if (state->flags & MATCH_REGEX)
- regfree(&state->match.re);
- else
- free(state->match.string);
-
- free(state);
- return CLI_OK;
- }
-
- if (state->flags & MATCH_REGEX)
- {
- if (!regexec(&state->match.re, string, 0, NULL, 0))
- r = CLI_OK;
- }
- else
- {
- if (strstr(string, state->match.string))
- r = CLI_OK;
- }
-
- if (state->flags & MATCH_INVERT)
- {
- if (r == CLI_OK)
- r = CLI_ERROR;
- else
- r = CLI_OK;
- }
-
- return r;
-}
-
-struct cli_range_filter_state {
- int matched;
- char *from;
- char *to;
-};
-
-int cli_range_filter_init(struct cli_def *cli, int argc, char **argv, struct cli_filter *filt)
-{
- struct cli_range_filter_state *state;
- char *from = 0;
- char *to = 0;
-
- if (!strncmp(argv[0], "bet", 3)) // between
- {
- if (argc < 3)
- {
- if (cli->client)
- fprintf(cli->client, "Between filter requires 2 arguments\r\n");
-
- return CLI_ERROR;
- }
-
- if (!(from = strdup(argv[1])))
- return CLI_ERROR;
- to = join_words(argc-2, argv+2);
- }
- else // begin
- {
- if (argc < 2)
- {
- if (cli->client)
- fprintf(cli->client, "Begin filter requires an argument\r\n");
-
- return CLI_ERROR;
- }
-
- from = join_words(argc-1, argv+1);
- }
-
- filt->filter = cli_range_filter;
- filt->data = state = calloc(sizeof(struct cli_range_filter_state), 1);
-
- state->from = from;
- state->to = to;
-
- return CLI_OK;
-}
-
-int cli_range_filter(UNUSED(struct cli_def *cli), const char *string, void *data)
-{
- struct cli_range_filter_state *state = data;
- int r = CLI_ERROR;
-
- if (!string) // clean up
- {
- free_z(state->from);
- free_z(state->to);
- free_z(state);
- return CLI_OK;
- }
-
- if (!state->matched)
- state->matched = !!strstr(string, state->from);
-
- if (state->matched)
- {
- r = CLI_OK;
- if (state->to && strstr(string, state->to))
- state->matched = 0;
- }
-
- return r;
-}
-
-int cli_count_filter_init(struct cli_def *cli, int argc, UNUSED(char **argv), struct cli_filter *filt)
-{
- if (argc > 1)
- {
- if (cli->client)
- fprintf(cli->client, "Count filter does not take arguments\r\n");
-
- return CLI_ERROR;
- }
-
- filt->filter = cli_count_filter;
- if (!(filt->data = calloc(sizeof(int), 1)))
- return CLI_ERROR;
-
- return CLI_OK;
-}
-
-int cli_count_filter(struct cli_def *cli, const char *string, void *data)
-{
- int *count = data;
-
- if (!string) // clean up
- {
- // print count
- if (cli->client)
- fprintf(cli->client, "%d\r\n", *count);
-
- free(count);
- return CLI_OK;
- }
-
- while (isspace(*string))
- string++;
-
- if (*string)
- (*count)++; // only count non-blank lines
-
- return CLI_ERROR; // no output
-}
+/* cryptech: removed unused filter functions */
void cli_print_callback(struct cli_def *cli, void (*callback)(struct cli_def *, const char *))
{
@@ -2331,7 +1752,6 @@ void cli_set_idle_timeout(struct cli_def *cli, unsigned int seconds)
if (seconds < 1)
seconds = 0;
cli->idle_timeout = seconds;
- time(&cli->last_action);
}
void cli_set_idle_timeout_callback(struct cli_def *cli, unsigned int seconds, int (*callback)(struct cli_def *))
@@ -2351,3 +1771,13 @@ void cli_set_context(struct cli_def *cli, void *context) {
void *cli_get_context(struct cli_def *cli) {
return cli->user_context;
}
+
+void cli_read_callback(struct cli_def *cli, int (*callback)(struct cli_def *, void *, size_t))
+{
+ cli->read_callback = callback;
+}
+
+void cli_write_callback(struct cli_def *cli, int (*callback)(struct cli_def *, const void *, size_t))
+{
+ cli->write_callback = callback;
+}
diff --git a/libcli.h b/libcli.h
index e978526..01b334f 100644
--- a/libcli.h
+++ b/libcli.h
@@ -16,7 +16,8 @@ extern "C" {
#define CLI_QUIT -2
#define CLI_ERROR_ARG -3
-#define MAX_HISTORY 256
+#define MAX_HISTORY 5 /* testing, used to be 256 */
+#define HISTORY_CMD_LEN 128
#define PRIVILEGE_UNPRIVILEGED 0
#define PRIVILEGE_PRIVILEGED 15
@@ -30,8 +31,20 @@ extern "C" {
#define PRINT_FILTERED 0x01
#define PRINT_BUFFERED 0x02
-#define CLI_MAX_LINE_LENGTH 4096
-#define CLI_MAX_LINE_WORDS 128
+#define CLI_MAX_LINE_LENGTH 64
+#define CLI_MAX_LINE_WORDS 16
+#define CLI_MAX_CMD_NAME_LEN 32
+
+#define CLI_LOOP_CTRL_CONTINUE 1
+#define CLI_LOOP_CTRL_BREAK 2
+
+enum cli_states {
+ CLI_STATE_LOGIN,
+ CLI_STATE_PASSWORD,
+ CLI_STATE_NORMAL,
+ CLI_STATE_ENABLE_PASSWORD,
+ CLI_STATE_ENABLE
+};
struct cli_def {
int completion_callback;
@@ -42,29 +55,28 @@ struct cli_def {
char *banner;
struct unp *users;
char *enable_password;
- char *history[MAX_HISTORY];
+ char history[MAX_HISTORY][HISTORY_CMD_LEN];
char showprompt;
char *promptchar;
char *hostname;
char *modestring;
int privilege;
int mode;
- int state;
+ enum cli_states state;
struct cli_filter *filters;
void (*print_callback)(struct cli_def *cli, const char *string);
FILE *client;
/* internal buffers */
void *conn;
void *service;
- char *commandname; // temporary buffer for cli_command_name() to prevent leak
- char *buffer;
- unsigned buf_size;
struct timeval timeout_tm;
time_t idle_timeout;
int (*idle_timeout_callback)(struct cli_def *);
time_t last_action;
int telnet_protocol;
void *user_context;
+ int (*read_callback)(struct cli_def *cli, void *buf, const size_t count);
+ int (*write_callback)(struct cli_def *cli, const void *buf, const size_t count);
};
struct cli_filter {
@@ -85,24 +97,31 @@ struct cli_command {
struct cli_command *parent;
};
-struct cli_def *cli_init();
+struct cli_loop_ctx {
+ char cmd[CLI_MAX_LINE_LENGTH];
+ char username[64];
+ int l, restore_cmd_l;
+ int cursor, insertmode;
+ int lastchar, is_telnet_option, skip, esc;
+ signed int in_history;
+ int sockfd;
+};
+
+int cli_init(struct cli_def *cli);
int cli_done(struct cli_def *cli);
-struct cli_command *cli_register_command(struct cli_def *cli, struct cli_command *parent, const char *command,
- int (*callback)(struct cli_def *, const char *, char **, int), int privilege,
- int mode, const char *help);
+void cli_register_command2(struct cli_def *cli, struct cli_command *cmd, struct cli_command *parent);
int cli_unregister_command(struct cli_def *cli, const char *command);
int cli_run_command(struct cli_def *cli, const char *command);
int cli_loop(struct cli_def *cli, int sockfd);
int cli_file(struct cli_def *cli, FILE *fh, int privilege, int mode);
void cli_set_auth_callback(struct cli_def *cli, int (*auth_callback)(const char *, const char *));
void cli_set_enable_callback(struct cli_def *cli, int (*enable_callback)(const char *));
-void cli_allow_user(struct cli_def *cli, const char *username, const char *password);
void cli_allow_enable(struct cli_def *cli, const char *password);
void cli_deny_user(struct cli_def *cli, const char *username);
-void cli_set_banner(struct cli_def *cli, const char *banner);
-void cli_set_hostname(struct cli_def *cli, const char *hostname);
-void cli_set_promptchar(struct cli_def *cli, const char *promptchar);
-void cli_set_modestring(struct cli_def *cli, const char *modestring);
+void cli_set_banner(struct cli_def *cli, char *banner);
+void cli_set_hostname(struct cli_def *cli, char *hostname);
+void cli_set_promptchar(struct cli_def *cli, char *promptchar);
+void cli_set_modestring(struct cli_def *cli, char *modestring);
int cli_set_privilege(struct cli_def *cli, int privilege);
int cli_set_configmode(struct cli_def *cli, int mode, const char *config_desc);
void cli_reprompt(struct cli_def *cli);
@@ -116,6 +135,14 @@ void cli_print_callback(struct cli_def *cli, void (*callback)(struct cli_def *,
void cli_free_history(struct cli_def *cli);
void cli_set_idle_timeout(struct cli_def *cli, unsigned int seconds);
void cli_set_idle_timeout_callback(struct cli_def *cli, unsigned int seconds, int (*callback)(struct cli_def *));
+void cli_read_callback(struct cli_def *cli, int (*callback)(struct cli_def *cli, void *buf, size_t count));
+void cli_write_callback(struct cli_def *cli, int (*callback)(struct cli_def *cli, const void *buf, size_t count));
+
+void cli_loop_start_new_command(struct cli_def *cli, struct cli_loop_ctx *ctx);
+void cli_loop_show_prompt(struct cli_def *cli, struct cli_loop_ctx *ctx);
+int cli_loop_read_next_char(struct cli_def *cli, struct cli_loop_ctx *ctx, unsigned char *c);
+int cli_loop_process_char(struct cli_def *cli, struct cli_loop_ctx *ctx, unsigned char c);
+int cli_loop_process_cmd(struct cli_def *cli, struct cli_loop_ctx *ctx);
// Enable or disable telnet protocol negotiation.
// Note that this is enabled by default and must be changed before cli_loop() is run.