diff options
Diffstat (limited to 'ncat/ncat_connect.c')
-rw-r--r-- | ncat/ncat_connect.c | 1425 |
1 files changed, 1425 insertions, 0 deletions
diff --git a/ncat/ncat_connect.c b/ncat/ncat_connect.c new file mode 100644 index 0000000..dda5183 --- /dev/null +++ b/ncat/ncat_connect.c @@ -0,0 +1,1425 @@ +/*************************************************************************** + * ncat_connect.c -- Ncat connect mode. * + ***********************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 "base64.h" +#include "nsock.h" +#include "ncat.h" +#include "util.h" +#include "sys_wrap.h" + +#include "nbase.h" +#include "http.h" + +#ifndef WIN32 +#include <unistd.h> +#include <netdb.h> +#endif +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#ifdef HAVE_OPENSSL +#include <openssl/ssl.h> +#include <openssl/err.h> + +/* Deprecated in OpenSSL 3.0 */ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +# define SSL_get_peer_certificate SSL_get1_peer_certificate +#endif +#endif + +#ifdef WIN32 +/* Define missing constant for shutdown(2). + * See: + * http://msdn.microsoft.com/en-us/library/windows/desktop/ms740481%28v=vs.85%29.aspx + */ +#define SHUT_WR SD_SEND +#endif + +struct conn_state { + nsock_iod sock_nsi; + nsock_iod stdin_nsi; + nsock_event_id idle_timer_event_id; + int crlf_state; +}; + +static struct conn_state cs = { + NULL, + NULL, + 0, + 0 +}; + +static void try_nsock_connect(nsock_pool nsp, struct sockaddr_list *conn_addr); +static void connect_handler(nsock_pool nsp, nsock_event evt, void *data); +static void post_connect(nsock_pool nsp, nsock_iod iod); +static void read_stdin_handler(nsock_pool nsp, nsock_event evt, void *data); +static void read_socket_handler(nsock_pool nsp, nsock_event evt, void *data); +static void write_socket_handler(nsock_pool nsp, nsock_event evt, void *data); +static void idle_timer_handler(nsock_pool nsp, nsock_event evt, void *data); +static void refresh_idle_timer(nsock_pool nsp); + +#ifdef HAVE_OPENSSL +/* This callback is called for every certificate in a chain. ok is true if + OpenSSL's internal verification has verified the certificate. We don't change + anything about the verification, we only need access to the certificates to + provide diagnostics. */ +static int verify_callback(int ok, X509_STORE_CTX *store) +{ + X509 *cert = X509_STORE_CTX_get_current_cert(store); + int err = X509_STORE_CTX_get_error(store); + + /* Print the subject, issuer, and fingerprint depending on the verbosity + level. */ + if ((!ok && o.verbose) || o.debug > 1) { + char digest_buf[SHA1_STRING_LENGTH + 1]; + char *fp; + + loguser("Subject: "); + X509_NAME_print_ex_fp(stderr, X509_get_subject_name(cert), 0, XN_FLAG_COMPAT); + loguser_noprefix("\n"); + loguser("Issuer: "); + X509_NAME_print_ex_fp(stderr, X509_get_issuer_name(cert), 0, XN_FLAG_COMPAT); + loguser_noprefix("\n"); + + fp = ssl_cert_fp_str_sha1(cert, digest_buf, sizeof(digest_buf)); + ncat_assert(fp == digest_buf); + loguser("SHA-1 fingerprint: %s\n", digest_buf); + } + + if (!ok && o.verbose) { + loguser("Certificate verification failed (%s).\n", + X509_verify_cert_error_string(err)); + } + + return ok; +} + +static void set_ssl_ctx_options(SSL_CTX *ctx) +{ + if (o.ssltrustfile == NULL) { + ssl_load_default_ca_certs(ctx); + } else { + if (o.debug) + logdebug("Using trusted CA certificates from %s.\n", o.ssltrustfile); + if (SSL_CTX_load_verify_locations(ctx, o.ssltrustfile, NULL) != 1) { + bye("Could not load trusted certificates from %s.\n%s", + o.ssltrustfile, ERR_error_string(ERR_get_error(), NULL)); + } + } + + if (o.sslverify) { + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback); + } else { + /* Still check verification status and report it */ + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_callback); + if (o.ssl && o.debug) + logdebug("Not doing certificate verification.\n"); + } + + if (o.sslcert != NULL && o.sslkey != NULL) { + if (SSL_CTX_use_certificate_file(ctx, o.sslcert, SSL_FILETYPE_PEM) != 1) + bye("SSL_CTX_use_certificate_file(): %s.", ERR_error_string(ERR_get_error(), NULL)); + if (SSL_CTX_use_PrivateKey_file(ctx, o.sslkey, SSL_FILETYPE_PEM) != 1) + bye("SSL_CTX_use_Privatekey_file(): %s.", ERR_error_string(ERR_get_error(), NULL)); + } else { + if ((o.sslcert == NULL) != (o.sslkey == NULL)) + bye("The --ssl-key and --ssl-cert options must be used together."); + } + if (o.sslciphers == NULL) { + if (!SSL_CTX_set_cipher_list(ctx, "ALL:!aNULL:!eNULL:!LOW:!EXP:!RC4:!MD5:@STRENGTH")) + bye("Unable to set OpenSSL cipher list: %s", ERR_error_string(ERR_get_error(), NULL)); + } + else { + printf("setting ciphers: %s\n", o.sslciphers); + if (!SSL_CTX_set_cipher_list(ctx, o.sslciphers)) + bye("Unable to set OpenSSL cipher list: %s", ERR_error_string(ERR_get_error(), NULL)); + } + +#ifdef HAVE_ALPN_SUPPORT + + if (o.sslalpn) { + size_t alpn_len; + unsigned char *alpn = next_protos_parse(&alpn_len, o.sslalpn); + + if (alpn == NULL) + bye("Could not parse ALPN string"); + + if (o.debug) + logdebug("Using ALPN String %s\n", o.sslalpn); + + /* SSL_CTX_set_alpn_protos returns 0 on success */ + if (SSL_CTX_set_alpn_protos(ctx, alpn, alpn_len) != 0){ + free(alpn); + bye("SSL_CTX_set_alpn_protos: %s.", ERR_error_string(ERR_get_error(), NULL)); + } + + free(alpn); + } + +#endif + +} +#endif + +/* Depending on verbosity, print a message that a connection was established. */ +static void connect_report(nsock_iod nsi) +{ + union sockaddr_u peer; + zmem(&peer, sizeof(peer.storage)); + + nsock_iod_get_communication_info(nsi, NULL, NULL, NULL, &peer.sockaddr, + sizeof(peer.storage)); + if (o.verbose) { + char peer_str[INET6_ADDRSTRLEN + sizeof(union sockaddr_u)] = {0}; + if (o.proxytype) { + Snprintf(peer_str, sizeof(peer_str), "%s:%u", o.target, o.portno); + } + else { + Strncpy(peer_str, socktop(&peer, 0), sizeof(peer_str)); + } +#ifdef HAVE_OPENSSL + if (nsock_iod_check_ssl(nsi)) { + X509 *cert; + X509_NAME *subject; + char digest_buf[SHA1_STRING_LENGTH + 1]; + char *fp; + + loguser("SSL connection to %s.", peer_str); + + cert = SSL_get_peer_certificate((SSL *)nsock_iod_get_ssl(nsi)); + ncat_assert(cert != NULL); + + subject = X509_get_subject_name(cert); + if (subject != NULL) { + char buf[256]; + int n; + + n = X509_NAME_get_text_by_NID(subject, NID_organizationName, buf, sizeof(buf)); + if (n >= 0 && n <= sizeof(buf) - 1) + loguser_noprefix(" %s", buf); + } + + loguser_noprefix("\n"); + + fp = ssl_cert_fp_str_sha1(cert, digest_buf, sizeof(digest_buf)); + ncat_assert(fp == digest_buf); + loguser("SHA-1 fingerprint: %s\n", digest_buf); + } else +#endif + { + loguser("Connected to %s.\n", peer_str); + } + } +} + +/* Just like inet_socktop, but it puts IPv6 addresses in square brackets. */ +static const char *sock_to_url(char *host_str, unsigned short port) +{ + static char buf[512]; + + switch(getaddrfamily(host_str)) { + case -1: + case 1: + Snprintf(buf, sizeof(buf), "%s:%hu", host_str, port); + break; + case 2: + Snprintf(buf, sizeof(buf), "[%s]:%hu", host_str, port); + } + + return buf; +} + +static int append_connect_request_line(char **buf, size_t *size, size_t *offset, + char* host_str,unsigned short port) +{ + return strbuf_sprintf(buf, size, offset, "CONNECT %s HTTP/1.0\r\n", + sock_to_url(host_str,port)); +} + +static char *http_connect_request(char* host_str, unsigned short port, int *n) +{ + char *buf = NULL; + size_t size = 0, offset = 0; + + append_connect_request_line(&buf, &size, &offset, host_str, port); + strbuf_append_str(&buf, &size, &offset, "\r\n"); + *n = offset; + + return buf; +} + +static char *http_connect_request_auth(char* host_str, unsigned short port, int *n, + struct http_challenge *challenge) +{ + char *buf = NULL; + size_t size = 0, offset = 0; + + append_connect_request_line(&buf, &size, &offset, host_str, port); + strbuf_append_str(&buf, &size, &offset, "Proxy-Authorization:"); + if (challenge->scheme == AUTH_BASIC) { + char *auth_str; + + auth_str = b64enc((unsigned char *) o.proxy_auth, strlen(o.proxy_auth)); + strbuf_sprintf(&buf, &size, &offset, " Basic %s\r\n", auth_str); + free(auth_str); +#if HAVE_HTTP_DIGEST + } else if (challenge->scheme == AUTH_DIGEST) { + char *proxy_auth; + char *username, *password; + char *response_hdr; + + /* Split up the proxy auth argument. */ + proxy_auth = Strdup(o.proxy_auth); + username = proxy_auth; + password = strchr(proxy_auth, ':'); + if (password == NULL) { + free(proxy_auth); + return NULL; + } + *password++ = '\0'; + response_hdr = http_digest_proxy_authorization(challenge, + username, password, "CONNECT", sock_to_url(o.target,o.portno)); + if (response_hdr == NULL) { + free(proxy_auth); + return NULL; + } + strbuf_append_str(&buf, &size, &offset, response_hdr); + free(proxy_auth); + free(response_hdr); +#endif + } else { + bye("Unknown authentication type."); + } + strbuf_append_str(&buf, &size, &offset, "\r\n"); + *n = offset; + + return buf; +} + +/* Return a usable socket descriptor after proxy negotiation, or -1 on any + error. If any bytes are received through the proxy after negotiation, they + are written to stdout. */ +static int do_proxy_http(void) +{ + struct socket_buffer sockbuf; + char *request; + char *status_line, *header; + char *remainder; + size_t len; + int sd, code; + int n; + char *target; + union sockaddr_u addr; + size_t sslen; + char addrstr[INET6_ADDRSTRLEN]; + + request = NULL; + status_line = NULL; + header = NULL; + + sd = do_connect(SOCK_STREAM); + if (sd == -1) { + loguser("Proxy connection failed: %s.\n", socket_strerror(socket_errno())); + return -1; + } + + if (proxyresolve(o.target, 0, &addr.storage, &sslen, o.af)) { + /* target resolution has failed, possibly because it is disabled */ + if (!(o.proxydns & PROXYDNS_REMOTE)) { + loguser("Error: Failed to resolve host %s locally.\n", o.target); + goto bail; + } + if (o.verbose) + loguser("Host %s will be resolved by the proxy.\n", o.target); + target = o.target; + } else { + /* addr is now populated with either sockaddr_in or sockaddr_in6 */ + Strncpy(addrstr, inet_socktop(&addr), sizeof(addrstr)); + target = addrstr; + if (o.verbose && getaddrfamily(o.target) == -1) + loguser("Host %s locally resolved to %s.\n", o.target, target); + } + + /* First try a request with no authentication. */ + request = http_connect_request(target, o.portno, &n); + if (send(sd, request, n, 0) < 0) { + loguser("Error sending proxy request: %s.\n", socket_strerror(socket_errno())); + goto bail; + } + free(request); + request = NULL; + + socket_buffer_init(&sockbuf, sd); + + if (http_read_status_line(&sockbuf, &status_line) != 0) { + loguser("Error reading proxy response Status-Line.\n"); + goto bail; + } + code = http_parse_status_line_code(status_line); + if (o.debug) + logdebug("Proxy returned status code %d.\n", code); + free(status_line); + status_line = NULL; + if (http_read_header(&sockbuf, &header) != 0) { + loguser("Error reading proxy response header.\n"); + goto bail; + } + + if (code == 407 && o.proxy_auth != NULL) { + struct http_header *h; + struct http_challenge challenge; + + close(sd); + sd = -1; + + if (http_parse_header(&h, header) != 0) { + loguser("Error parsing proxy response header.\n"); + goto bail; + } + free(header); + header = NULL; + + if (http_header_get_proxy_challenge(h, &challenge) == NULL) { + loguser("Error getting Proxy-Authenticate challenge.\n"); + http_header_free(h); + goto bail; + } + http_header_free(h); + + sd = do_connect(SOCK_STREAM); + if (sd == -1) { + loguser("Proxy reconnection failed: %s.\n", socket_strerror(socket_errno())); + goto bail; + } + + request = http_connect_request_auth(target, o.portno, &n, &challenge); + if (request == NULL) { + loguser("Error building Proxy-Authorization header.\n"); + http_challenge_free(&challenge); + goto bail; + } + if (o.debug) + logdebug("Reconnection header:\n%s", request); + if (send(sd, request, n, 0) < 0) { + loguser("Error sending proxy request: %s.\n", socket_strerror(socket_errno())); + http_challenge_free(&challenge); + goto bail; + } + free(request); + request = NULL; + http_challenge_free(&challenge); + + socket_buffer_init(&sockbuf, sd); + + if (http_read_status_line(&sockbuf, &status_line) != 0) { + loguser("Error reading proxy response Status-Line.\n"); + goto bail; + } + code = http_parse_status_line_code(status_line); + if (o.debug) + logdebug("Proxy returned status code %d.\n", code); + free(status_line); + status_line = NULL; + if (http_read_header(&sockbuf, &header) != 0) { + loguser("Error reading proxy response header.\n"); + goto bail; + } + } + + if (code != 200) { + loguser("Proxy returned status code %d.\n", code); + goto bail; + } + + free(header); + header = NULL; + + remainder = socket_buffer_remainder(&sockbuf, &len); + Write(STDOUT_FILENO, remainder, len); + + return sd; + +bail: + if (sd != -1) + close(sd); + if (request != NULL) + free(request); + if (status_line != NULL) + free(status_line); + if (header != NULL) + free(header); + + return -1; +} + + +/* SOCKS4a support + * Return a usable socket descriptor after + * proxy negotiation, or -1 on any error. + */ +static int do_proxy_socks4(void) +{ + char socksbuf[8]; + struct socks4_data socks4msg; + size_t datalen; + char *username = o.proxy_auth != NULL ? o.proxy_auth : ""; + union sockaddr_u addr; + size_t sslen; + int sd; + + if (getaddrfamily(o.target) == 2) { + loguser("Error: IPv6 addresses are not supported with Socks4.\n"); + return -1; + } + + sd = do_connect(SOCK_STREAM); + if (sd == -1) { + loguser("Proxy connection failed: %s.\n", socket_strerror(socket_errno())); + return sd; + } + + if (o.verbose) { + loguser("Connected to proxy %s:%hu\n", inet_socktop(&targetaddrs->addr), + inet_port(&targetaddrs->addr)); + } + + /* Fill the socks4_data struct */ + zmem(&socks4msg, sizeof(socks4msg)); + socks4msg.version = SOCKS4_VERSION; + socks4msg.type = SOCKS_CONNECT; + socks4msg.port = htons(o.portno); + + if (strlen(username) >= sizeof(socks4msg.data)) { + loguser("Error: username is too long.\n"); + close(sd); + return -1; + } + strcpy(socks4msg.data, username); + datalen = strlen(username) + 1; + + if (proxyresolve(o.target, 0, &addr.storage, &sslen, AF_INET)) { + /* target resolution has failed, possibly because it is disabled */ + if (!(o.proxydns & PROXYDNS_REMOTE)) { + loguser("Error: Failed to resolve host %s locally.\n", o.target); + close(sd); + return -1; + } + if (o.verbose) + loguser("Host %s will be resolved by the proxy.\n", o.target); + socks4msg.address = inet_addr("0.0.0.1"); + if (datalen + strlen(o.target) >= sizeof(socks4msg.data)) { + loguser("Error: host name is too long.\n"); + close(sd); + return -1; + } + strcpy(socks4msg.data + datalen, o.target); + datalen += strlen(o.target) + 1; + } else { + /* addr is now populated with sockaddr_in */ + socks4msg.address = addr.in.sin_addr.s_addr; + if (o.verbose && getaddrfamily(o.target) == -1) + loguser("Host %s locally resolved to %s.\n", o.target, + inet_socktop(&addr)); + } + + if (send(sd, (char *)&socks4msg, offsetof(struct socks4_data, data) + datalen, 0) < 0) { + loguser("Error: sending proxy request: %s.\n", socket_strerror(socket_errno())); + close(sd); + return -1; + } + + /* The size of the socks4 response is 8 bytes. So read exactly + 8 bytes from the buffer */ + if (recv(sd, socksbuf, 8, 0) < 0) { + loguser("Error: short response from proxy.\n"); + close(sd); + return -1; + } + + if (sd != -1 && socksbuf[1] != SOCKS4_CONN_ACC) { + loguser("Proxy connection failed.\n"); + close(sd); + return -1; + } + + return sd; +} + +/* SOCKS5 support + * Return a usable socket descriptor after + * proxy negotiation, or -1 on any error. + */ +static int do_proxy_socks5(void) +{ + struct socks5_connect socks5msg; + uint16_t proxyport = htons(o.portno); + char socksbuf[4]; + int sd; + size_t dstlen, targetlen; + struct socks5_request socks5msg2; + struct socks5_auth socks5auth; + char *uptr, *pptr; + size_t authlen, ulen, plen; + union sockaddr_u addr; + size_t sslen; + void *addrbuf; + size_t addrlen; + size_t bndaddrlen; + char bndaddr[SOCKS5_DST_MAXLEN + 2]; /* IPv4/IPv6/hostname and port */ + + sd = do_connect(SOCK_STREAM); + if (sd == -1) { + loguser("Proxy connection failed: %s.\n", socket_strerror(socket_errno())); + return sd; + } + + if (o.verbose) { + loguser("Connected to proxy %s:%hu\n", inet_socktop(&targetaddrs->addr), + inet_port(&targetaddrs->addr)); + } + + zmem(&socks5msg,sizeof(socks5msg)); + socks5msg.ver = SOCKS5_VERSION; + socks5msg.nmethods = 0; + socks5msg.methods[socks5msg.nmethods++] = SOCKS5_AUTH_NONE; + + if (o.proxy_auth) + socks5msg.methods[socks5msg.nmethods++] = SOCKS5_AUTH_USERPASS; + + if (send(sd, (char *)&socks5msg, offsetof(struct socks5_connect, methods) + socks5msg.nmethods, 0) < 0) { + loguser("Error: proxy request: %s.\n", socket_strerror(socket_errno())); + close(sd); + return -1; + } + + /* connect response just two bytes, version and auth method */ + if (recv(sd, socksbuf, 2, 0) < 0) { + loguser("Error: malformed connect response from proxy.\n"); + close(sd); + return -1; + } + + if (socksbuf[0] != SOCKS5_VERSION) { + loguser("Error: wrong SOCKS version in connect response.\n"); + close(sd); + return -1; + } + + switch((unsigned char)socksbuf[1]) { + case SOCKS5_AUTH_NONE: + if (o.verbose) + loguser("No authentication needed.\n"); + break; + + case SOCKS5_AUTH_USERPASS: + if (o.verbose) + loguser("Doing username and password authentication.\n"); + + if(!o.proxy_auth){ + /* Proxy must not select a method not offered by the client */ + loguser("Error: proxy selected invalid authentication method.\n"); + close(sd); + return -1; + } + + /* Split up the proxy auth argument. */ + uptr = o.proxy_auth; + pptr = strchr(o.proxy_auth, ':'); + if (pptr == NULL) { + loguser("Error: invalid username:password combo.\n"); + close(sd); + return -1; + } + + ulen = (pptr++) - uptr; + plen = strlen(pptr); + if (ulen > SOCKS5_USR_MAXLEN) { + loguser("Error: username length exceeds %d.\n", SOCKS5_USR_MAXLEN); + close(sd); + return -1; + } + if (plen > SOCKS5_PWD_MAXLEN) { + loguser("Error: password length exceeds %d.\n", SOCKS5_PWD_MAXLEN); + close(sd); + return -1; + } + + /* + * For username/password authentication the client's authentication request is + * field 1: version number, 1 byte (must be 0x01 -- version of subnegotiation) + * field 2: username length, 1 byte + * field 3: username + * field 4: password length, 1 byte + * field 5: password + * + * Server response for username/password authentication: + * field 1: version, 1 byte + * field 2: status code, 1 byte. + * 0x00 = success + * any other value = failure, connection must be closed + */ + + socks5auth.ver = 1; + authlen = 0; + socks5auth.data[authlen++] = ulen; + memcpy(socks5auth.data + authlen, uptr, ulen); + authlen += ulen; + + socks5auth.data[authlen++] = plen; + memcpy(socks5auth.data + authlen, pptr, plen); + authlen += plen; + + if (send(sd, (char *) &socks5auth, offsetof(struct socks5_auth, data) + authlen, 0) < 0) { + loguser("Error: sending proxy authentication.\n"); + close(sd); + return -1; + } + + if (recv(sd, socksbuf, 2, 0) < 0) { + loguser("Error: malformed proxy authentication response.\n"); + close(sd); + return -1; + } + + if (socksbuf[0] != 1 || socksbuf[1] != 0) { + loguser("Error: authentication failed.\n"); + close(sd); + return -1; + } + + break; + + case SOCKS5_AUTH_FAILED: + loguser("Error: no acceptable authentication method proposed.\n"); + close(sd); + return -1; + + default: + /* Proxy must not select a method not offered by the client */ + loguser("Error: proxy selected invalid authentication method.\n"); + close(sd); + return -1; + } + + zmem(&socks5msg2,sizeof(socks5msg2)); + socks5msg2.ver = SOCKS5_VERSION; + socks5msg2.cmd = SOCKS_CONNECT; + socks5msg2.rsv = 0; + + if (proxyresolve(o.target, 0, &addr.storage, &sslen, o.af)) { + /* target resolution has failed, possibly because it is disabled */ + if (!(o.proxydns & PROXYDNS_REMOTE)) { + loguser("Error: Failed to resolve host %s locally.\n", o.target); + close(sd); + return -1; + } + if (o.verbose) + loguser("Host %s will be resolved by the proxy.\n", o.target); + socks5msg2.atyp = SOCKS5_ATYP_NAME; + targetlen = strlen(o.target); + if (targetlen > SOCKS5_DST_MAXLEN){ + loguser("Error: hostname length exceeds %d.\n", SOCKS5_DST_MAXLEN); + close(sd); + return -1; + } + dstlen = 0; + socks5msg2.dst[dstlen++] = targetlen; + memcpy(socks5msg2.dst + dstlen, o.target, targetlen); + dstlen += targetlen; + } else { + /* addr is now populated with either sockaddr_in or sockaddr_in6 */ + switch (addr.sockaddr.sa_family) { + case AF_INET: + socks5msg2.atyp = SOCKS5_ATYP_IPv4; + addrbuf = &addr.in.sin_addr; + addrlen = 4; + break; + case AF_INET6: + socks5msg2.atyp = SOCKS5_ATYP_IPv6; + addrbuf = &addr.in6.sin6_addr; + addrlen = 16; + break; + default: + ncat_assert(0); + } + memcpy(socks5msg2.dst, addrbuf, addrlen); + dstlen = addrlen; + if (o.verbose && getaddrfamily(o.target) == -1) + loguser("Host %s locally resolved to %s.\n", o.target, + inet_socktop(&addr)); + } + + memcpy(socks5msg2.dst + dstlen, &proxyport, 2); + dstlen += 2; + + if (send(sd, (char *) &socks5msg2, offsetof(struct socks5_request , dst) + dstlen, 0) < 0) { + loguser("Error: sending proxy request: %s.\n", socket_strerror(socket_errno())); + close(sd); + return -1; + } + + if (recv(sd, socksbuf, 4, 0) < 0) { + loguser("Error: malformed request response from proxy.\n"); + close(sd); + return -1; + } + + if (socksbuf[0] != SOCKS5_VERSION) { + loguser("Error: wrong SOCKS version in request response.\n"); + close(sd); + return -1; + } + + switch(socksbuf[1]) { + case 0: + if (o.verbose) + loguser("connection succeeded.\n"); + break; + case 1: + loguser("Error: general SOCKS server failure.\n"); + close(sd); + return -1; + case 2: + loguser("Error: connection not allowed by ruleset.\n"); + close(sd); + return -1; + case 3: + loguser("Error: Network unreachable.\n"); + close(sd); + return -1; + case 4: + loguser("Error: Host unreachable.\n"); + close(sd); + return -1; + case 5: + loguser("Error: Connection refused.\n"); + close(sd); + return -1; + case 6: + loguser("Error: TTL expired.\n"); + close(sd); + return -1; + case 7: + loguser("Error: Command not supported.\n"); + close(sd); + return -1; + case 8: + loguser("Error: Address type not supported.\n"); + close(sd); + return -1; + default: + loguser("Error: unassigned value in the reply.\n"); + close(sd); + return -1; + } + + switch (socksbuf[3]) { + case SOCKS5_ATYP_IPv4: + bndaddrlen = 4 + 2; + break; + case SOCKS5_ATYP_IPv6: + bndaddrlen = 16 + 2; + break; + case SOCKS5_ATYP_NAME: + if (recv(sd, socksbuf, 1, 0) < 0) { + loguser("Error: malformed request response from proxy.\n"); + close(sd); + return -1; + } + bndaddrlen = (unsigned char)socksbuf[0] + 2; + break; + default: + loguser("Error: invalid proxy bind address type.\n"); + close(sd); + return -1; + } + + if (recv(sd, bndaddr, bndaddrlen, 0) < 0) { + loguser("Error: malformed request response from proxy.\n"); + close(sd); + return -1; + } + + return(sd); +} + +static nsock_iod new_iod(nsock_pool mypool) { + nsock_iod nsi = nsock_iod_new(mypool, NULL); + if (nsi == NULL) + bye("Failed to create nsock_iod."); + if (nsock_iod_set_hostname(nsi, o.sslservername) == -1) + bye("Failed to set hostname on iod."); + + switch (srcaddr.storage.ss_family) { + case AF_UNSPEC: + break; + case AF_INET: + nsock_iod_set_localaddr(nsi, &srcaddr.storage, + sizeof(srcaddr.in)); + break; +#ifdef AF_INET6 + case AF_INET6: + nsock_iod_set_localaddr(nsi, &srcaddr.storage, + sizeof(srcaddr.in6)); + break; +#endif +#if HAVE_SYS_UN_H + case AF_UNIX: + nsock_iod_set_localaddr(nsi, &srcaddr.storage, + SUN_LEN((struct sockaddr_un *)&srcaddr.storage)); + break; +#endif + default: + nsock_iod_set_localaddr(nsi, &srcaddr.storage, + sizeof(srcaddr.storage)); + break; + } + + if (o.numsrcrtes) { + unsigned char *ipopts = NULL; + size_t ipoptslen = 0; + + if (o.af != AF_INET) + bye("Sorry, -g can only currently be used with IPv4."); + ipopts = buildsrcrte(targetaddrs->addr.in.sin_addr, o.srcrtes, o.numsrcrtes, o.srcrteptr, &ipoptslen); + + nsock_iod_set_ipoptions(nsi, ipopts, ipoptslen); + free(ipopts); /* Nsock has its own copy */ + } + return nsi; +} + +int ncat_connect(void) +{ + nsock_pool mypool; + int rc; + + /* Unless explicitly asked not to do so, ncat uses the + * fallback nsock engine to maximize compatibility between + * operating systems and the different use cases. + */ + if (!o.nsock_engine) + nsock_set_default_engine("select"); + + /* Create an nsock pool */ + if ((mypool = nsock_pool_new(NULL)) == NULL) + bye("Failed to create nsock_pool."); + + if (o.debug >= 6) + nsock_set_loglevel(NSOCK_LOG_DBG_ALL); + else if (o.debug >= 3) + nsock_set_loglevel(NSOCK_LOG_DBG); + else if (o.debug >= 1) + nsock_set_loglevel(NSOCK_LOG_INFO); + else + nsock_set_loglevel(NSOCK_LOG_ERROR); + + /* Allow connections to broadcast addresses. */ + nsock_pool_set_broadcast(mypool, 1); + +#ifdef HAVE_OPENSSL +#ifdef HAVE_DTLS_CLIENT_METHOD + if(o.proto == IPPROTO_UDP) + set_ssl_ctx_options((SSL_CTX *) nsock_pool_dtls_init(mypool, 0)); + else +#endif + set_ssl_ctx_options((SSL_CTX *) nsock_pool_ssl_init(mypool, 0)); +#endif + + if (!o.proxytype) { +#if HAVE_SYS_UN_H + /* For DGRAM UNIX socket we have to use source socket */ + if (o.af == AF_UNIX && o.proto == IPPROTO_UDP) + { + if (srcaddr.storage.ss_family != AF_UNIX) { + char *tmp_name = NULL; +#if HAVE_MKSTEMP + char *tmpdir = getenv("TMPDIR"); + size_t size=0, offset=0; + strbuf_sprintf(&tmp_name, &size, &offset, "%s/ncat.XXXXXX", + tmpdir ? tmpdir : "/tmp"); + if (mkstemp(tmp_name) == -1) { + bye("Failed to create name for temporary DGRAM source Unix domain socket (mkstemp)."); + } + unlink(tmp_name); +#else + /* If no source socket was specified, we have to create temporary one. */ + if ((tmp_name = tempnam(NULL, "ncat.")) == NULL) + bye("Failed to create name for temporary DGRAM source Unix domain socket (tempnam)."); +#endif + + NCAT_INIT_SUN(&srcaddr, tmp_name); + free (tmp_name); + } + + if (o.verbose) + loguser("[%s] used as source DGRAM Unix domain socket.\n", srcaddr.un.sun_path); + } +#endif + /* A non-proxy connection. Create an iod for a new socket. */ + cs.sock_nsi = new_iod(mypool); + +#if HAVE_SYS_UN_H + if (o.af == AF_UNIX) { + if (o.proto == IPPROTO_UDP) { + nsock_connect_unixsock_datagram(mypool, cs.sock_nsi, connect_handler, NULL, + &targetaddrs->addr.sockaddr, + SUN_LEN((struct sockaddr_un *)&targetaddrs->addr.sockaddr)); + } else { + nsock_connect_unixsock_stream(mypool, cs.sock_nsi, connect_handler, o.conntimeout, + NULL, &targetaddrs->addr.sockaddr, + SUN_LEN((struct sockaddr_un *)&targetaddrs->addr.sockaddr)); + } + } else +#endif + { + /* Add connection to first resolved address. */ + try_nsock_connect(mypool, targetaddrs); + } + } else { + /* A proxy connection. */ + static int connect_socket; + + if (strcmp(o.proxytype, "http") == 0) { + connect_socket = do_proxy_http(); + } else if (strcmp(o.proxytype, "socks4") == 0) { + connect_socket = do_proxy_socks4(); + } else if (strcmp(o.proxytype, "socks5") == 0) { + connect_socket = do_proxy_socks5(); + } + + if (connect_socket == -1) + { + nsock_pool_delete(mypool); + return 1; + } + + /* Once the proxy negotiation is done, Nsock takes control of the + socket. */ + cs.sock_nsi = nsock_iod_new2(mypool, connect_socket, NULL); + if (nsock_iod_set_hostname(cs.sock_nsi, o.sslservername) == -1) + bye("Failed to set hostname on iod."); + if (o.ssl) + { + /* connect_handler creates stdin_nsi and calls post_connect */ + nsock_reconnect_ssl(mypool, cs.sock_nsi, connect_handler, o.conntimeout, NULL, NULL); + } + else + { + /* Create IOD for nsp->stdin */ + if ((cs.stdin_nsi = nsock_iod_new2(mypool, 0, NULL)) == NULL) + bye("Failed to create stdin nsiod."); + + post_connect(mypool, cs.sock_nsi); + } + } + + /* connect */ + rc = nsock_loop(mypool, -1); + + free_sockaddr_list(targetaddrs); + + if (o.verbose) { + struct timeval end_time; + double time; + gettimeofday(&end_time, NULL); + time = TIMEVAL_MSEC_SUBTRACT(end_time, start_time) / 1000.0; + loguser("%lu bytes sent, %lu bytes received in %.2f seconds.\n", + nsock_iod_get_write_count(cs.sock_nsi), + nsock_iod_get_read_count(cs.sock_nsi), time); + } + +#if HAVE_SYS_UN_H + if (o.af == AF_UNIX && o.proto == IPPROTO_UDP) { + if (o.verbose) + loguser("Deleting source DGRAM Unix domain socket. [%s]\n", srcaddr.un.sun_path); + unlink(srcaddr.un.sun_path); + } +#endif + + nsock_pool_delete(mypool); + + return rc == NSOCK_LOOP_ERROR ? 1 : 0; +} + +static void try_nsock_connect(nsock_pool nsp, struct sockaddr_list *conn_addr) +{ +#ifdef HAVE_OPENSSL + if (o.ssl) { + nsock_connect_ssl(nsp, cs.sock_nsi, connect_handler, + o.conntimeout, (void *)conn_addr->next, + &conn_addr->addr.sockaddr, conn_addr->addrlen, + o.proto, inet_port(&conn_addr->addr), + NULL); + } + else +#endif +#ifdef HAVE_LINUX_VM_SOCKETS_H + if (o.af == AF_VSOCK) { + if (o.proto == IPPROTO_UDP) { + nsock_connect_vsock_datagram(nsp, cs.sock_nsi, connect_handler, + (void *)conn_addr->next, &conn_addr->addr.sockaddr, + conn_addr->addrlen, conn_addr->addr.vm.svm_port); + } else { + nsock_connect_vsock_stream(nsp, cs.sock_nsi, connect_handler, + o.conntimeout, (void *)conn_addr->next, + &conn_addr->addr.sockaddr, conn_addr->addrlen, + conn_addr->addr.vm.svm_port); + } + } + else +#endif + if (o.proto == IPPROTO_UDP) { + nsock_connect_udp(nsp, cs.sock_nsi, connect_handler, (void *)conn_addr->next, + &conn_addr->addr.sockaddr, conn_addr->addrlen, + inet_port(&conn_addr->addr)); + } + else if (o.proto == IPPROTO_SCTP) { + nsock_connect_sctp(nsp, cs.sock_nsi, connect_handler, + o.conntimeout, (void *)conn_addr->next, + &conn_addr->addr.sockaddr, conn_addr->addrlen, + inet_port(&conn_addr->addr)); + } + else { + nsock_connect_tcp(nsp, cs.sock_nsi, connect_handler, + o.conntimeout, (void *)conn_addr->next, + &conn_addr->addr.sockaddr, conn_addr->addrlen, + inet_port(&conn_addr->addr)); + } +} + +static void send_udp_null(nsock_pool nsp) +{ + char *NULL_PROBE = "\0"; + int length = 1; + nsock_write(nsp, cs.sock_nsi, write_socket_handler, -1, NULL, NULL_PROBE, length); +} + +static void connect_handler(nsock_pool nsp, nsock_event evt, void *data) +{ + enum nse_status status = nse_status(evt); + enum nse_type type = nse_type(evt); + struct sockaddr_list *next_addr = (struct sockaddr_list *)data; + + ncat_assert(type == NSE_TYPE_CONNECT || type == NSE_TYPE_CONNECT_SSL); + + if (status == NSE_STATUS_ERROR || status == NSE_STATUS_TIMEOUT) { + /* If there are more resolved addresses, try connecting to next one */ + if (next_addr != NULL) { + if (o.verbose) { + union sockaddr_u peer; + zmem(&peer, sizeof(peer.storage)); + nsock_iod_get_communication_info(cs.sock_nsi, NULL, NULL, NULL, + &peer.sockaddr, sizeof(peer.storage)); + loguser("Connection to %s failed: %s.\n", inet_socktop(&peer), + (status == NSE_STATUS_TIMEOUT) + ? nse_status2str(status) + : socket_strerror(nse_errorcode(evt))); + loguser("Trying next address...\n"); + } + /* Delete the old IOD and make a new one for the next address. + * This also clears SSL session info. */ + nsock_iod_delete(cs.sock_nsi, NSOCK_PENDING_NOTIFY); + cs.sock_nsi = new_iod(nsp); + + try_nsock_connect(nsp, next_addr); + return; + } + else { + free_sockaddr_list(targetaddrs); + if (!o.zerobyte||o.verbose) + loguser("%s.\n", + (status == NSE_STATUS_TIMEOUT) + ? nse_status2str(status) + : socket_strerror(nse_errorcode(evt))); + exit(1); + } + } else { + ncat_assert(status == NSE_STATUS_SUCCESS); + } + +#ifdef HAVE_OPENSSL + if (nsock_iod_check_ssl(cs.sock_nsi)) { + /* Check the domain name. ssl_post_connect_check prints an + error message if appropriate. */ + if (!ssl_post_connect_check((SSL *)nsock_iod_get_ssl(cs.sock_nsi), o.sslservername)) + bye("Certificate verification error."); + } +#endif + + connect_report(cs.sock_nsi); + if (o.proto != IPPROTO_UDP && o.zerobyte) { + nsock_loop_quit(nsp); + } + + /* Create IOD for nsp->stdin */ + if ((cs.stdin_nsi = nsock_iod_new2(nsp, 0, NULL)) == NULL) + bye("Failed to create stdin nsiod."); + + post_connect(nsp, nse_iod(evt)); +} + +/* Handle --exec if appropriate, otherwise start the initial read events and set + the idle timeout. */ +static void post_connect(nsock_pool nsp, nsock_iod iod) +{ + /* Command to execute. */ + if (o.cmdexec) { + struct fdinfo info; + + info.fd = nsock_iod_get_sd(iod); +#ifdef HAVE_OPENSSL + info.ssl = (SSL *)nsock_iod_get_ssl(iod); +#endif + /* Convert Nsock's non-blocking socket to an ordinary blocking one. It's + possible for a program to write fast enough that it will get an + EAGAIN on write on a non-blocking socket. */ + block_socket(info.fd); + netexec(&info, o.cmdexec); + } + + /* Start the initial reads. */ + + if (!o.sendonly && !o.zerobyte) + nsock_read(nsp, cs.sock_nsi, read_socket_handler, -1, NULL); + + if (!o.recvonly && !o.zerobyte) + nsock_readbytes(nsp, cs.stdin_nsi, read_stdin_handler, -1, NULL, 0); + + if (o.zerobyte && o.proto==IPPROTO_UDP) + send_udp_null(nsp); + + /* The --idle-timeout option says to exit after a certain period of + inactivity. We start a timer here and reset it on every read event; see + refresh_idle_timer. */ + if (o.idletimeout > 0) { + cs.idle_timer_event_id = + nsock_timer_create(nsp, idle_timer_handler, o.idletimeout, NULL); + } +} + +static void read_stdin_handler(nsock_pool nsp, nsock_event evt, void *data) +{ + enum nse_status status = nse_status(evt); + enum nse_type type = nse_type(evt); + char *buf, *tmp = NULL; + int nbytes; + + ncat_assert(type == NSE_TYPE_READ); + + + if (status == NSE_STATUS_EOF) { + if (!o.noshutdown) { +#ifdef HAVE_OPENSSL + SSL *ssl = NULL; + if (o.ssl && NULL != (ssl = (SSL *)nsock_iod_get_ssl(cs.sock_nsi))) { + SSL_shutdown(ssl); + } + else +#endif + shutdown(nsock_iod_get_sd(cs.sock_nsi), SHUT_WR); + } + /* In --send-only mode or non-TCP mode, exit after EOF on stdin. */ + if (o.proto != IPPROTO_TCP || (o.proto == IPPROTO_TCP && o.sendonly)) + nsock_loop_quit(nsp); + return; + } else if (status == NSE_STATUS_ERROR) { + loguser("%s.\n", socket_strerror(nse_errorcode(evt))); + exit(1); + } else if (status == NSE_STATUS_TIMEOUT) { + loguser("%s.\n", nse_status2str(status)); + exit(1); + } else if (status == NSE_STATUS_CANCELLED || status == NSE_STATUS_KILL) { + return; + } else { + ncat_assert(status == NSE_STATUS_SUCCESS); + } + + buf = nse_readbuf(evt, &nbytes); + + /* read from stdin */ + if (o.linedelay) + ncat_delay_timer(o.linedelay); + + if (o.crlf) { + if (fix_line_endings(buf, &nbytes, &tmp, &cs.crlf_state)) + buf = tmp; + } + + nsock_write(nsp, cs.sock_nsi, write_socket_handler, -1, NULL, buf, nbytes); + ncat_log_send(buf, nbytes); + + if (tmp) + free(tmp); + + refresh_idle_timer(nsp); +} + +static void read_socket_handler(nsock_pool nsp, nsock_event evt, void *data) +{ + enum nse_status status = nse_status(evt); + enum nse_type type = nse_type(evt); + char *buf; + int nbytes; + + ncat_assert(type == NSE_TYPE_READ); + + if (status == NSE_STATUS_EOF) { +#ifdef WIN32 + _close(STDOUT_FILENO); +#else + Close(STDOUT_FILENO); +#endif + /* In --recv-only mode or non-TCP mode, exit after EOF on the socket. */ + if (o.proto != IPPROTO_TCP || (o.proto == IPPROTO_TCP && o.recvonly)) + nsock_loop_quit(nsp); + return; + } else if (status == NSE_STATUS_ERROR) { + if (!o.zerobyte||o.verbose) + loguser("%s.\n", socket_strerror(nse_errorcode(evt))); + exit(1); + } else if (status == NSE_STATUS_TIMEOUT) { + loguser("%s.\n", nse_status2str(status)); + exit(1); + } else if (status == NSE_STATUS_CANCELLED || status == NSE_STATUS_KILL) { + return; + } else { + ncat_assert(status == NSE_STATUS_SUCCESS); + } + + buf = nse_readbuf(evt, &nbytes); + + if (o.linedelay) + ncat_delay_timer(o.linedelay); + + if (o.telnet) + dotelnet(nsock_iod_get_sd(nse_iod(evt)), (unsigned char *) buf, nbytes); + + /* Write socket data to stdout */ + Write(STDOUT_FILENO, buf, nbytes); + ncat_log_recv(buf, nbytes); + + nsock_readbytes(nsp, cs.sock_nsi, read_socket_handler, -1, NULL, 0); + + refresh_idle_timer(nsp); +} + +static void write_socket_handler(nsock_pool nsp, nsock_event evt, void *data) +{ + enum nse_status status = nse_status(evt); + enum nse_type type = nse_type(evt); + + ncat_assert(type == NSE_TYPE_WRITE); + + if (status == NSE_STATUS_ERROR) { + loguser("%s.\n", socket_strerror(nse_errorcode(evt))); + exit(1); + } else if (status == NSE_STATUS_TIMEOUT) { + loguser("%s.\n", nse_status2str(status)); + exit(1); + } else if (status == NSE_STATUS_CANCELLED || status == NSE_STATUS_KILL) { + return; + } else { + ncat_assert(status == NSE_STATUS_SUCCESS); + } + + if (o.zerobyte){ + ncat_assert(o.proto == IPPROTO_UDP); + nsock_read(nsp, cs.sock_nsi, read_socket_handler, -1, NULL); + return; + } + /* The write to the socket was successful. Allow reading more from stdin + now. */ + nsock_readbytes(nsp, cs.stdin_nsi, read_stdin_handler, -1, NULL, 0); +} + +static void idle_timer_handler(nsock_pool nsp, nsock_event evt, void *data) +{ + enum nse_status status = nse_status(evt); + enum nse_type type = nse_type(evt); + + ncat_assert(type == NSE_TYPE_TIMER); + + if (status == NSE_STATUS_CANCELLED || status == NSE_STATUS_KILL) + return; + + ncat_assert(status == NSE_STATUS_SUCCESS); + + if (o.zerobyte&&o.proto==IPPROTO_UDP){ + if (o.verbose) + loguser("UDP packet sent successfully\n"); + nsock_loop_quit(nsp); + return; + } + + loguser("Idle timeout expired (%d ms).\n", o.idletimeout); + + exit(1); +} + +static void refresh_idle_timer(nsock_pool nsp) +{ + if (o.idletimeout <= 0) + return; + nsock_event_cancel(nsp, cs.idle_timer_event_id, 0); + cs.idle_timer_event_id = + nsock_timer_create(nsp, idle_timer_handler, o.idletimeout, NULL); +} |