summaryrefslogtreecommitdiffstats
path: root/ncat/ncat_posix.c
diff options
context:
space:
mode:
Diffstat (limited to 'ncat/ncat_posix.c')
-rw-r--r--ncat/ncat_posix.c388
1 files changed, 388 insertions, 0 deletions
diff --git a/ncat/ncat_posix.c b/ncat/ncat_posix.c
new file mode 100644
index 0000000..1465b2f
--- /dev/null
+++ b/ncat/ncat_posix.c
@@ -0,0 +1,388 @@
+/***************************************************************************
+ * ncat_posix.c -- POSIX-specific functions. *
+ ***********************IMPORTANT NMAP LICENSE TERMS************************
+ *
+ * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap
+ * Project"). Nmap is also a registered trademark of the Nmap Project.
+ *
+ * This program is distributed under the terms of the Nmap Public Source
+ * License (NPSL). The exact license text applying to a particular Nmap
+ * release or source code control revision is contained in the LICENSE
+ * file distributed with that version of Nmap or source code control
+ * revision. More Nmap copyright/legal information is available from
+ * https://nmap.org/book/man-legal.html, and further information on the
+ * NPSL license itself can be found at https://nmap.org/npsl/ . This
+ * header summarizes some key points from the Nmap license, but is no
+ * substitute for the actual license text.
+ *
+ * Nmap is generally free for end users to download and use themselves,
+ * including commercial use. It is available from https://nmap.org.
+ *
+ * The Nmap license generally prohibits companies from using and
+ * redistributing Nmap in commercial products, but we sell a special Nmap
+ * OEM Edition with a more permissive license and special features for
+ * this purpose. See https://nmap.org/oem/
+ *
+ * If you have received a written Nmap license agreement or contract
+ * stating terms other than these (such as an Nmap OEM license), you may
+ * choose to use and redistribute Nmap under those terms instead.
+ *
+ * The official Nmap Windows builds include the Npcap software
+ * (https://npcap.com) for packet capture and transmission. It is under
+ * separate license terms which forbid redistribution without special
+ * permission. So the official Nmap Windows builds may not be redistributed
+ * without special permission (such as an Nmap OEM license).
+ *
+ * Source is provided to this software because we believe users have a
+ * right to know exactly what a program is going to do before they run it.
+ * This also allows you to audit the software for security holes.
+ *
+ * Source code also allows you to port Nmap to new platforms, fix bugs, and add
+ * new features. You are highly encouraged to submit your changes as a Github PR
+ * or by email to the dev@nmap.org mailing list for possible incorporation into
+ * the main distribution. Unless you specify otherwise, it is understood that
+ * you are offering us very broad rights to use your submissions as described in
+ * the Nmap Public Source License Contributor Agreement. This is important
+ * because we fund the project by selling licenses with various terms, and also
+ * because the inability to relicense code has caused devastating problems for
+ * other Free Software projects (such as KDE and NASM).
+ *
+ * The free version of Nmap is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,
+ * indemnification and commercial support are all available through the
+ * Npcap OEM program--see https://nmap.org/oem/
+ *
+ ***************************************************************************/
+
+/* $Id$ */
+
+#include "ncat.h"
+
+#ifdef HAVE_LUA
+#include "ncat_lua.h"
+#endif
+
+char **cmdline_split(const char *cmdexec);
+
+/* fork and exec a child process with netexec. Close the given file descriptor
+ in the parent process. Return the child's PID or -1 on error. */
+int netrun(struct fdinfo *info, char *cmdexec)
+{
+ int pid;
+
+ errno = 0;
+ pid = fork();
+ if (pid == 0) {
+ /* In the child process. */
+ netexec(info, cmdexec);
+ }
+
+ Close(info->fd);
+
+ if (pid == -1 && o.verbose)
+ logdebug("Error in fork: %s\n", strerror(errno));
+
+ return pid;
+}
+
+/* Call write in a loop until all the data is written or an error occurs. The
+ return value is the number of bytes written. If it is less than size, then
+ there was an error. */
+static int write_loop(int fd, char *buf, size_t size)
+{
+ char *p;
+ int n;
+
+ p = buf;
+ while (p - buf < size) {
+ n = write(fd, p, size - (p - buf));
+ if (n == -1) {
+ if (errno == EINTR)
+ continue;
+ else
+ break;
+ }
+ p += n;
+ }
+
+ return p - buf;
+}
+
+/* Run the given command line as if with exec. What we actually do is fork the
+ command line as a subprocess, then loop, relaying data between the socket and
+ the subprocess. This allows Ncat to handle SSL from the socket and give plain
+ text to the subprocess, and also allows things like logging and line delays.
+ Never returns. */
+void netexec(struct fdinfo *info, char *cmdexec)
+{
+ int child_stdin[2];
+ int child_stdout[2];
+ int pid;
+ int crlf_state;
+
+ char buf[DEFAULT_TCP_BUF_LEN];
+ int maxfd;
+
+ if (o.debug) {
+ switch (o.execmode) {
+ case EXEC_SHELL:
+ logdebug("Executing with shell: %s\n", cmdexec);
+ break;
+#ifdef HAVE_LUA
+ case EXEC_LUA:
+ logdebug("Executing as lua script: %s\n", cmdexec);
+ break;
+#endif
+ default:
+ logdebug("Executing: %s\n", cmdexec);
+ break;
+ }
+ }
+
+ if (pipe(child_stdin) == -1 || pipe(child_stdout) == -1)
+ bye("Can't create child pipes: %s", strerror(errno));
+
+ pid = fork();
+ if (pid == -1)
+ bye("Error in fork: %s", strerror(errno));
+ if (pid == 0) {
+ /* This is the child process. Exec the command. */
+ close(child_stdin[1]);
+ close(child_stdout[0]);
+
+ /* We might have turned off SIGPIPE handling in ncat_listen.c. Since
+ the child process SIGPIPE might mean that the connection got broken,
+ ignoring it could result in an infinite loop if the code here
+ ignores the error codes of read()/write() calls. So, just in case,
+ let's restore SIGPIPE so that writing to a broken pipe results in
+ killing the child process. */
+ Signal(SIGPIPE, SIG_DFL);
+
+ /* rearrange stdin and stdout */
+ Dup2(child_stdin[0], STDIN_FILENO);
+ Dup2(child_stdout[1], STDOUT_FILENO);
+
+ setup_environment(info);
+
+ switch (o.execmode) {
+ char **cmdargs;
+
+ case EXEC_SHELL:
+ execl("/bin/sh", "sh", "-c", cmdexec, (void *) NULL);
+ break;
+#ifdef HAVE_LUA
+ case EXEC_LUA:
+ lua_run();
+ break;
+#endif
+ default:
+ cmdargs = cmdline_split(cmdexec);
+ execv(cmdargs[0], cmdargs);
+ break;
+ }
+
+ /* exec failed. */
+ die("exec");
+ }
+
+ close(child_stdin[0]);
+ close(child_stdout[1]);
+
+ maxfd = child_stdout[0];
+ if (info->fd > maxfd)
+ maxfd = info->fd;
+
+ /* This is the parent process. Enter a "caretaker" loop that reads from the
+ socket and writes to the subprocess, and reads from the subprocess and
+ writes to the socket. We exit the loop on any read error (or EOF). On a
+ write error we just close the opposite side of the conversation. */
+ crlf_state = 0;
+ for (;;) {
+ fd_set fds;
+ int r, n_r;
+
+ FD_ZERO(&fds);
+ checked_fd_set(info->fd, &fds);
+ checked_fd_set(child_stdout[0], &fds);
+
+ r = fselect(maxfd + 1, &fds, NULL, NULL, NULL);
+ if (r == -1) {
+ if (errno == EINTR)
+ continue;
+ else
+ break;
+ }
+ if (checked_fd_isset(info->fd, &fds)) {
+ int pending;
+
+ do {
+ n_r = ncat_recv(info, buf, sizeof(buf), &pending);
+ if (n_r <= 0) {
+ /* return value can be 0 without meaning EOF in some cases such as SSL
+ * renegotiations that require read/write socket operations but do not
+ * have any application data. */
+ if(n_r == 0 && info->lasterr == 0) {
+ continue; /* Check pending */
+ }
+ goto loop_end;
+ }
+ r = write_loop(child_stdin[1], buf, n_r);
+ if (r != n_r)
+ goto loop_end;
+ } while (pending);
+ }
+ if (checked_fd_isset(child_stdout[0], &fds)) {
+ char *crlf = NULL, *wbuf;
+ n_r = read(child_stdout[0], buf, sizeof(buf));
+ if (n_r <= 0)
+ break;
+ wbuf = buf;
+ if (o.crlf) {
+ if (fix_line_endings((char *) buf, &n_r, &crlf, &crlf_state))
+ wbuf = crlf;
+ }
+ r = ncat_send(info, wbuf, n_r);
+ if (crlf != NULL)
+ free(crlf);
+ if (r <= 0)
+ goto loop_end;
+ }
+ }
+loop_end:
+
+#ifdef HAVE_OPENSSL
+ if (info->ssl != NULL) {
+ SSL_shutdown(info->ssl);
+ SSL_free(info->ssl);
+ }
+#endif
+ close(info->fd);
+
+ exit(0);
+}
+
+/*
+ * Split a command line into an array suitable for handing to execv.
+ *
+ * A note on syntax: words are split on whitespace and '\' escapes characters.
+ * '\\' will show up as '\' and '\ ' will leave a space, combining two
+ * words. Examples:
+ * "ncat\ experiment -l -k" will be parsed as the following tokens:
+ * "ncat experiment", "-l", "-k".
+ * "ncat\\ -l -k" will be parsed as "ncat\", "-l", "-k"
+ * See the test program, test/test-cmdline-split to see additional cases.
+ */
+char **cmdline_split(const char *cmdexec)
+{
+ const char *ptr;
+ char *cur_arg, **cmd_args;
+ int max_tokens = 0, arg_idx = 0, ptr_idx = 0;
+
+ /* Figure out the maximum number of tokens needed */
+ ptr = cmdexec;
+ while (*ptr) {
+ // Find the start of the token
+ while (('\0' != *ptr) && isspace((int) (unsigned char) *ptr))
+ ptr++;
+ if ('\0' == *ptr)
+ break;
+ max_tokens++;
+ // Find the start of the whitespace again
+ while (('\0' != *ptr) && !isspace((int) (unsigned char) *ptr))
+ ptr++;
+ }
+
+ /* The line is not empty so we've got something to deal with */
+ cmd_args = (char **) safe_malloc(sizeof(char *) * (max_tokens + 1));
+ cur_arg = (char *) Calloc(sizeof(char), strlen(cmdexec) + 1);
+
+ /* Get and copy the tokens */
+ ptr = cmdexec;
+ while (*ptr) {
+ while (('\0' != *ptr) && isspace((int) (unsigned char) *ptr))
+ ptr++;
+ if ('\0' == *ptr)
+ break;
+
+ while (('\0' != *ptr) && !isspace((int) (unsigned char) *ptr)) {
+ if ('\\' == *ptr) {
+ ptr++;
+ if ('\0' == *ptr)
+ break;
+
+ cur_arg[ptr_idx] = *ptr;
+ ptr_idx++;
+ ptr++;
+
+ if ('\\' != *(ptr - 1)) {
+ while (('\0' != *ptr) && isspace((int) (unsigned char) *ptr))
+ ptr++;
+ }
+ } else {
+ cur_arg[ptr_idx] = *ptr;
+ ptr_idx++;
+ ptr++;
+ }
+ }
+ cur_arg[ptr_idx] = '\0';
+
+ cmd_args[arg_idx] = strdup(cur_arg);
+ cur_arg[0] = '\0';
+ ptr_idx = 0;
+ arg_idx++;
+ }
+
+ cmd_args[arg_idx] = NULL;
+
+ /* Clean up */
+ free(cur_arg);
+
+ return cmd_args;
+}
+
+int ncat_openlog(const char *logfile, int append)
+{
+ if (append)
+ return Open(logfile, O_WRONLY | O_CREAT | O_APPEND, 0664);
+ else
+ return Open(logfile, O_WRONLY | O_CREAT | O_TRUNC, 0664);
+}
+
+void set_lf_mode(void)
+{
+ /* Nothing needed. */
+}
+
+#ifdef HAVE_OPENSSL
+
+#define NCAT_CA_CERTS_PATH (NCAT_DATADIR "/" NCAT_CA_CERTS_FILE)
+
+int ssl_load_default_ca_certs(SSL_CTX *ctx)
+{
+ int rc;
+
+ if (o.debug)
+ logdebug("Using system default trusted CA certificates and those in %s.\n", NCAT_CA_CERTS_PATH);
+
+ /* Load distribution-provided defaults, if any. */
+ rc = SSL_CTX_set_default_verify_paths(ctx);
+ ncat_assert(rc > 0);
+
+ /* Also load the trusted certificates we ship. */
+ rc = SSL_CTX_load_verify_locations(ctx, NCAT_CA_CERTS_PATH, NULL);
+ if (rc != 1) {
+ if (o.debug)
+ logdebug("Unable to load trusted CA certificates from %s: %s\n",
+ NCAT_CA_CERTS_PATH, ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+int setenv_portable(const char *name, const char *value)
+{
+ return setenv(name, value, 1);
+}