/*
* Copyright (c) 2002, 2003, 2004, 2005 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sys/param.h>
#include <sys/types.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_IOCCOM_H
#include <sys/ioccom.h>
#endif
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/tree.h>
#include <sys/wait.h>
#include <sys/queue.h>
#include <pcap.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <signal.h>
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include <unistd.h>
#include <getopt.h>
#include <pwd.h>
#include <assert.h>
#undef timeout_pending
#undef timeout_initialized
#include <dnet.h>
#include <event.h>
#include "honeyd.h"
#include "template.h"
#include "subsystem.h"
#include "personality.h"
#include "xprobe_assoc.h"
#include "ipfrag.h"
#include "router.h"
#include "network.h"
#include "tcp.h"
#include "udp.h"
#include "hooks.h"
#include "pool.h"
#include "plugins_config.h"
#include "plugins.h"
#include "interface.h"
#include "arp.h"
#include "gre.h"
#include "log.h"
#include "osfp.h"
#include "parser.h"
#include "ui.h"
#include "ethernet.h"
#include "tagging.h"
#include "stats.h"
#include "dhcpclient.h"
#include "rrdtool.h"
#include "histogram.h"
#include "update.h"
#include "util.h"
#ifdef HAVE_PYTHON
#include <Python.h>
#include "pyextend.h"
#include "pydataprocessing.h"
#include "pydatahoneyd.h"
#endif
/* Prototypes */
void honeyd_tcp_timeout(int, short, void *);
void honeyd_udp_timeout(int, short, void *);
void honeyd_delay_cb(int, short, void *);
enum forward honeyd_route_packet(struct ip_hdr *, u_int, struct addr *,
struct addr *, int *);
void tcp_retrans_timeout(int, short, void *);
void icmp_error_send(struct template *, struct addr *, uint8_t, uint8_t,
struct ip_hdr *, struct spoof);
struct tree tcpcons;
struct conlru tcplru;
struct tree udpcons;
struct conlru udplru;
struct spoof no_spoof; /* spoof settings for default packet processing */
struct config config = {
NULL,
PATH_HONEYDDATA "/nmap.prints",
PATH_HONEYDDATA "/xprobe2.conf",
PATH_HONEYDDATA "/nmap.assoc",
PATH_HONEYDDATA "/pf.os"
};
struct stats_network stats_network = {
0, /* input bytes */
0 /* output bytes */
};
SPLAY_PROTOTYPE(tree, tuple, node, conhdr_compare);
SPLAY_GENERATE(tree, tuple, node, conhdr_compare);
struct rrdtool_drv *honeyd_rrd_drv;
struct rrdtool_db *honeyd_traffic_db;
struct event honeyd_rrd_ev;
FILE *honeyd_servicefp;
struct timeval honeyd_uptime;
static FILE *honeyd_logfp;
static ip_t *honeyd_ip;
struct pool *pool_pkt;
struct pool *pool_delay;
rand_t *honeyd_rand;
int honeyd_sig;
int honeyd_nconnects;
int honeyd_nchildren;
int honeyd_ttl = HONEYD_DFL_TTL;
struct tcp_con honeyd_tmp;
int honeyd_show_include_dir;
int honeyd_show_data_dir;
int honeyd_show_version;
int honeyd_show_usage;
int honeyd_debug;
uid_t honeyd_uid = 32767;
gid_t honeyd_gid = 32767;
int honeyd_needsroot; /* Need different IDs */
int honeyd_disable_webserver = 0;
int honeyd_disable_update = 0;
int honeyd_ignore_parse_errors = 0;
int honeyd_verify_config = 0;
int honeyd_webserver_fix_permissions = 0;
char *honeyd_webserver_address = "127.0.0.1";
int honeyd_webserver_port = 80;
char *honeyd_webserver_root = PATH_HONEYDDATA \
"/webserver/htdocs";
char *honeyd_rrdtool_path = PATH_RRDTOOL;
/* can be used by unittests to do bad stuff */
void (*honeyd_delay_callback)(int, short, void *) = honeyd_delay_cb;
static char *logfile = NULL; /* Log file names */
static char *servicelog = NULL;
static struct option honeyd_long_opts[] = {
{"include-dir", 0, &honeyd_show_include_dir, 1},
{"data-dir", 0, &honeyd_show_data_dir, 1},
{"version", 0, &honeyd_show_version, 1},
{"help", 0, &honeyd_show_usage, 1},
{"webserver-address", required_argument, NULL, 'A'},
{"webserver-port", required_argument, NULL, 'W'},
{"webserver-root", required_argument, NULL, 'X'},
{"rrdtool-path", required_argument, NULL, 'Y'},
{"disable-webserver", 0, &honeyd_disable_webserver, 1},
{"disable-update", 0, &honeyd_disable_update, 1},
{"verify-config", 0, &honeyd_verify_config, 1},
{"ignore-parse-errors", 0, &honeyd_ignore_parse_errors, 1},
{"fix-webserver-permissions", 0, &honeyd_webserver_fix_permissions, 1},
{0, 0, 0, 0}
};
void
usage(void)
{
fprintf(stderr,
"Usage: honeyd [OPTIONS] [net ...]\n\n"
"where options include:\n"
" -d Do not daemonize, be verbose.\n"
" -P Enable polling mode.\n"
" -l logfile Log packets and connections to logfile.\n"
" -s logfile Logs service status output to logfile.\n"
" -i interface Listen on interface.\n"
" -p file Read nmap-style fingerprints from file.\n"
" -x file Read xprobe-style fingerprints from file.\n"
" -a assocfile Read nmap-xprobe associations from file.\n"
" -0 osfingerprints Read pf-style OS fingerprints from file.\n"
" -u uid Set the uid Honeyd should run as.\n"
" -g gid Set the gid Honeyd should run as.\n"
" -f configfile Read configuration from file.\n"
" -c host:port:name:pass Reports starts to collector.\n"
" --webserver-address=address Address on which webserver listens.\n"
" --webserver-port=port Port on which webserver listens.\n"
" --webserver-root=path Root of document tree.\n"
" --fix-webserver-permissions Change ownership and permissions.\n"
" --rrdtool-path=path Path to rrdtool.\n"
" --disable-webserver Disables internal webserver\n"
" --disable-update Disables checking for security fixes.\n"
" --verify-config Verify configuration file then exit.\n"
" -V, --version Print program version and exit.\n"
" -h, --help Print this message and exit.\n"
"\n"
"For plugin development:\n"
" --include-dir Prints out header files directory and exits.\n"
" --data-dir Prints out data/plug-in directory and exits.\n");
exit(1);
}
/* XXX ches debug */
void
print_spoof(char *msg, struct spoof s) {
char buf2[100], buf3[100];
if (s.new_src.addr_type == ADDR_TYPE_NONE)
strlcpy(buf2, "**no addr**", sizeof(buf2));
else
addr_ntop(&s.new_src, buf2, sizeof(buf2));
if (s.new_dst.addr_type == ADDR_TYPE_NONE)
strlcpy(buf3, "**no addr**", sizeof(buf3));
else
addr_ntop(&s.new_dst, buf3, sizeof(buf3));
}
void
honeyd_settcp(struct tcp_con *con, struct ip_hdr *ip, struct tcp_hdr *tcp,
int local)
{
struct tuple *hdr = &con->conhdr;
memset(hdr, 0, sizeof(struct tuple));
hdr->ip_src = ip->ip_src;
hdr->ip_dst = ip->ip_dst;
hdr->sport = ntohs(tcp->th_sport);
hdr->dport = ntohs(tcp->th_dport);
hdr->type = SOCK_STREAM;
hdr->local = local;
con->rcv_flags = tcp->th_flags;
con->cmd.pfd = -1;
con->cmd.perrfd = -1;
}
void
honeyd_setudp(struct udp_con *con, struct ip_hdr *ip, struct udp_hdr *udp,
int local)
{
struct tuple *hdr = &con->conhdr;
memset(hdr, 0, sizeof(struct tuple));
hdr->ip_src = ip->ip_src;
hdr->ip_dst = ip->ip_dst;
hdr->sport = ntohs(udp->uh_sport);
hdr->dport = ntohs(udp->uh_dport);
hdr->type = SOCK_DGRAM;
hdr->local = local;
con->softerrors = 0;
con->cmd.pfd = -1;
con->cmd.perrfd = -1;
TAILQ_INIT(&con->incoming);
}
struct tuple *
tuple_find(struct tree *root, struct tuple *key)
{
return SPLAY_FIND(tree, root, key);
}
/*
* Iterate over all connection objects.
*/
int
tuple_iterate(struct conlru *head, int (*f)(struct tuple *, void *), void *arg)
{
struct tuple *conhdr;
TAILQ_FOREACH(conhdr, head, next) {
if ((*f)(conhdr, arg) == -1)
return (-1);
}
return (0);
}
static void
syslog_init(int argc, char *argv[])
{
int options, i;
char buf[MAXPATHLEN];
#ifdef LOG_PERROR
options = LOG_PERROR|LOG_PID|LOG_CONS;
#else
options = LOG_PID|LOG_CONS;
#endif
openlog("honeyd", options, LOG_DAEMON);
/* Create a string containing all the command line
* arguments and pass it to syslog:
*/
buf[0] = '\0';
for (i = 1; i < argc; i++) {
if (i > 1 && strlcat(buf, " ", sizeof(buf)) >= sizeof(buf))
break;
if (strlcat(buf, argv[i], sizeof(buf)) >= sizeof(buf))
break;
}
syslog(LOG_NOTICE, "started with %s", buf);
}
/*
* Update traffic statistics for honeyd.
*/
void
honeyd_rrd_cb(int fd, short what, void *arg)
{
static int count;
char line[1024];
struct event *ev = arg;
struct timeval tv;
snprintf(line, sizeof(line), "%f:%f",
(double)count_get_minute(stats_network.input_bytes)/60.0,
(double)count_get_minute(stats_network.output_bytes)/60.0);
rrdtool_db_update(honeyd_traffic_db, NULL, line);
timerclear(&tv);
tv.tv_sec = 60;
evtimer_add(ev, &tv);
/* Create a graph every five minutes */
if (count++ % 5 == 0) {
char filename[1024];
struct timeval tv;
/* Hourly graph */
timerclear(&tv);
tv.tv_sec = -7200;
snprintf(filename, sizeof(filename),
"%s/graphs/traffic_hourly.gif", honeyd_webserver_root);
rrdtool_graph(honeyd_traffic_db, filename, &tv, NULL,
"DEF:inoctets=/tmp/honeyd_traffic.rrd:input:AVERAGE "
"DEF:outoctets=/tmp/honeyd_traffic.rrd:output:AVERAGE "
"AREA:inoctets#00FF00:\"In traffic\" "
"LINE1:outoctets#0000FF:\"Out traffic\"");
/* Daily graph */
timerclear(&tv);
tv.tv_sec = -86400;
snprintf(filename, sizeof(filename),
"%s/graphs/traffic_daily.gif", honeyd_webserver_root);
rrdtool_graph(honeyd_traffic_db, filename, &tv, NULL,
"DEF:inoctets=/tmp/honeyd_traffic.rrd:input:AVERAGE "
"DEF:outoctets=/tmp/honeyd_traffic.rrd:output:AVERAGE "
"AREA:inoctets#00FF00:\"In traffic\" "
"LINE1:outoctets#0000FF:\"Out traffic\"");
}
}
void
honeyd_rrd_start(const char *rrdtool_path)
{
/* Initialize our traffic stats for rrdtool */
char *honeyd_traffic_filename = "/tmp/honeyd_traffic.rrd";
if ((honeyd_rrd_drv = rrdtool_init(rrdtool_path)) == NULL)
errx(1, "%s: cannot start rrdtool", __func__);
if ((honeyd_traffic_db = rrdtool_db_start(honeyd_rrd_drv,
honeyd_traffic_filename, 60)) == NULL)
errx(1, "%s: cannot create rrd db: %s",
__func__, honeyd_traffic_filename);
rrdtool_db_datasource(honeyd_traffic_db,
"input", "GAUGE", 600);
rrdtool_db_datasource(honeyd_traffic_db,
"output", "GAUGE", 600);
rrdtool_db_commit(honeyd_traffic_db);
/* Start the periodic traffic update timer */
evtimer_set(&honeyd_rrd_ev, honeyd_rrd_cb, &honeyd_rrd_ev);
honeyd_rrd_cb(-1, EV_TIMEOUT, &honeyd_rrd_ev);
}
/*
* Initializes data structures pertaining to the daemon
*/
void
honeyd_init(void)
{
struct rlimit rl;
struct passwd *pwd;
/* Record our start time */
gettimeofday(&honeyd_uptime, NULL);
/* Find the correct ids for nobody */
if ((pwd = getpwnam("nobody")) != NULL) {
honeyd_uid = pwd->pw_uid;
honeyd_gid = pwd->pw_gid;
}
/* Initalize ongoing connection state */
SPLAY_INIT(&tcpcons);
TAILQ_INIT(&tcplru);
SPLAY_INIT(&udpcons);
TAILQ_INIT(&udplru);
memset(&honeyd_tmp, 0, sizeof(honeyd_tmp));
/* Raising file descriptor limits */
rl.rlim_max = RLIM_INFINITY;
rl.rlim_cur = RLIM_INFINITY;
if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
/* Linux does not seem to like this */
if (getrlimit(RLIMIT_NOFILE, &rl) == -1)
err(1, "getrlimit: NOFILE");
rl.rlim_cur = rl.rlim_max;
if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
err(1, "setrlimit: NOFILE");
}
#ifdef RLIMIT_NPROC
if (getrlimit(RLIMIT_NPROC, &rl) == -1)
err(1, "getrlimit: NPROC");
rl.rlim_max = rl.rlim_max/2;
rl.rlim_cur = rl.rlim_max;
if (setrlimit(RLIMIT_NPROC, &rl) == -1)
err(1, "setrlimit: NPROC");
#endif
stats_network.input_bytes = count_new();
stats_network.output_bytes = count_new();
}
#ifdef HAVE_PYTHON
static int
honeyd_is_webserver_enabled(void)
{
if (honeyd_webserver_port <= 0)
return 0;
if (honeyd_disable_webserver)
return 0;
return (1);
}
#endif
void
honeyd_exit(int status)
{
honeyd_logend(honeyd_logfp);
honeyd_logend(honeyd_servicefp);
template_free_all(TEMPLATE_FREE_DEALLOCATE);
interface_close_all();
rand_close(honeyd_rand);
ip_close(honeyd_ip);
closelog();
unlink(PIDFILE);
#ifdef HAVE_PYTHON
if (honeyd_is_webserver_enabled())
pyextend_webserver_exit();
pyextend_exit();
#endif
exit(status);
}
/* Encapsulate a packet into Ethernet */
void
honeyd_ether_cb(struct arp_req *req, int success, void *arg)
{
u_char pkt[HONEYD_MTU + 40]; /* XXX - Enough? */
struct interface *inter = req->inter;
struct ip_hdr *ip = arg;
u_int len, iplen = ntohs(ip->ip_len);
eth_pack_hdr(pkt,
req->ha.addr_eth, /* destination */
req->src_ha.addr_eth, /* source */
ETH_TYPE_IP);
len = ETH_HDR_LEN + iplen;
if (sizeof(pkt) < len) {
syslog(LOG_WARNING, "%s: IP packet is larger than buffer: %d",
__func__, len);
goto out;
}
memcpy(pkt + ETH_HDR_LEN, ip, iplen);
if (eth_send(inter->if_eth, pkt, len) != len) {
syslog(LOG_ERR, "%s: couldn't send packet size %d: %m",
__func__, len);
} else {
count_increment(stats_network.output_bytes, iplen);
}
out:
pool_free(pool_pkt, ip);
}
/*
* Delivers an IP packet to a specific interface.
* Generates ARP request if necessary.
*/
void
honeyd_deliver_ethernet(struct interface *inter,
struct addr *src_pa, struct addr *src_ha,
struct addr *dst_pa, struct ip_hdr *ip, u_int iplen)
{
struct arp_req *req;
ip_checksum(ip, iplen);
/* Ethernet delivery if possible */
if ((req = arp_find(dst_pa)) == NULL) {
arp_request(inter, src_pa, src_ha, dst_pa, honeyd_ether_cb,ip);
} else if (req->cnt == -1) {
/*
* The source MAC of the original requestor does not help
* us here, but we can overwrite it with the MAC of this
* honeypot without causing any harm.
*/
req->src_ha = *src_ha;
honeyd_ether_cb(req, 1, ip);
} else {
/*
* Fall through in case that this packet needs
* to be dropped.
*/
pool_free(pool_pkt, ip);
}
}
/*
* Makes sure that we end up owning the memory referenced by
* the delay descriptor. We either tell to not free the
* memory or just make our own copy.
*/
struct ip_hdr *
honeyd_delay_own_memory(struct delay *delay, struct ip_hdr *ip, u_int iplen)
{
/* If we are not supposed to free the buffer then we do not own it */
if (!(delay->flags & DELAY_FREEPKT)) {
void *tmp = pool_alloc(pool_pkt);
memcpy(tmp, ip, iplen);
ip = tmp;
} else {
/*
* We are handling the memory ourselves: if we
* delegate the memory to the ARP handler, it will get
* freed later.
*/
delay->flags &= ~DELAY_FREEPKT;
}
return (ip);
}
/*
* This function delivers the actual packet to the network.
* It supports internal delivery, external delivery via ip_send
* and external delivery via ethernet encapsulation.
*
* This function handles the following cases:
* - TTL is 0: send ICMP time exceeded in transit message
* - External: Packet is delivered to the real network
* - Tunnel: Packet is GRE encapsulated and sent to a remote location
* - Ethernet: A physical machine has been integrate into the virtual
* routing topology and we need to ethernet encapsulate the packet.
* - Arp: The destination machine is configured to be on the physical link,
* so arp for it and ethernet encapsulate the packet.
* - Everything else: The packet is delivered internally after potential
* fragment reassembly.
*
* It needs to unreference the passed template value.
*/
static __inline void
honeyd_send_normally(struct ip_hdr *ip, u_int iplen)
{
ip_checksum(ip, iplen);
if (ip_send(honeyd_ip, ip, iplen) != iplen) {
int level = LOG_ERR;
if (errno == EHOSTDOWN || errno == EHOSTUNREACH)
level = LOG_DEBUG;
syslog(level, "couldn't send packet: %m");
} else {
count_increment(stats_network.output_bytes, iplen);
}
}
void
honeyd_delay_cb(int fd, short which, void *arg)
{
struct delay *delay = arg;
struct ip_hdr *ip = delay->ip;
struct template *tmpl = delay->tmpl;
u_int iplen = delay->iplen;
if (!ip->ip_ttl) {
/* Fix up TTL */
ip->ip_ttl++;
ip_checksum(ip, ip->ip_hl << 2);
icmp_error_send(tmpl, &delay->src,
ICMP_TIMEXCEED, ICMP_TIMEXCEED_INTRANS, ip, delay->spoof);
} else if (delay->flags & DELAY_UNREACH) {
/* Fix up TTL */
ip->ip_ttl++;
ip_checksum(ip, ip->ip_hl << 2);
icmp_error_send(tmpl, &delay->src,
ICMP_UNREACH, ICMP_UNREACH_NET, ip, delay->spoof);
} else if (delay->flags & DELAY_EXTERNAL) {
struct addr dst;
addr_pack(&dst, ADDR_TYPE_IP, IP_ADDR_BITS,
&ip->ip_dst, IP_ADDR_LEN);
/* This is the source template */
if (tmpl != NULL && tmpl->ethernet_addr != NULL &&
interface_find_responsible(&dst) == tmpl->inter) {
struct addr src;
/* To do ARP, we need to know all this information */
addr_pack(&src, ADDR_TYPE_IP, IP_ADDR_BITS,
&ip->ip_src, IP_ADDR_LEN);
ip = honeyd_delay_own_memory(delay, ip, iplen);
/* This function computes the IP checksum for us */
honeyd_deliver_ethernet(tmpl->inter,
&src, tmpl->ethernet_addr,
&dst, ip, iplen);
} else {
honeyd_send_normally(ip, iplen);
}
} else if (delay->flags & DELAY_TUNNEL) {
ip_checksum(ip, iplen);
if (gre_encapsulate(honeyd_ip, &delay->src, &delay->dst,
ip, iplen) == -1)
syslog(LOG_ERR, "couldn't GRE encapsulate packet: %m");
else
count_increment(stats_network.output_bytes, iplen);
} else if (delay->flags & DELAY_ETHERNET) {
extern struct network *reverse;
struct interface *inter = tmpl->inter;
struct router *router;
struct addr addr;
/*
* If a physical honeypot has been integrated into the
* virtual routing topology, we need to find the
* corresponding router.
*/
addr_pack(&addr, ADDR_TYPE_IP, IP_ADDR_BITS,
&ip->ip_dst, IP_ADDR_LEN);
router = network_lookup(reverse, &addr);
if (router == NULL)
errx(1, "%s: bad configuration", __func__);
/*
* If we are routing for an external sender, then we
* might have to copy the packet into an allocated
* buffer.
*/
ip = honeyd_delay_own_memory(delay, ip, iplen);
/* This function computes the IP checksum for us */
honeyd_deliver_ethernet(inter,
&router->addr, &inter->if_ent.intf_link_addr,
&addr, ip, iplen);
} else {
struct addr addr;
uint16_t ipoff;
template_free(tmpl);
addr_pack(&addr, ADDR_TYPE_IP, IP_ADDR_BITS,
&ip->ip_dst, IP_ADDR_LEN);
/* Internal delivery */
tmpl = template_find_best(addr_ntoa(&addr), ip, iplen);
tmpl = template_ref(tmpl);
/* Check for fragmentation */
ipoff = ntohs(ip->ip_off);
if ((ipoff & IP_OFFMASK) || (ipoff & IP_MF)) {
struct ip_hdr *nip;
u_short niplen;
if (ip_fragment(tmpl, ip, iplen, &nip, &niplen) == 0)
honeyd_dispatch(tmpl, nip, niplen);
} else
honeyd_dispatch(tmpl, ip, iplen);
}
if (delay->flags & DELAY_FREEPKT)
pool_free(pool_pkt, ip);
template_free(tmpl);
if (delay->flags & DELAY_NEEDFREE)
pool_free(pool_delay, delay);
}
/*
* Delays a packet for a specified amount of ms to simulate routing delay.
* Host is used for the router that might generate a XCEED message.
*/
void
honeyd_delay_packet(struct template *tmpl, struct ip_hdr *ip, u_int iplen,
const struct addr *src, const struct addr *dst, int ms, int flags,
struct spoof spoof)
{
struct delay *delay, tmp_delay;
struct timeval tv;
if (ms) {
delay = pool_alloc(pool_delay);
flags |= DELAY_NEEDFREE;
/*
* If the IP packet is not allocated separately, we
* need to allocate it here.
*/
if ((flags & DELAY_FREEPKT) == 0) {
void *tmp;
if (iplen < HONEYD_MTU)
tmp = pool_alloc(pool_pkt);
else
tmp = pool_alloc_size(pool_pkt, iplen);
memcpy(tmp, ip, iplen);
ip = tmp;
flags |= DELAY_FREEPKT;
}
} else {
memset(&tmp_delay, 0, sizeof(tmp_delay));
delay = &tmp_delay;
}
delay->ip = ip;
delay->iplen = iplen;
if (src != NULL)
delay->src = *src;
if (dst != NULL)
delay->dst = *dst;
delay->tmpl = template_ref(tmpl);
delay->flags = flags;
delay->spoof = spoof;
if (ms) {
evtimer_set(&delay->timeout, honeyd_delay_callback, delay);
timerclear(&tv);
tv.tv_sec = ms / 1000;
tv.tv_usec = (ms % 1000) * 1000;
evtimer_add(&delay->timeout, &tv);
} else
honeyd_delay_callback(-1, EV_TIMEOUT, delay);
}
/*
* This function allows us to deliver packets to virtual hosts as well
* as to external hosts. If virtual routing topologies are enabled,
* characteristics like packet loss, latency and ttl decrements are
* taken into consideration.
*/
void
honeyd_ip_send(u_char *pkt, u_int iplen, struct spoof spoof)
{
struct template *tmpl = NULL;
struct ip_hdr *ip = (struct ip_hdr *)pkt;
enum forward res = FW_EXTERNAL;
int delay = 0, flags = 0;
struct addr addr, src;
print_spoof("honeyd_ip_send", spoof);
if (iplen > HONEYD_MTU) {
u_short off = ntohs(ip->ip_off);
if ((off & IP_DF) == 0)
ip_send_fragments(HONEYD_MTU, ip, iplen, spoof);
goto drop;
}
if (spoof.new_dst.addr_type != ADDR_TYPE_NONE)
ip->ip_dst = spoof.new_dst.addr_ip;
addr_pack(&addr, ADDR_TYPE_IP, IP_ADDR_BITS, &ip->ip_dst, IP_ADDR_LEN);
addr_pack(&src, ADDR_TYPE_IP, IP_ADDR_BITS, &ip->ip_src, IP_ADDR_LEN);
/* Find the template for the external address */
tmpl = template_find_best(addr_ntoa(&addr), ip, iplen);
if (tmpl != NULL && tmpl->flags & TEMPLATE_EXTERNAL)
flags |= DELAY_ETHERNET;
/* But all sending decisions are really based on the source template */
tmpl = template_find_best(addr_ntoa(&src), ip, iplen);
if (router_used) {
extern struct network *reverse;
struct router *router;
router = network_lookup(reverse, &src);
if (router == NULL) {
syslog(LOG_NOTICE, "No reverse routing map for %s",
addr_ntoa(&src));
goto drop;
}
/*
* If the router itself is sending the packet, the first
* routing table lookup does not decrease the ttl.
*/
if (addr_cmp(&src, &router->addr) == 0)
ip->ip_ttl++; /* XXX - Ugly hack */
if (spoof.new_src.addr_type != ADDR_TYPE_NONE)
ip->ip_src = spoof.new_src.addr_ip;
res = honeyd_route_packet(ip, iplen, &router->addr, &addr,
&delay);
if (res == FW_DROP)
goto drop;
}
/* Remember that the packet buffer has to be freed at the end */
flags |= DELAY_FREEPKT;
if (res == FW_EXTERNAL)
flags |= DELAY_EXTERNAL;
if (spoof.new_src.addr_type != ADDR_TYPE_NONE)
ip->ip_src = spoof.new_src.addr_ip;
if (spoof.new_dst.addr_type != ADDR_TYPE_NONE)
ip->ip_dst = spoof.new_dst.addr_ip;
/* Delay the packet if necessary, otherwise deliver it directly */
honeyd_delay_packet(tmpl, ip, iplen, NULL, NULL, delay, flags, spoof);
return;
drop:
/* Deallocate the packet */
pool_free(pool_pkt, pkt);
}
static void
connection_insert(struct tree *tree, struct conlru *head, struct tuple *hdr)
{
SPLAY_INSERT(tree, tree, hdr);
TAILQ_INSERT_HEAD(head, hdr, next);
}
static void
connection_remove(struct tree *tree, struct conlru *head, struct tuple *hdr)
{
SPLAY_REMOVE(tree, tree, hdr);
TAILQ_REMOVE(head, hdr, next);
evtimer_del(&hdr->timeout);
}
/* Called when a connection received data and has not been idle */
static void
connection_update(struct conlru *head, struct tuple *hdr)
{
TAILQ_REMOVE(head, hdr, next);
TAILQ_INSERT_HEAD(head, hdr, next);
generic_timeout(&hdr->timeout, HONEYD_IDLE_TIMEOUT);
}
struct tcp_con *
tcp_new(struct ip_hdr *ip, struct tcp_hdr *tcp, int local)
{
struct tcp_con *con;
if (honeyd_nconnects >= HONEYD_MAX_CONNECTS) {
/*
* We seem to be in an overload situation - remove the
* oldest connection available.
*/
con = (struct tcp_con *)TAILQ_LAST(&tcplru, conlru);
tcp_free(con);
}
if ((con = calloc(1, sizeof(struct tcp_con))) == NULL) {
syslog(LOG_WARNING, "calloc: %m");
return (NULL);
}
honeyd_nconnects++;
honeyd_settcp(con, ip, tcp, local);
evtimer_set(&con->conhdr.timeout, honeyd_tcp_timeout, con);
evtimer_set(&con->retrans_timeout, tcp_retrans_timeout, con);
connection_insert(&tcpcons, &tcplru, &con->conhdr);
honeyd_log_flownew(honeyd_logfp, IP_PROTO_TCP, &con->conhdr);
return (con);
}
void
tcp_free(struct tcp_con *con)
{
struct port *port = con->port;
struct port_encapsulate *pending = con->conhdr.pending;
if (pending != NULL)
port_encapsulation_free(pending);
if (port != NULL)
port_free(port->subtmpl, port);
connection_remove(&tcpcons, &tcplru, &con->conhdr);
hooks_dispatch(IP_PROTO_TCP, HD_INCOMING_STREAM, &con->conhdr,
NULL, 0);
honeyd_log_flowend(honeyd_logfp, IP_PROTO_TCP, &con->conhdr);
evtimer_del(&con->retrans_timeout);
if (con->cmd_pfd > 0)
cmd_free(&con->cmd);
if (con->payload != NULL)
free(con->payload);
if (con->readbuf != NULL)
free(con->readbuf);
if (con->tmpl != NULL)
template_free(con->tmpl);
honeyd_nconnects--;
free(con);
}
void
tcp_retrans_timeout(int fd, short event, void *arg)
{
struct tcp_con *con = arg;
/* Restart transmitting from the last acknowledged segment */
con->poff = 0;
con->retrans_time *= 2;
/* Upper bound on the retransmit time */
if (con->retrans_time >= 60) {
tcp_free(con);
return;
}
switch (con->state) {
case TCP_STATE_SYN_SENT:
con->snd_una--;
tcp_send(con, TH_SYN, NULL, 0);
con->snd_una++;
generic_timeout(&con->retrans_timeout, con->retrans_time);
break;
case TCP_STATE_SYN_RECEIVED:
con->snd_una--;
tcp_send(con, TH_SYN|TH_ACK, NULL, 0);
con->snd_una++;
generic_timeout(&con->retrans_timeout, con->retrans_time);
break;
default:
/* Will reschedule retransmit timeout if needed */
tcp_senddata(con, TH_ACK);
break;
}
}
struct udp_con *
udp_new(struct ip_hdr *ip, struct udp_hdr *udp, int local)
{
struct udp_con *con;
if ((con = calloc(1, sizeof(struct udp_con))) == NULL) {
syslog(LOG_WARNING, "calloc: %m");
return (NULL);
}
honeyd_setudp(con, ip, udp, local);
connection_insert(&udpcons, &udplru, &con->conhdr);
evtimer_set(&con->conhdr.timeout, honeyd_udp_timeout, con);
honeyd_log_flownew(honeyd_logfp, IP_PROTO_UDP, &con->conhdr);
return (con);
}
void
udp_free(struct udp_con *con)
{
struct conbuffer *buf;
struct port *port = con->port;
struct port_encapsulate *pending = con->conhdr.pending;
if (pending != NULL)
port_encapsulation_free(pending);
if (port != NULL)
port_free(port->subtmpl, port);
connection_remove(&udpcons, &udplru, &con->conhdr);
hooks_dispatch(IP_PROTO_TCP, HD_INCOMING_STREAM, &con->conhdr,
NULL, 0);
honeyd_log_flowend(honeyd_logfp, IP_PROTO_UDP, &con->conhdr);
while ((buf = TAILQ_FIRST(&con->incoming)) != NULL) {
TAILQ_REMOVE(&con->incoming, buf, next);
free(buf->buf);
free(buf);
}
if (con->cmd_pfd > 0)
cmd_free(&con->cmd);
if (con->tmpl != NULL)
template_free(con->tmpl);
free(con);
}
void
honeyd_tcp_timeout(int fd, short event, void *arg)
{
struct tcp_con *con = arg;
syslog(LOG_DEBUG, "Expiring TCP %s (%p) in state %d",
honeyd_contoa(&con->conhdr), con, con->state);
tcp_free(con);
}
void
honeyd_udp_timeout(int fd, short event, void *arg)
{
struct udp_con *con = arg;
syslog(LOG_DEBUG, "Expiring UDP %s (%p)",
honeyd_contoa(&con->conhdr), con);
udp_free(con);
}
struct action *
honeyd_protocol(struct template *tmpl, int proto)
{
switch (proto) {
case IP_PROTO_TCP:
return (&tmpl->tcp);
case IP_PROTO_UDP:
return (&tmpl->udp);
case IP_PROTO_ICMP:
return (&tmpl->icmp);
default:
return (NULL);
}
}
int
honeyd_block(struct template *tmpl, int proto, int number)
{
struct port *port;
struct action *action;
if (tmpl == NULL)
return (0);
port = port_find(tmpl, proto, number);
if (port == NULL)
action = honeyd_protocol(tmpl, proto);
else
action = &port->action;
return (action->status == PORT_BLOCK);
}
void
honeyd_varexpand(struct tcp_con *con, char *line, u_int linesize)
{
char asc[32], *p;
struct addr src, dst;
addr_pack(&dst, ADDR_TYPE_IP, IP_ADDR_BITS, &con->con_ipdst, IP_ADDR_LEN);
addr_pack(&src, ADDR_TYPE_IP, IP_ADDR_BITS, &con->con_ipsrc, IP_ADDR_LEN);
/* Do some simple replacements */
p = addr_ntoa(&src);
while (strrpl(line, linesize, "$ipsrc", p) != NULL)
;
p = addr_ntoa(&dst);
while (strrpl(line, linesize, "$ipdst", p) != NULL)
;
p = honeyd_logdate();
while (strrpl(line, linesize, "$date", p) != NULL)
;
snprintf(asc, sizeof(asc), "%d", con->con_sport);
while (strrpl(line, linesize, "$sport", asc) != NULL)
;
snprintf(asc, sizeof(asc), "%d", con->con_dport);
while (strrpl(line, linesize, "$dport", asc) != NULL)
;
}
/*
* Returns the configuration of a particular port by looking
* at the default template of connections.
*/
struct action *
honeyd_port(struct template *tmpl, int proto, u_short number)
{
struct port *port;
struct action *action;
if (tmpl == NULL)
return (NULL);
port = port_find(tmpl, proto, number);
if (port == NULL)
action = honeyd_protocol(tmpl, proto);
else
action = &port->action;
return (action);
}
/*
* Create a proxy connection, either use precomputed addrinfo or
* generate correct address information.
*/
int
proxy_connect(struct tuple *hdr, struct command *cmd, struct addrinfo *ai,
char *line, void *arg)
{
int res;
/* Check if the address has been resolved for us already */
if (ai == NULL) {
char *name, *strport = line;
u_short nport;
name = strsep(&strport, ":");
if (strport == NULL || (nport = atoi(strport)) == 0)
return (-1);
if ((ai = cmd_proxy_getinfo(name, hdr->type, nport)) == NULL)
return (-1);
res = cmd_proxy_connect(hdr, cmd, ai, arg);
freeaddrinfo(ai);
} else
res = cmd_proxy_connect(hdr, cmd, ai, arg);
return (res);
}
/* Cleans up receive and send buffers if cmd does not start */
void
tcp_connectfail(struct tcp_con *con)
{
if (con->payload) {
free(con->payload);
con->payload = NULL;
}
if (con->readbuf) {
free(con->readbuf);
con->readbuf = NULL;
}
}
/* Sets up buffers for a fully connected TCP connection */
int
tcp_setupconnect(struct tcp_con *con)
{
struct tuple *hdr = &con->conhdr;
/* Allocate buffers */
if ((con->payload = malloc(TCP_DEFAULT_SIZE)) == NULL) {
syslog(LOG_WARNING, "malloc %s: %m", honeyd_contoa(hdr));
goto err;
}
if ((con->readbuf = malloc(TCP_DEFAULT_SIZE)) == NULL) {
syslog(LOG_WARNING, "malloc %s: %m", honeyd_contoa(hdr));
goto err;
}
con->psize = TCP_DEFAULT_SIZE;
con->rsize = TCP_DEFAULT_SIZE;
return (0);
err:
tcp_connectfail(con);
return (-1);
}
void
generic_connect(struct template *tmpl, struct tuple *hdr,
struct command *cmd, void *con)
{
char line[512], command[512];
char *argv[32], *p, *p2;
struct action *action = NULL;
struct port *port;
int proto = 0;
int i;
if (hdr->type == SOCK_STREAM)
proto = IP_PROTO_TCP;
else
proto = IP_PROTO_UDP;
if (tmpl == NULL)
goto out;
if ((port = port_find(tmpl, proto, hdr->dport)) == NULL) {
/* We need to use the default action for the protocol */
action = proto == IP_PROTO_TCP ? &tmpl->tcp : &tmpl->udp;
} else
action = &port->action;
if (action->status == PORT_OPEN) {
if (action->action == NULL || strlen(action->action) == 0)
goto out;
}
if (proto == IP_PROTO_TCP && tcp_setupconnect(con) == -1)
goto out;
/* Connect to the already started sub system */
if (action->status == PORT_SUBSYSTEM) {
if (cmd_subsystem_schedule_connect(hdr, cmd, port, con) == -1)
goto out;
return;
} else if (action->status == PORT_PYTHON) {
#ifdef HAVE_PYTHON
if (pyextend_connection_start(hdr, cmd, con,
action->action_extend) == -1)
goto out;
return;
#endif
}
/* 3-way handshake has been completed */
if (proto == IP_PROTO_TCP && action->status == PORT_RESERVED) {
if (cmd_subsystem_localconnect(hdr, cmd, port, con) == -1)
goto err;
return;
}
if (action->status == PORT_OPEN || action->aitop == NULL) {
assert(action->action != NULL && strlen(action->action));
strlcpy(line, action->action, sizeof(line));
honeyd_varexpand(con, line, sizeof(line));
/* Copy for print out */
strlcpy(command, line, sizeof(line));
}
/* Setup a proxy connection, no need to fork a new process */
if (action->status == PORT_PROXY) {
int res;
res = proxy_connect(hdr, cmd, action->aitop, line, con);
if (res == -1)
goto out;
return;
}
/* Create arguments */
p2 = line;
for (i = 0; i < sizeof(argv)/sizeof(char *) - 1; i++) {
if ((p = strsep(&p2, " ")) == NULL)
break;
if (strlen(p) == 0) {
i--;
continue;
}
argv[i] = p;
}
argv[i] = NULL;
if (cmd_fork(hdr, cmd, tmpl, argv[0], argv, con) == -1) {
syslog(LOG_WARNING, "malloc %s: %m", honeyd_contoa(hdr));
goto err;
}
syslog(LOG_DEBUG, "Connection established: %s %s <-> %s",
proto == IP_PROTO_TCP ? "tcp" : "udp",
honeyd_contoa(hdr), command);
return;
err:
if (proto == IP_PROTO_TCP)
tcp_connectfail(con);
out:
syslog(LOG_DEBUG, "Connection established: %s %s",
proto == IP_PROTO_TCP ? "tcp" : "udp",
honeyd_contoa(hdr));
}
int
tcp_send(struct tcp_con *con, uint8_t flags, u_char *payload, u_int len)
{
u_char *pkt;
struct tcp_hdr *tcp;
u_int iplen;
int window = 16000;
int dontfragment = 0;
char *options;
uint16_t id = rand_uint16(honeyd_rand);
struct spoof spoof;
struct template *tmpl = con->tmpl;
if (con->window)
window = con->window;
/*
* The TCP personality will always set snd_una for us if necessary.
* snd_una maybe 0 on RST segments.
*/
if (tcp_personality(con, &flags, &window, &dontfragment, &id,
&options) == -1) {
/*
* If we do not match a personality and sent a reset
* segment then we do not want to include options.
*/
if (flags & TH_RST) {
options = NULL;
window = con->window;
} else if (flags & TH_SYN) {
options = "m";
}
}
/* Empty flags indicates packet drop */
if (flags == 0)
return (0);
if (con->flags & TCP_TARPIT)
window = 5;
if ((flags & TH_SYN) && !con->window)
con->window = window;
/* Simple window tracking */
if (window && con->rlen) {
window -= con->rlen;
if (window < 0)
window = 0;
}
pkt = pool_alloc(pool_pkt);
tcp = (struct tcp_hdr *)(pkt + IP_HDR_LEN);
tcp_pack_hdr(tcp,
con->con_dport, con->con_sport,
con->snd_una, con->rcv_next, flags, window, 0);
/* ET - options is non-NULL if a personality was found. If a
* personality was found, it means that this packet is a response
* to an NMAP TCP test (not a Sequence number test, a weird flags test).
* Therefore if options is not NULL, you have to add the options to
* the response otherwise the reply packet will not have the complete
* personality. Of the seven NMAP TCP tests, only a couple may
* return a packet with the SYN flag. I needed to remove the
* requirement of the SYN flag so that the other NMAP TCP tests would
* have the personality TCP options. */
if (options != NULL)
tcp_personality_options(con, tcp, options);
iplen = IP_HDR_LEN + (tcp->th_off << 2) + len;
if (tmpl != NULL)
spoof = tmpl->spoof;
else
spoof = no_spoof;
/* Src and Dst are reversed both for ip and tcp */
ip_pack_hdr(pkt, 0, iplen, id,
dontfragment ? IP_DF : 0, honeyd_ttl,
IP_PROTO_TCP, con->con_ipdst, con->con_ipsrc);
memcpy(pkt + IP_HDR_LEN + (tcp->th_off << 2), payload, len);
hooks_dispatch(IP_PROTO_TCP, HD_OUTGOING, &con->conhdr,
pkt, iplen);
honeyd_ip_send(pkt, iplen, spoof);
return (len);
}
void
tcp_senddata(struct tcp_con *con, uint8_t flags)
{
int space, sent;
int needretrans = 0;
do {
space = TCP_MAX_INFLIGHT - TCP_BYTESINFLIGHT(con);
if (space > TCP_MAX_SEND)
space = TCP_MAX_SEND;
if (con->plen - con->poff < space)
space = con->plen - con->poff;
/* Reduce the amount of data that we can send */
if (space && (con->flags & TCP_TARPIT))
space = 1;
if (con->sentfin && !con->finacked)
flags |= TH_FIN;
if (con->plen > space)
flags &= ~TH_FIN;
/*
* If we do not ack new data, and have nothing to send,
* and do not need to send a FIN, stop sending.
*/
if (space == 0 && con->last_acked == con->rcv_next &&
!(flags & TH_FIN))
break;
con->snd_una += con->poff;
sent = tcp_send(con, flags, con->payload + con->poff, space);
con->snd_una -= con->poff;
con->poff += sent;
/* Statistics */
con->conhdr.sent += space;
if (flags & TH_ACK)
con->last_acked = con->rcv_next;
if (con->flags & TCP_TARPIT)
break;
} while (sent && !con->dupacks);
/*
* We need to retransmit if we still have outstanding data or
* our FIN did not get acked.
*/
needretrans = con->poff || (con->sentfin && !con->finacked);
if (needretrans && !evtimer_pending(&con->retrans_timeout, NULL)) {
if (!con->retrans_time)
con->retrans_time = 1;
generic_timeout(&con->retrans_timeout, con->retrans_time);
}
}
void
tcp_sendfin(struct tcp_con *con)
{
con->sentfin = 1;
tcp_senddata(con, TH_ACK);
switch (con->state) {
case TCP_STATE_ESTABLISHED:
con->state = TCP_STATE_FIN_WAIT_1;
break;
case TCP_STATE_CLOSE_WAIT:
con->state = TCP_STATE_CLOSING;
break;
}
}
void
icmp_send(struct template *tmpl,
u_char *pkt, uint8_t tos, u_int iplen, uint16_t df, uint8_t ttl,
int proto, ip_addr_t src, ip_addr_t dst, struct spoof spoof)
{
struct ip_hdr ip;
uint16_t ipid;
/* Fake up IP hdr */
ip.ip_src = dst;
ip.ip_dst = src;
ip.ip_hl = sizeof(ip) >> 2;
ip.ip_len = 0;
if (tmpl != NULL)
ip_personality(tmpl, &ipid);
else
ipid = rand_uint16(honeyd_rand);
ip_pack_hdr(pkt, tos, iplen, ipid, df ? IP_DF: 0, ttl,
IP_PROTO_ICMP, src, dst);
honeyd_ip_send(pkt, iplen, spoof);
}
void
icmp_error_send(struct template *tmpl, struct addr *addr,
uint8_t type, uint8_t code, struct ip_hdr *rip, struct spoof spoof)
{
u_char *pkt;
u_int iplen;
uint8_t tos = 0, df = 0, ttl = honeyd_ttl;
int quotelen, riplen;
quotelen = 40;
if (!icmp_error_personality(tmpl, addr, rip, &df, &tos, "elen, &ttl))
return;
riplen = ntohs(rip->ip_len);
if (riplen < quotelen)
quotelen = riplen;
iplen = IP_HDR_LEN + ICMP_HDR_LEN + 4 + quotelen;
pkt = pool_alloc(pool_pkt);
icmp_pack_hdr_quote(pkt + IP_HDR_LEN, type, code, 0, rip, quotelen);
icmp_send(tmpl, pkt, tos, iplen, df ? IP_DF: 0, ttl,
IP_PROTO_ICMP, addr->addr_ip, rip->ip_src, spoof);
}
/*
* icmp_echo_reply
* rip should be the ip_header pointing to an actual raw
* packet (has payload in it so icmp can be extracted)
*
* This function changes the IP and ICMP header data (i.e.
* the ICMP packet and its IP header) to match the OS you want.
*
* The code, ipid, tos, offset, and ttl parameters should
* probably move inside this function so that the icmp_personality
* can have control over them.
*
* We should create a structure that includes all possible ICMP
* fields and just pass in that structure into icmp_personality
* function and have a flag to indicate what ICMP message it is
* and what parameters need to set to what value depend on the
* OS we need to emulate.
* e.g.
if (!icmp_personality(addr, rip, &ICMP_STRUCT)
return;
*
* param rip The raw ip packet in IP header form
* param code ICMP Header REPLY echo code, OS dependent, 0 or !0
* param ipid IP Header id, 0 or !0(RANDOM)
* param tos IP Header type of service, 0 or !0(0xc0)
* param offset IP Header DF bit and offset, 0 or 1(IP_DF)
* param ttl IP header time to live, <65, <129, or <256
* param payload ICMP Echo request payload, should not be null, use by
* ping programs to determine RTT
* param len Length of the payload to return
*/
void
icmp_echo_reply(struct template *tmpl,
struct ip_hdr *rip, uint8_t code, uint8_t tos,
uint16_t offset, uint8_t ttl, u_char *payload, u_int len, struct spoof spoof)
{
u_char *pkt;
u_int iplen;
struct icmp_msg_echo *icmp_echo;
icmp_echo = (struct icmp_msg_echo *) ((u_char *)rip + (rip->ip_hl << 2) + ICMP_HDR_LEN);
iplen = IP_HDR_LEN + ICMP_HDR_LEN + 4 + len;
if (iplen < HONEYD_MTU)
pkt = pool_alloc(pool_pkt);
else
pkt = pool_alloc_size(pool_pkt, iplen);
icmp_pack_hdr_echo(pkt + IP_HDR_LEN, ICMP_ECHOREPLY,
code, ntohs(icmp_echo->icmp_id), ntohs(icmp_echo->icmp_seq),
payload, len);
icmp_send(tmpl, pkt, tos, iplen, offset, ttl,
IP_PROTO_ICMP, rip->ip_dst, rip->ip_src, spoof);
}
/*
* We should create a structure that includes all possible ICMP
* fields and just pass in that structure into icmp_personality
* function and have a flag to indicate what ICMP message it is
* and what parameters need to set to what value depend on the
* OS we need to emulate.
* e.g.
if (!icmp_personality(addr, rip, &ICMP_STRUCT)
return;
* ICMP_TIMESTAMP REPLY,
*
* param rip The raw ip packet in IP header form
* param icmp_rip icmp timestamp message, includes the
* icmp header.
* param ttl Time to live of the emulated OS (<65, <129, <256)
*/
void
icmp_timestamp_reply(struct template *tmpl, struct ip_hdr *rip,
struct icmp_msg_timestamp* icmp_rip, uint8_t ttl, struct spoof spoof)
{
u_char *pkt;
u_int iplen;
struct icmp_msg_timestamp icmp_time;
uint8_t padding = 6;
struct tm *now_tm;
time_t now;
uint32_t milliseconds;
pkt = pool_alloc(pool_pkt);
now = time(NULL);
now_tm = localtime(&now);
milliseconds = (now_tm->tm_hour * 60 * 60 +
now_tm->tm_min * 60 +
now_tm->tm_sec) * 1000;
icmp_time.hdr.icmp_type = ICMP_TSTAMPREPLY,
icmp_time.hdr.icmp_code = 0;
icmp_time.icmp_id = icmp_rip->icmp_id;
icmp_time.icmp_seq = icmp_rip->icmp_seq;
/* For now just do the following */
icmp_time.icmp_ts_orig = icmp_rip->icmp_ts_orig;
icmp_time.icmp_ts_rx = icmp_rip->icmp_ts_orig + milliseconds;
icmp_time.icmp_ts_tx = icmp_rip->icmp_ts_rx;
iplen = IP_HDR_LEN + ICMP_HDR_LEN + 16 + padding;
/* 6 bytes of 0 at the end, why? RedHat and Windows have 6 bytes of
* padding to this type of message. I don't know yet why they do this.
*/
memcpy(pkt + IP_HDR_LEN, &icmp_time, sizeof(icmp_time));
icmp_send(tmpl, pkt, rip->ip_tos, iplen, rip->ip_off, ttl,
IP_PROTO_ICMP, rip->ip_dst, rip->ip_src, spoof);
}
/*
* ICMP_MASK_REPLY.
* We should create a structure that includes all possible ICMP
* fields and just pass in that structure into icmp_personality
* function and have a flag to indicate what ICMP message it is
* and what parameters need to set to what value depend on the
* OS we need to emulate.
* e.g.
if (!icmp_personality(addr, rip, &ICMP_STRUCT)
return;
* param rip The raw ip packet in IP header form
* param idseq id and seq of the icmp header, should be same
* from the mask request icmp header.
* param ttl time to live of OS simulated (<65, <129, or <255)
* param mask mask of the emulated OS (i.e. 255.255.0.0)
*/
void
icmp_mask_reply(struct template *tmpl, struct ip_hdr *rip,
struct icmp_msg_idseq *idseq, uint8_t ttl, uint32_t addrmask, struct spoof spoof)
{
u_char *pkt;
u_int iplen;
struct icmp_mesg_mask mask;
pkt = pool_alloc(pool_pkt);
iplen = IP_HDR_LEN + ICMP_HDR_LEN + 8;
mask.hdr.icmp_type = ICMP_MASKREPLY;
mask.hdr.icmp_code = ICMP_CODE_NONE;
mask.icmp_id = idseq->icmp_id;
mask.icmp_seq = idseq->icmp_seq;
mask.icmp_mask = htonl(addrmask);
memcpy(pkt + IP_HDR_LEN, &mask, sizeof(mask));
icmp_send(tmpl, pkt, rip->ip_tos, iplen, rip->ip_off, ttl,
IP_PROTO_ICMP, rip->ip_dst, rip->ip_src, spoof);
}
/*
* We should create a structure that includes all possible ICMP
* fields and just pass in that structure into icmp_personality
* function and have a flag to indicate what ICMP message it is
* and what parameters need to set to what value depend on the
* OS we need to emulate.
* e.g.
if (!icmp_personality(addr, rip, &ICMP_STRUCT)
return;
* ICMP_INFO_REPLY
*
* param rip The raw ip packet in IP header form
* param idseq id and seq of the icmp header, should be same
* from the info request icmp header.
* param ttl Time to live of the emulated OS (<65, <129, <256)
*/
void
icmp_info_reply(struct template *tmpl, struct ip_hdr *rip,
struct icmp_msg_idseq *idseq, uint8_t ttl, struct spoof spoof)
{
u_char *pkt;
u_int iplen;
struct icmp_msg_inforeply inforeply;
pkt = pool_alloc(pool_pkt);
iplen = IP_HDR_LEN + ICMP_HDR_LEN + 4;
inforeply.hdr.icmp_type = ICMP_INFOREPLY;
inforeply.hdr.icmp_code = ICMP_CODE_NONE;
inforeply.idseq.icmp_id = idseq->icmp_id;
inforeply.idseq.icmp_seq = idseq->icmp_seq;
memcpy(pkt + IP_HDR_LEN, &inforeply, sizeof(inforeply));
icmp_send(tmpl, pkt, rip->ip_tos, iplen, rip->ip_off, ttl,
IP_PROTO_ICMP, rip->ip_dst, rip->ip_src, spoof);
}
void
tcp_do_options(struct tcp_con *con, struct tcp_hdr *tcp, int isonsyn)
{
u_char *p, *end;
p = (u_char *)(tcp + 1);
end = (u_char *)tcp + (tcp->th_off << 2);
while (p < end) {
struct tcp_opt opt, *tmp = (struct tcp_opt *)p;
if (tmp->opt_type == TCP_OPT_NOP) {
p++;
continue;
} else if (tmp->opt_type == TCP_OPT_EOL)
break;
if (p + tmp->opt_len > end)
break;
memcpy(&opt, tmp, tmp->opt_len);
switch (opt.opt_type) {
case TCP_OPT_MSS:
if (!isonsyn) {
con->mss = ntohs(opt.opt_data.mss);
}
break;
case TCP_OPT_WSCALE:
if (!isonsyn) {
con->sawwscale = 1;
}
break;
case TCP_OPT_TIMESTAMP:
con->sawtimestamp = 1;
con->echotimestamp = opt.opt_data.timestamp[0];
break;
default:
break;
}
p += opt.opt_len;
if (opt.opt_len < 1)
break;
}
}
void
generic_timeout(struct event *ev, int seconds)
{
struct timeval tv;
timerclear(&tv);
tv.tv_sec = seconds;
evtimer_add(ev, &tv);
}
/* Checks that the sequence number is where we expect it to be */
#define TCP_CHECK_SEQ_OR_ACK do { \
int has_ack = tcp->th_flags & TH_ACK; \
if (tcp->th_flags & TH_RST) { \
if (th_seq != con->rcv_next) \
goto drop; \
goto close; \
} \
if (!has_ack) \
goto drop; \
if (TCP_SEQ_LT(th_ack, con->snd_una)) { \
if (tcp->th_flags & TH_RST) \
goto drop; \
}\
/* Don't accept out of order data */ \
if (TCP_SEQ_GT(th_seq, con->rcv_next)) { \
if (has_ack) \
tcp_send(con, TH_ACK, NULL, 0); \
goto drop; \
} \
} while(0)
#define TCP_RECV_SEND_DATA do { \
/* Find new data: doff contains already acked data */ \
dlen = ntohs(ip->ip_len) - (ip->ip_hl * 4) -(tcp->th_off * 4);\
doff = con->rcv_next - th_seq; \
if (doff > dlen ||(doff == dlen && (tiflags & TH_FIN) == 0)) {\
/* Need to ACK this segments */ \
tiflags &= ~TH_FIN; \
doff = dlen; \
} \
dlen -= doff; \
\
con->conhdr.received += dlen; \
\
if (con->plen || con->cmd_pfd > 0) { \
int ackinc = 0; \
dlen = tcp_add_readbuf(con, data + doff, dlen); \
\
acked = th_ack - con->snd_una; \
if (acked > con->plen) { \
if (con->sentfin && acked == con->plen + 1){ \
con->finacked = 1; \
ackinc = 1; \
} \
acked = con->plen; \
} \
tcp_drain_payload(con, acked); \
acked += ackinc; \
if (con->cmd_pfd == -1 && con->plen <= TCP_MAX_SEND) \
con->sentfin = 1; \
} else if (con->cmd_pfd == -1) { \
tcp_add_readbuf(con, data + doff, dlen); \
} \
if (con->sentfin) { \
if (th_ack == con->snd_una + 1) { \
acked = 1; \
con->finacked = 1; \
} \
} \
if (acked == 0 && con->poff) { \
con->dupacks++; \
if (con->dupacks >= 3) { \
con->dupacks = 3; \
con->poff = 0; \
} \
} else if (acked) { \
con->retrans_time = 0; \
evtimer_del(&con->retrans_timeout); \
con->dupacks=0; \
} \
} while (0)
void
tcp_recv_cb(struct template *tmpl, u_char *pkt, u_short pktlen)
{
char *comment = NULL;
struct ip_hdr *ip;
struct tcp_hdr *tcp;
struct tcp_con *con;
struct action *action;
uint32_t th_seq, th_ack;
uint32_t acked = 0;
uint16_t th_sum;
u_char *data;
u_int dlen, doff;
uint8_t tiflags, flags;
ip = (struct ip_hdr *)pkt;
tcp = (struct tcp_hdr *)(pkt + (ip->ip_hl << 2));
data = (u_char *)(pkt + (ip->ip_hl*4) + (tcp->th_off*4));
if (pktlen < (ip->ip_hl << 2) + TCP_HDR_LEN)
return;
/*
* Check if we have a real connection header for this connection, so
* that we can look at potential flags like local origination.
*/
honeyd_settcp(&honeyd_tmp, ip, tcp, 0);
con = (struct tcp_con *)SPLAY_FIND(tree, &tcpcons, &honeyd_tmp.conhdr);
hooks_dispatch(ip->ip_p, HD_INCOMING,
con != NULL ? &con->conhdr : &honeyd_tmp.conhdr, pkt, pktlen);
if (honeyd_block(tmpl, IP_PROTO_TCP, ntohs(tcp->th_dport)))
goto justlog;
/* Check the checksum the brutal way, until libdnet supports */
th_sum = tcp->th_sum;
ip_checksum(ip, pktlen);
if (th_sum != tcp->th_sum)
goto justlog;
action = honeyd_port(tmpl, IP_PROTO_TCP, ntohs(tcp->th_dport));
honeyd_tmp.state = action == NULL || PORT_ISOPEN(action) ?
TCP_STATE_LISTEN : TCP_STATE_CLOSED;
tiflags = tcp->th_flags;
if (con == NULL) {
if (honeyd_tmp.state != TCP_STATE_LISTEN)
goto kill;
if ((tiflags & TH_SYN) == 0)
goto kill;
if (tiflags & ~TH_SYN &
(TH_FIN|TH_RST|TH_PUSH|TH_ACK|TH_URG)) {
int win = 0, df = 0;
uint16_t id;
flags = TH_SYN|TH_ACK;
if (tiflags & (TH_FIN|TH_RST))
comment = " {Honeyd Scanner?}";
/*
* Some stacks might reply to a packet like
* this. So, check the personalities and see
* what the flags say.
*/
if (tcp_personality(&honeyd_tmp,
&flags, &win, &df, &id, NULL) == -1) {
/*
* These flags normally cause a termination,
* so drop or reset the connection as we did
* not match a fingerprint.
*/
if (tiflags & (TH_RST|TH_ACK))
goto kill;
tiflags &= ~TH_FIN;
}
/* Just drop the packet */
if (flags & TH_RST)
goto kill;
}
syslog(LOG_DEBUG, "Connection request: tcp %s",
honeyd_contoa(&honeyd_tmp.conhdr));
/* Check if we should drop this SYN packet */
if (tmpl != NULL && tmpl->drop_synrate) {
uint16_t value;
value = rand_uint16(honeyd_rand) % (100*100);
if (value < tmpl->drop_synrate)
goto justlog;
}
/* Out of memory is dealt with by killing the connection */
if ((con = tcp_new(ip, tcp, 0)) == NULL) {
goto kill;
}
con->rcv_flags = tiflags;
/* Check if this connection is a tar pit */
if (action != NULL && (action->flags & PORT_TARPIT))
con->flags |= TCP_TARPIT;
tcp_do_options(con, tcp, 1);
con->tmpl = template_ref(tmpl);
con->rcv_next = ntohl(tcp->th_seq) + 1;
con->snd_una = 0;
con->state = TCP_STATE_LISTEN;
tcp_send(con, TH_SYN|TH_ACK, NULL, 0);
con->snd_una++;
con->state = TCP_STATE_SYN_RECEIVED;
generic_timeout(&con->conhdr.timeout, HONEYD_SYN_WAIT);
/* Get initial value from personality */
con->retrans_time = 3;
generic_timeout(&con->retrans_timeout, con->retrans_time);
return;
}
/*
* Subsystems can remove ports on the fly - even when parts of the
* 3-way handshake are in progress already.
*/
if (action != NULL) {
switch (action->status) {
case PORT_RESET:
goto dropwithreset;
case PORT_BLOCK:
goto drop;
default:
break;
}
}
th_seq = ntohl(tcp->th_seq);
th_ack = ntohl(tcp->th_ack);
con->rcv_flags = tiflags;
switch (con->state) {
case TCP_STATE_SYN_SENT:
if (tiflags & TH_RST)
goto close;
if (!(tiflags & TH_SYN))
goto drop;
if (!(tiflags & TH_ACK))
goto drop;
/* No simultaneous open allowed */
if (th_ack != con->snd_una)
goto dropwithreset;
tcp_do_options(con, tcp, 0);
con->rcv_next = th_seq + 1;
tcp_send(con, TH_ACK, NULL, 0);
con->state = TCP_STATE_ESTABLISHED;
generic_connect(tmpl, &con->conhdr, &con->cmd, con);
break;
case TCP_STATE_SYN_RECEIVED:
if (tiflags & TH_ACK) {
if (tiflags & TH_SYN)
goto dropwithreset;
if (th_ack != con->snd_una)
goto dropwithreset;
}
if (tiflags & TH_SYN) {
if (th_seq != con->rcv_next - 1)
goto dropwithreset;
con->snd_una--;
tcp_send(con, TH_SYN|TH_ACK, NULL,0);
con->snd_una++;
return;
}
if (tiflags & TH_RST)
goto close;
if (!(tiflags & TH_ACK))
goto drop;
tcp_do_options(con, tcp, 0);
/* Clear retransmit timeout */
con->retrans_time = 0;
evtimer_del(&con->retrans_timeout);
connection_update(&tcplru, &con->conhdr);
con->state = TCP_STATE_ESTABLISHED;
generic_connect(tmpl, &con->conhdr, &con->cmd, con);
break;
case TCP_STATE_ESTABLISHED:
TCP_CHECK_SEQ_OR_ACK;
TCP_RECV_SEND_DATA;
tcp_do_options(con, tcp, 0);
connection_update(&tcplru, &con->conhdr);
if (tiflags & TH_FIN && !(con->flags & TCP_TARPIT)) {
if (con->cmd_pfd > 0) {
if (con->rlen == 0) {
/*
* If we already transmitted all data,
* we can completely shutdown the write
* part of the connection.
*/
shutdown(con->cmd_pfd, SHUT_WR);
} else {
/*
* If we still have data to write to
* our child process, we need to delay
* the shutdown until later.
*/
con->cmd.fdgotfin = 1;
}
} else {
con->sentfin = 1;
}
con->state = TCP_STATE_CLOSE_WAIT;
dlen++;
}
con->rcv_next += dlen;
con->snd_una += acked;
if (con->sentfin) {
tcp_sendfin(con);
} else
tcp_senddata(con, TH_ACK);
break;
case TCP_STATE_CLOSE_WAIT:
TCP_CHECK_SEQ_OR_ACK;
TCP_RECV_SEND_DATA;
tcp_do_options(con, tcp, 0);
connection_update(&tcplru, &con->conhdr);
if (dlen)
goto dropwithreset;
con->snd_una += acked;
tcp_senddata(con, TH_ACK);
if (con->sentfin)
con->state = TCP_STATE_CLOSING;
break;
case TCP_STATE_CLOSING:
TCP_CHECK_SEQ_OR_ACK;
TCP_RECV_SEND_DATA;
tcp_do_options(con, tcp, 0);
connection_update(&tcplru, &con->conhdr);
con->snd_una += acked;
if (con->finacked)
goto closed;
tcp_senddata(con, TH_ACK);
break;
case TCP_STATE_FIN_WAIT_1:
TCP_CHECK_SEQ_OR_ACK;
TCP_RECV_SEND_DATA;
tcp_do_options(con, tcp, 0);
if (tiflags & TH_FIN && !(con->flags & TCP_TARPIT)) {
con->state = TCP_STATE_CLOSING;
generic_timeout(&con->conhdr.timeout,
HONEYD_CLOSE_WAIT);
dlen++;
} else {
connection_update(&tcplru, &con->conhdr);
}
con->rcv_next += dlen;
con->snd_una += acked;
tcp_senddata(con, TH_ACK);
break;
}
return;
kill:
honeyd_log_probe(honeyd_logfp, IP_PROTO_TCP, &honeyd_tmp.conhdr,
pktlen, tcp->th_flags, comment);
/* Do not kill on reset */
if (tiflags & TH_RST)
return;
syslog(LOG_DEBUG, "Killing %s connection: tcp %s",
(tcp->th_flags & TH_SYN) ? "attempted" : "unknown",
honeyd_contoa(&honeyd_tmp.conhdr));
/* Fake connection element */
honeyd_tmp.rcv_next = ntohl(tcp->th_seq) + 1;
honeyd_tmp.snd_una = ntohl(tcp->th_ack);
honeyd_tmp.tmpl = tmpl;
if (tiflags & TH_ACK)
flags = TH_RST;
else
flags = TH_RST | TH_ACK;
/*
* The TCP personality matches, all the sequence numbers are
* going to be taken care off via the Nmap fingerprint,
* otherwise, we are going to fill in reasonable defaults.
*/
if (tcp_personality_match(&honeyd_tmp, flags)) {
honeyd_tmp.rcv_next = ntohl(tcp->th_seq) + 1;
honeyd_tmp.snd_una = ntohl(tcp->th_ack);
} else if (tiflags & TH_ACK) {
honeyd_tmp.rcv_next = 0;
honeyd_tmp.snd_una = ntohl(tcp->th_ack);
} else {
flags = TH_RST | TH_ACK;
honeyd_tmp.rcv_next = ntohl(tcp->th_seq) + 1;
honeyd_tmp.snd_una = 0;
}
/*
* Even though options processing does not make any sense on
* RST segment, some stacks apparently do it anyway.
*/
tcp_do_options(&honeyd_tmp, tcp, 1);
tcp_send(&honeyd_tmp, flags, NULL, 0);
return;
close:
if (tiflags & TH_RST) {
syslog(LOG_DEBUG, "Connection dropped by reset: tcp %s",
honeyd_contoa(&con->conhdr));
}
goto free;
dropwithreset:
syslog(LOG_DEBUG, "Connection dropped with reset: tcp %s",
honeyd_contoa(&con->conhdr));
if ((tiflags & TH_RST) == 0)
tcp_send(con, TH_RST|TH_ACK, NULL, 0);
free:
tcp_free(con);
return;
closed:
syslog(LOG_DEBUG, "Connection closed: tcp %s",
honeyd_contoa(&con->conhdr));
/* Forget about this connection */
tcp_free(con);
drop:
return;
justlog:
honeyd_settcp(&honeyd_tmp, ip, tcp, 0);
honeyd_log_probe(honeyd_logfp, IP_PROTO_TCP,&honeyd_tmp.conhdr,
pktlen, tcp->th_flags, comment);
}
int
udp_send(struct udp_con *con, u_char *payload, u_int len)
{
u_char *pkt;
struct udp_hdr *udp;
u_int iplen;
uint16_t id = rand_uint16(honeyd_rand);
int dontfragment = 0;
struct spoof spoof;
struct template *tmpl = con->tmpl;
/* Statistics */
con->conhdr.sent += len;
ip_personality(tmpl, &id);
pkt = pool_alloc(pool_pkt);
udp = (struct udp_hdr *)(pkt + IP_HDR_LEN);
udp_pack_hdr(udp, con->con_dport, con->con_sport, UDP_HDR_LEN + len);
iplen = IP_HDR_LEN + UDP_HDR_LEN + len;
/* Src and Dst are reversed both for ip and tcp */
ip_pack_hdr(pkt, 0, iplen, id,
dontfragment ? IP_DF : 0, honeyd_ttl,
IP_PROTO_UDP, con->con_ipdst, con->con_ipsrc);
memcpy(pkt + IP_HDR_LEN + UDP_HDR_LEN, payload, len);
if (tmpl)
spoof = tmpl->spoof;
else
spoof = no_spoof;
ip_checksum(pkt, iplen);
hooks_dispatch(IP_PROTO_UDP, HD_OUTGOING, &con->conhdr,
pkt, iplen);
honeyd_ip_send(pkt, iplen, spoof);
generic_timeout(&con->conhdr.timeout, HONEYD_UDP_WAIT);
return (len);
}
void
udp_recv_cb(struct template *tmpl, u_char *pkt, u_short pktlen)
{
struct ip_hdr *ip = NULL;
struct udp_hdr *udp;
struct udp_con *con, honeyd_udp;
struct addr addr;
struct spoof spoof;
uint16_t uh_sum;
u_char *data;
u_int dlen;
u_short portnum;
ip = (struct ip_hdr *)pkt;
udp = (struct udp_hdr *)(pkt + (ip->ip_hl << 2));
if (pktlen < (ip->ip_hl << 2) + UDP_HDR_LEN)
return;
/*
* Check if we have a real connection header for this connection, so
* that we can look at potential flags like local origination.
*/
honeyd_setudp(&honeyd_udp, ip, udp, 0);
con = (struct udp_con *)SPLAY_FIND(tree, &udpcons, &honeyd_udp.conhdr);
hooks_dispatch(ip->ip_p, HD_INCOMING,
con != NULL ? &con->conhdr : &honeyd_udp.conhdr, pkt, pktlen);
data = (u_char *)(pkt + (ip->ip_hl*4) + UDP_HDR_LEN);
dlen = ntohs(ip->ip_len) - (ip->ip_hl << 2) - UDP_HDR_LEN;
if (dlen != (ntohs(udp->uh_ulen) - UDP_HDR_LEN))
return;
portnum = ntohs(udp->uh_dport);
if (honeyd_block(tmpl, IP_PROTO_UDP, portnum))
goto justlog;
/* Check the packet checksum, if no uh_sum is set, we ignore it */
uh_sum = udp->uh_sum;
ip_checksum(ip, pktlen);
if ((uh_sum && uh_sum != udp->uh_sum))
goto justlog;
if (con == NULL) {
struct action *action;
action = honeyd_port(tmpl, IP_PROTO_UDP, portnum);
/* Send unreachable on closed port */
if (action == NULL || !PORT_ISOPEN(action)) {
syslog(LOG_DEBUG, "Connection to closed port: udp %s",
honeyd_contoa(&honeyd_udp.conhdr));
goto closed;
}
/* Otherwise create a new udp connection */
syslog(LOG_DEBUG, "Connection: udp %s",
honeyd_contoa(&honeyd_udp.conhdr));
/* Out of memory is dealt by having the port closed */
if ((con = udp_new(ip, udp, 0)) == NULL) {
goto closed;
}
generic_connect(tmpl, &con->conhdr, &con->cmd, con);
}
/* Keep this state active */
generic_timeout(&con->conhdr.timeout, HONEYD_UDP_WAIT);
con->softerrors = 0;
/* Statistics */
con->conhdr.received += dlen;
/* Add the data to the incoming buffers */
udp_add_readbuf(con, data, dlen);
return;
closed:
honeyd_log_probe(honeyd_logfp, IP_PROTO_UDP, &honeyd_udp.conhdr,
pktlen, 0, NULL);
addr_pack(&addr, ADDR_TYPE_IP, IP_ADDR_BITS, &ip->ip_dst, IP_ADDR_LEN);
/*
* compute possible spoofed source for this error response to a UDP packet
*/
if (tmpl)
spoof = tmpl->spoof;
else
spoof = no_spoof;
// compute_spoof(&spoof, tmpl, &tmpl->spoof, ip->ip_src, ip->ip_dst);
print_spoof("udp_recv_cb after", spoof);
icmp_error_send(tmpl, &addr, ICMP_UNREACH, ICMP_UNREACH_PORT, ip, spoof);
return;
justlog:
honeyd_setudp(&honeyd_udp, ip, udp, 0);
honeyd_log_probe(honeyd_logfp, IP_PROTO_UDP, &honeyd_udp.conhdr,
pktlen, 0, NULL);
}
void
icmp_recv_cb(struct template *tmpl, u_char *pkt, u_short pktlen)
{
struct ip_hdr *ip = NULL;
struct icmp_hdr *icmp;
struct icmp_msg_quote *icmp_quote;
struct ip_hdr *rip, tmpip;
struct udp_hdr *udp, tmpudp;
struct udp_con *con, honeyd_udp;
/* YM - ICMP Messages */
struct icmp_msg_echo *icmp_echo;
struct icmp_msg_timestamp *icmp_tstamp;
struct icmp_msg_idseq *icmp_idseq;
struct xp_fingerprint *xp_print = NULL; /* JVR */
struct tuple icmphdr;
struct addr src, dst;
struct spoof spoof;
char asrc[100], adst[100];
char osrc[100], odst[100];
char ssrc[100], sdst[100];
u_char *dat;
uint16_t cksum;
int dlen;
ip = (struct ip_hdr *)pkt;
if (pktlen < (ip->ip_hl << 2) + ICMP_HDR_LEN)
return;
icmp = (struct icmp_hdr *)(pkt + (ip->ip_hl << 2));
icmphdr.local = 0;
icmphdr.ip_src = ip->ip_src;
icmphdr.ip_dst = ip->ip_dst;
icmphdr.type = SOCK_RAW;
icmphdr.sport = icmp->icmp_type; /* XXX - horrible cludge */
icmphdr.dport = icmp->icmp_code;
honeyd_log_probe(honeyd_logfp, IP_PROTO_ICMP, &icmphdr, pktlen, 0, NULL);
/* We can block ICMP, too */
if (tmpl && tmpl->icmp.status == PORT_BLOCK)
return;
if (tmpl != NULL && tmpl->person != NULL)
xp_print = tmpl->person->xp_fprint;
/* Without xprobe fingerprint, we understand only ECHO and UNREACH */
if (xp_print == NULL) {
if (!(icmp->icmp_type == ICMP_ECHO) &&
!(icmp->icmp_type == ICMP_UNREACH &&
icmp->icmp_code == ICMP_UNREACH_PORT))
return;
}
cksum = icmp->icmp_cksum;
ip_checksum(ip, pktlen);
if (cksum != icmp->icmp_cksum)
return;
dlen = pktlen - IP_HDR_LEN - ICMP_HDR_LEN;
dlen -= 4;
if (dlen < 0)
return;
/* AscII representation of original addresses */
addr_pack(&src, ADDR_TYPE_IP, IP_ADDR_BITS, &ip->ip_src, IP_ADDR_LEN);
addr_pack(&dst, ADDR_TYPE_IP, IP_ADDR_BITS, &ip->ip_dst, IP_ADDR_LEN);
addr_ntop(&src, osrc, sizeof(osrc));
addr_ntop(&dst, odst, sizeof(odst));
if (tmpl)
spoof = tmpl->spoof;
else
spoof = no_spoof;
addr_ntop(&spoof.new_src, ssrc, sizeof(ssrc));
addr_ntop(&spoof.new_dst, sdst, sizeof(sdst));
if (spoof.new_src.addr_type != ADDR_TYPE_NONE)
snprintf(adst, sizeof(adst), "%s (was %s)", ssrc, odst);
else
snprintf(adst, sizeof(adst), "%s", odst);
if (spoof.new_dst.addr_type != ADDR_TYPE_NONE)
snprintf(asrc, sizeof(asrc), "%s (was %s)", sdst, osrc);
else
snprintf(asrc, sizeof(asrc), "%s", osrc);
switch (icmp->icmp_type) {
case ICMP_ECHO:
icmp_echo = (struct icmp_msg_echo *)(icmp + 1);
dat = (u_char *)(icmp_echo + 1);
syslog(LOG_DEBUG, "Sending ICMP Echo Reply: %s -> %s",
adst, asrc);
if (xp_print) {
/* ym: Use our own icmp echo reply function */
icmp_echo_reply(tmpl, ip, xp_print->flags.icmp_echo_code,
xp_print->flags.icmp_echo_tos_bits ? ip->ip_tos : 0,
xp_print->flags.icmp_echo_df_bit ? IP_DF : 0,
xp_print->ttl_vals.icmp_echo_reply_ttl.ttl_val,
dat, dlen, spoof);
} else
icmp_echo_reply(tmpl, ip,
ICMP_CODE_NONE, 0, 0, honeyd_ttl, dat, dlen, spoof);
break;
case ICMP_UNREACH:
/* Only port unreachable at the moment */
icmp_quote = (struct icmp_msg_quote *)(icmp + 1);
rip = (struct ip_hdr *)(&icmp_quote->icmp_ip);
if (rip->ip_p != IP_PROTO_UDP)
break;
udp = (struct udp_hdr *)((u_char *)rip + (ip->ip_hl<<2));
tmpip.ip_src = rip->ip_dst;
tmpip.ip_dst = rip->ip_src;
tmpudp.uh_sport = udp->uh_dport;
tmpudp.uh_dport = udp->uh_sport;
honeyd_setudp(&honeyd_udp, &tmpip, &tmpudp, 0);
/* Find matching state */
con = (struct udp_con *)SPLAY_FIND(tree, &udpcons,
&honeyd_udp.conhdr);
if (con == NULL)
break;
con->softerrors++;
syslog(LOG_DEBUG,
"Received port unreachable: %s -> %s: errors %d",
asrc, adst, con->softerrors);
if (con->softerrors >= HONEYD_MAX_SOFTERRS)
udp_free(con);
break;
/* YM: Add ICMP Timestamp reply capability */
case ICMP_TSTAMP:
/* Happens only if xp_print != NULL */
if (xp_print->flags.icmp_timestamp_reply) {
icmp_tstamp = (struct icmp_msg_timestamp *)
((u_char*)pkt + (ip->ip_hl << 2));
syslog(LOG_DEBUG, "Sending ICMP Timestamp Reply: %s -> %s",
adst, asrc);
icmp_timestamp_reply(tmpl, ip, icmp_tstamp,
xp_print->ttl_vals.icmp_timestamp_reply_ttl.ttl_val, spoof);
}
break;
/* YM: Added ICMP Address Mask reply capability */
case ICMP_MASK:
/* Happens only if xp_print != NULL */
if (xp_print->flags.icmp_addrmask_reply) {
icmp_idseq = (struct icmp_msg_idseq *)(icmp + 1);
syslog(LOG_DEBUG, "Sending ICMP Address Mask Reply: %s -> %s",
adst, asrc);
icmp_mask_reply(tmpl, ip, icmp_idseq,
xp_print->ttl_vals.icmp_addrmask_reply_ttl.ttl_val,
HONEYD_ADDR_MASK, spoof);
}
break;
/* YM: Added ICMP Information reply capability */
case ICMP_INFO:
/* Happens only if xp_print != NULL */
if (xp_print->flags.icmp_info_reply) {
icmp_idseq = (struct icmp_msg_idseq *)(icmp + 1);
syslog(LOG_DEBUG, "Sending ICMP Info Reply: %s -> %s",
adst, asrc);
icmp_info_reply(tmpl, ip, icmp_idseq,
xp_print->ttl_vals.icmp_info_reply_ttl.ttl_val, spoof);
}
break;
default:
break;
}
}
void
honeyd_dispatch(struct template *tmpl, struct ip_hdr *ip, u_short iplen)
{
struct tuple iphdr;
iphdr.ip_src = ip->ip_src;
iphdr.ip_dst = ip->ip_dst;
iphdr.type = -1;
/*
* We define a hook here for packet interception -- plugins
* can use it to do fun stuff with the packets.
*/
switch(ip->ip_p) {
case IP_PROTO_TCP:
tcp_recv_cb(tmpl, (u_char *)ip, iplen);
break;
case IP_PROTO_UDP:
udp_recv_cb(tmpl, (u_char *)ip, iplen);
break;
case IP_PROTO_ICMP:
hooks_dispatch(ip->ip_p, HD_INCOMING, &iphdr,
(u_char *)ip, iplen);
icmp_recv_cb(tmpl, (u_char *)ip, iplen);
break;
default:
hooks_dispatch(ip->ip_p, HD_INCOMING, &iphdr,
(u_char *)ip, iplen);
honeyd_log_probe(honeyd_logfp, ip->ip_p, &iphdr, iplen, 0, NULL);
return;
}
}
/*
* Given the queue dependent delay time, we can get an estimate of
* the queue length. We do a kind of random early drop (RED) between
* a delay time of low and high in ms.
*/
static __inline int
honeyd_router_drop(struct link_drop *drop, struct timeval *tv)
{
int msec;
int low = drop->low;
int high = drop->high;
if (high == 0)
return (0);
/* See if we fall into the random bracket */
msec = tv->tv_sec * 1000 + tv->tv_usec / 1000;
if (msec <= low)
return (0);
if (msec >= high)
return (1);
msec -= low;
if (rand_uint16(honeyd_rand) % (high - low) < msec)
return (1);
else
return (0);
}
/*
* Follow a packet through the routing table; starting with router gw.
* Return:
* FW_INTERNAL - means that the packet needs to be received internally
* FW_EXTERNAL - means that the packet needs to be sent to the wire
* FW_DROP - means that the packet has been handled by dropping, etc.
*/
enum forward
honeyd_route_packet(struct ip_hdr *ip, u_int iplen,
struct addr *gw, struct addr *addr, int *pdelay)
{
struct router *r, *lastrouter = NULL;
struct router_entry *rte = NULL;
struct link_entry *link = NULL;
struct template *tmpl;
struct addr host;
double packetloss = 1;
int delay = 0, external = 0;
host = *gw;
r = router_find(&host);
while (addr_cmp(&host, addr) != 0 && --ip->ip_ttl) {
if ((rte = network_lookup(r->routes, addr)) == NULL) {
if (r->flags & ROUTER_ISENTRY) {
external = 1;
break;
}
noroute:
syslog(LOG_DEBUG, "No route to %s", addr_ntoa(addr));
return (FW_DROP);
}
if (rte->gw != NULL && lastrouter == rte->gw)
goto noroute;
if (rte->type == ROUTE_TUNNEL)
break;
if (rte->type == ROUTE_LINK || rte->type == ROUTE_UNREACH)
break;
/* Get the attributes for this link */
link = rte->link;
if (link->latency)
delay += link->latency;
else
delay += 3;
if (link->bandwidth) {
int ms = iplen * link->bandwidth / link->divider;
struct timeval now, tv;
gettimeofday(&now, NULL);
if (timercmp(&now, &link->tv_busy, <)) {
/* Router is busy for a while */
timersub(&link->tv_busy, &now, &tv);
/* Opportunity to drop based on queue length */
if (honeyd_router_drop(&link->red, &tv))
return (FW_DROP);
delay += tv.tv_sec * 1000 + tv.tv_usec / 1000;
} else {
/* Router is busy now */
link->tv_busy = now;
}
/* Construct router delay time */
tv.tv_sec = ms / 1000;
tv.tv_usec = (ms % 1000) * 1000;
timeradd(&link->tv_busy, &tv, &link->tv_busy);
delay += ms;
}
if (link->packetloss)
packetloss *= 1 - ((double)link->packetloss / 10000.0);
lastrouter = r;
r = rte->gw;
host = r->addr;
}
/* Calculate the packet loss rate */
packetloss = (1 - packetloss) * 10000;
if (rand_uint16(honeyd_rand) % 10000 < packetloss)
return (FW_DROP);
/* Send ICMP_TIMEXCEED from router address */
if (!ip->ip_ttl) {
struct spoof spoof = no_spoof;
spoof.new_src = host;
syslog(LOG_DEBUG, "TTL exceeded for dst %s at gw %s",
addr_ntoa(addr), addr_ntoa(&host));
/*
* We need to use the template of the host that will
* send the ICMP error message.
*/
tmpl = template_find_best(addr_ntoa(&host), ip, iplen);
honeyd_delay_packet(tmpl, ip, iplen, &host, NULL, delay, 0, spoof);
return (FW_DROP);
}
/* Send ICMP_UNREACH from router address */
if (rte != NULL && rte->type == ROUTE_UNREACH) {
syslog(LOG_DEBUG, "dst %s unreachable at gw %s",
addr_ntoa(addr), addr_ntoa(&host));
/*
* We need to use the template of the host that will
* send the ICMP error message.
*/
tmpl = template_find_best(addr_ntoa(&host), ip, iplen);
honeyd_delay_packet(tmpl, ip, iplen, &host, NULL, delay,
DELAY_UNREACH, no_spoof);
return (FW_DROP);
}
/* We need to tunnel this packet */
if (rte != NULL && rte->type == ROUTE_TUNNEL) {
honeyd_delay_packet(NULL, ip, iplen,
&rte->tunnel_src, &rte->tunnel_dst,
delay, DELAY_TUNNEL, no_spoof);
return (FW_DROP);
}
if (!external) {
struct template *tmpl;
/* Check if a template specific drop rate applies */
tmpl = template_find_best(addr_ntoa(addr), ip, iplen);
if (tmpl != NULL && tmpl->drop_inrate) {
uint16_t value;
value = rand_uint16(honeyd_rand) % (100*100);
if (value < tmpl->drop_inrate)
return (FW_DROP);
}
}
/* The packet can be received; schedule it */
*pdelay = delay;
return (external ? FW_EXTERNAL : FW_INTERNAL);
}
void
honeyd_input(const struct interface *inter, struct ip_hdr *ip, u_short iplen)
{
extern struct network *reverse;
struct template *tmpl = NULL;
struct router *gw;
struct addr gw_addr;
struct router_entry *rte;
enum forward res = FW_INTERNAL;
int delay = 0, flags = 0;
struct addr src, addr;
addr_pack(&addr, ADDR_TYPE_IP, IP_ADDR_BITS, &ip->ip_dst, IP_ADDR_LEN);
if (!router_used) {
/* Check if a template specific drop rate applies */
tmpl = template_find_best(addr_ntoa(&addr), ip, iplen);
if (tmpl != NULL && tmpl->drop_inrate) {
uint16_t value;
value = rand_uint16(honeyd_rand) % (100*100);
if (value < tmpl->drop_inrate)
return;
}
if (tmpl != NULL && tmpl->flags & TEMPLATE_EXTERNAL)
flags |= DELAY_ETHERNET;
honeyd_delay_packet(NULL, ip, iplen, NULL, NULL, delay, flags, no_spoof);
return;
}
addr_pack(&src, ADDR_TYPE_IP, IP_ADDR_BITS, &ip->ip_src, IP_ADDR_LEN);
if (ip->ip_p == IP_PROTO_GRE) {
uint16_t ipoff;
/* Decapsulate GRE packet if it is legitimate */
if ((rte = router_find_tunnel(&addr, &src)) == NULL) {
syslog(LOG_DEBUG, "Unknown GRE packet from %s",
addr_ntoa(&src));
return;
}
/* Check for fragment GRE packets */
ipoff = ntohs(ip->ip_off);
if ((ipoff & IP_OFFMASK) || (ipoff & IP_MF)) {
if (ip_fragment(NULL, ip, iplen, &ip, &iplen) == -1)
return;
/*
* If a packet was reassembled successfully, we can
* just continue processing it. All checks so far
* are solely concerned with the IP header.
*/
}
if (gre_decapsulate(ip, iplen, &ip, &iplen) == -1)
return;
addr_pack(&src, ADDR_TYPE_IP, IP_ADDR_BITS, &ip->ip_src, IP_ADDR_LEN);
/* Check that the source address is valid */
if (!addr_contained(&rte->net, &src)) {
syslog(LOG_INFO,
"Bad address %s injected into tunnel %s",
addr_ntoa(&src), addr_ntoa(&rte->net));
return;
}
addr_pack(&addr, ADDR_TYPE_IP, IP_ADDR_BITS, &ip->ip_dst, IP_ADDR_LEN);
}
if ((gw = network_lookup(reverse, &src)) != NULL)
gw_addr = gw->addr;
/* Find the correct entry router based on destination IP */
else if ((gw = network_lookup(entry_routers, &addr)) != NULL)
gw_addr = gw->addr;
else {
/* Pick the first one on the list */
gw = entry_routers->data;
gw_addr = gw->addr;
}
res = honeyd_route_packet(ip, iplen, &gw_addr, &addr, &delay);
if (res == FW_DROP)
return;
/*
* We want to prevent routing loops. One good heuristic is to
* drop all packets that we received from an interface and
* that want to be routed out of an interface. In the case of
* ethernet, this is legitimate if we have external hosts
* integrated into the routing topology. In that case, we
* send the packet out, if there is a routing loop, we are
* going to receive it via loopback and drop it then.
*/
if (res == FW_EXTERNAL) {
if (inter != NULL && inter->if_ent.intf_link_addr.addr_type !=
ADDR_TYPE_ETH) {
syslog(LOG_DEBUG, "No route to %s",
addr_ntoa(&addr));
return;
} else
flags |= DELAY_EXTERNAL;
} else
tmpl = template_find_best(addr_ntoa(&addr), ip, iplen);
if (tmpl != NULL && tmpl->flags & TEMPLATE_EXTERNAL)
flags |= DELAY_ETHERNET;
/* Delay the packet if necessary, otherwise deliver it directly */
honeyd_delay_packet(NULL, ip, iplen, NULL, NULL, delay, flags, no_spoof);
}
void
honeyd_recv_cb(u_char *ag, const struct pcap_pkthdr *pkthdr, const u_char *pkt)
{
const struct interface *inter = (const struct interface *)ag;
struct ip_hdr *ip;
struct addr addr;
u_short iplen;
struct eth_hdr *eth = (struct eth_hdr *)pkt;
int is_etherpkt = 0;
count_increment(stats_network.input_bytes, pkthdr->caplen);
/* Check if we can receive arp traffic on this interface */
if ((router_used || need_arp) &&
inter->if_ent.intf_link_addr.addr_type == ADDR_TYPE_ETH) {
struct arp_req *req;
struct addr eth_sha;
/* Mark this packet as being delivered via ethernet */
is_etherpkt = 1;
/* Ignore our own packets */
addr_pack(ð_sha, ADDR_TYPE_ETH, ETH_ADDR_BITS,
ð->eth_src, ETH_ADDR_LEN);
if ((req = arp_find(ð_sha)) != NULL &&
(req->flags & ARP_INTERNAL))
return;
if (ntohs(eth->eth_type) == ETH_TYPE_ARP) {
arp_recv_cb(ag, pkthdr, pkt);
return;
}
}
/* Everything below assumes that the packet is IPv4 */
if (pkthdr->caplen < inter->if_dloff + IP_HDR_LEN)
return;
ip = (struct ip_hdr *)(pkt + inter->if_dloff);
iplen = ntohs(ip->ip_len);
if (pkthdr->caplen - inter->if_dloff < iplen)
return;
if (ip->ip_hl << 2 > iplen)
return;
if (ip->ip_hl << 2 < sizeof(struct ip_hdr))
return;
/* Check our own address */
addr_pack(&addr, ADDR_TYPE_IP, IP_ADDR_BITS, &ip->ip_dst, IP_ADDR_LEN);
if (addr_cmp(&addr, &inter->if_ent.intf_addr) == 0) {
/* Only accept packets for own address if they are GRE */
if (!router_used || ip->ip_p != IP_PROTO_GRE)
return;
}
/* Check for a DHCP server response */
if (is_etherpkt && ip->ip_p == IP_PROTO_UDP) {
struct udp_hdr *udp;
udp = (struct udp_hdr *)((u_char *)ip + (ip->ip_hl << 2));
if (iplen < (ip->ip_hl << 2) + UDP_HDR_LEN)
goto out;
if (ntohs(udp->uh_dport) == 68 && ntohs(udp->uh_sport) == 67) {
dhcp_recv_cb(eth, ip, iplen);
return;
}
}
out:
honeyd_input(inter, ip, iplen);
}
void
honeyd_sigchld(int fd, short what, void *arg)
{
pid_t pid;
while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) {
/* Ignore the rrdtool driver for children accounting */
if (honeyd_rrd_drv != NULL && honeyd_rrd_drv->pid == pid)
continue;
honeyd_nchildren--;
}
}
void
honeyd_signal(int fd, short what, void *arg)
{
syslog(LOG_NOTICE, "exiting on signal %d", fd);
honeyd_exit(0);
}
void
honeyd_sighup(int fd, short what, void *arg)
{
syslog(LOG_NOTICE, "rereading configuration on signal %d", fd);
template_free_all(TEMPLATE_FREE_REGULAR);
router_end();
if (config.config != NULL)
config_read(config.config);
}
void
honeyd_sigusr(int fd, short what, void *arg)
{
syslog(LOG_NOTICE, "rotating log files on signal %d", fd);
honeyd_logend(honeyd_logfp);
honeyd_logend(honeyd_servicefp);
if (logfile != NULL)
honeyd_logfp = honeyd_logstart(logfile);
if (servicelog != NULL)
honeyd_servicefp = honeyd_logstart(servicelog);
}
struct _unittest {
char *name;
void (*cb)(void);
} unittests[] = {
#ifdef HAVE_PYTHON
{ "pydataprocessing", pydataprocessing_test },
{ "pydatahoneyd", pydatahoneyd_test },
#endif
{ "rrdtool", rrdtool_test },
{ "ethernet", ethernet_test },
{ "interface", interface_test },
{ "network", network_test },
{ "template", template_test },
{ NULL, NULL}
};
void
unittest(void)
{
struct _unittest *ut;
fprintf(stderr, "Running unittests ...\n");
for (ut = unittests; ut->name != NULL; ut++) {
fprintf(stderr, " ---- %s TEST ---- \n", ut->name);
(*ut->cb)();
fprintf(stderr, " ---- %s OK ---- \n", ut->name);
}
fprintf(stderr, "All unitests are OK\n");
exit(0);
}
int
main(int argc, char *argv[])
{
extern int interface_dopoll;
struct event sigterm_ev, sigint_ev, sighup_ev, sigchld_ev, sigusr_ev;
char *dev[HONEYD_MAX_INTERFACES];
char **orig_argv;
struct addr stats_dst;
u_short stats_port = 0;
char *stats_username = NULL;
char *stats_password = NULL;
int want_unittest = 0;
int setrand = 0;
int i, c, orig_argc, ninterfaces = 0;
FILE *fp;
fprintf(stderr, "Honeyd V%s Copyright (c) 2002-2007 Niels Provos\n",
VERSION);
orig_argc = argc;
orig_argv = argv;
while ((c = getopt_long(argc, argv, "VPTdc:i:p:x:a:u:g:f:l:s:0:R:h?",
honeyd_long_opts, NULL)) != -1) {
char *ep;
switch (c) {
case 'Y':
honeyd_rrdtool_path = optarg;
break;
case 'A':
honeyd_webserver_address = optarg;
break;
case 'W':
honeyd_webserver_port = atoi(optarg);
if (honeyd_webserver_port == 0) {
fprintf(stderr, "Bad port number: %s\n",
optarg);
usage();
}
break;
case 'X':
honeyd_webserver_root = optarg;
break;
case 'T':
want_unittest = 1;
break;
case 'R':
/* For regression testing */
setrand = atoi(optarg);
break;
case 'c': {
char line[1024], *p = line;
char *address;
char *strport;
char *name;
char *password;
strlcpy(line, optarg, sizeof(line));
if ((address = strsep(&p, ":")) == NULL)
usage();
if ((strport = strsep(&p, ":")) == NULL)
usage();
if ((name = strsep(&p, ":")) == NULL)
usage();
if ((password = strsep(&p, ":")) == NULL)
usage();
if (p != NULL && *p != '\0')
usage();
if (addr_pton(address, &stats_dst) == -1) {
fprintf(stderr, "Bad destination address %s\n",
address);
usage();
}
if ((stats_port = atoi(strport)) == 0) {
fprintf(stderr, "Bad destination port %s\n",
strport);
usage();
}
stats_username = strdup(name);
stats_password = strdup(password);
}
break;
case 'u':
honeyd_uid = strtoul(optarg, &ep, 10);
honeyd_needsroot = -1;
if (optarg[0] == '\0' || *ep != '\0') {
fprintf(stderr, "Bad uid %s\n", optarg);
usage();
}
break;
case 'g':
honeyd_gid = strtoul(optarg, &ep, 10);
honeyd_needsroot = -1;
if (optarg[0] == '\0' || *ep != '\0') {
fprintf(stderr, "Bad gid %s\n", optarg);
usage();
}
break;
case 'V':
honeyd_show_version = 1;
break;
case 'P':
interface_dopoll = 1;
break;
case 'd':
honeyd_debug++;
break;
case 'i':
if (ninterfaces >= HONEYD_MAX_INTERFACES)
errx(1, "Too many interfaces specified");
dev[ninterfaces++] = optarg;
break;
case 'f':
config.config = optarg;
break;
case 'l':
logfile = optarg;
break;
case 's':
servicelog = optarg;
break;
case 'x':
config.xprobe = optarg;
break;
case 'a':
config.assoc = optarg;
break;
case 'p':
config.pers = optarg;
break;
case '0':
config.osfp = optarg;
break;
case 0:
/* long option handled -- skip this one. */
break;
default:
usage();
/* not reached */
}
}
if (honeyd_show_version) {
printf("Honeyd Version %s\n", VERSION);
exit(0);
}
if (honeyd_show_usage) {
usage();
/* not reached */
}
if (honeyd_show_include_dir) {
printf("%s\n", PATH_HONEYDINCLUDE);
exit(0);
}
if (honeyd_show_data_dir) {
printf("%s\n", PATH_HONEYDDATA);
exit(0);
}
argc -= optind;
argv += optind;
if ((honeyd_rand = rand_open()) == NULL)
err(1, "rand_open");
/* We need reproduceable random numbers for regression testing */
if (setrand)
rand_set(honeyd_rand, &setrand, sizeof(setrand));
/* disables event methods that don't work for bpf */
interface_prevent_init();
/* Initalize libevent */
event_init();
/* Three priorities - UI connections always get a better priority */
event_priority_init(3);
syslog_init(orig_argc, orig_argv);
/* Initalize pool allocator */
pool_pkt = pool_init(HONEYD_MTU);
pool_delay = pool_init(sizeof(struct delay));
/* Initialize honeyd's callback hooks */
hooks_init();
tagging_init();
arp_init();
interface_initialize(honeyd_recv_cb);
config_init();
router_init();
plugins_config_init();
if (stats_username != NULL) {
stats_init();
stats_init_collect(&stats_dst, stats_port,
stats_username, stats_password);
}
personality_init();
xprobe_personality_init();
associations_init();
/* Xprobe2 fingerprints */
if ((fp = fopen(config.xprobe, "r")) == NULL)
err(1, "fopen(%s)", config.xprobe);
if (xprobe_personality_parse(fp) == -1)
errx(1, "parsing xprobe personality file failed");
fclose(fp);
/* Association between xprobe and nmap fingerprints */
if ((fp = fopen(config.assoc, "r")) == NULL)
err(1, "fopen(%s)", config.assoc);
if (parse_associations(fp) == -1)
errx(1, "parsing associations file failed");
fclose(fp);
/* Nmap fingerprints */
if ((fp = fopen(config.pers, "r")) == NULL)
err(1, "fopen(%s)", config.pers);
if (personality_parse(fp) == -1)
errx(1, "parsing personality file failed");
fclose(fp);
/* PF OS fingerprints */
if (honeyd_osfp_init(config.osfp) == -1)
errx(1, "reading OS fingerprints failed");
honeyd_init();
if (want_unittest)
unittest();
if ((honeyd_ip = ip_open()) == NULL) {
/*
* We ignore this error if a user just wants to verify
* the configuration - some configs will not load without
* this call succeeding.
*/
if (!honeyd_verify_config)
err(1, "ip_open");
}
if (honeyd_verify_config) {
extern int interface_verify_config;
/* Make sure that we do not open interfaces for real */
interface_verify_config = 1;
}
/* Initialize the specified interfaces */
if (ninterfaces == 0)
interface_init(NULL, argc, argc ? argv : NULL);
else {
for (i = 0; i < ninterfaces; i++)
interface_init(dev[i], argc, argc ? argv : NULL);
}
#ifdef HAVE_PYTHON
/* Python support must be started before reading the configuration. */
pyextend_init();
/* Start our web server */
if (!honeyd_verify_config && honeyd_is_webserver_enabled())
pyextend_webserver_init(
honeyd_webserver_address,
honeyd_webserver_port,
honeyd_webserver_root);
#endif
/* Reads in the ethernet codes and indexes them for use in config */
ethernetcode_init();
/* Read main configuration file */
if (config.config != NULL)
config_read(config.config);
/* Just verify the configuration - exit with success */
if (honeyd_verify_config)
errx(0, "parsing configuration file successful");
/* Attach the UI interface */
ui_init();
/*
* We must initialize the plugins after the config file
* has been read, as the plugins may query config settings!
*/
plugins_init();
ip_fragment_init();
#ifdef HAVE_PYTHON
/* Fix permissions of the webserver directories if requested */
if (honeyd_webserver_fix_permissions)
pyextend_webserver_fix_permissions(honeyd_webserver_root,
honeyd_uid, honeyd_gid);
#endif
/* Create PID file, we might not be able to remove it */
unlink(PIDFILE);
if ((fp = fopen(PIDFILE, "w")) == NULL)
err(1, "fopen");
/* Start Honeyd in the background if necessary */
if (!honeyd_debug) {
setlogmask(LOG_UPTO(LOG_INFO));
fprintf(stderr, "Honeyd starting as background process\n");
if (daemon(1, 0) < 0) {
unlink(PIDFILE);
err(1, "daemon");
}
}
fprintf(fp, "%d\n", getpid());
fclose(fp);
chmod(PIDFILE, 0644);
/* Drop privileges if we do not need them */
if (honeyd_needsroot <= 0) {
cmd_droppriv(honeyd_uid, honeyd_gid);
syslog(LOG_NOTICE,
"Demoting process privileges to uid %u, gid %u",
honeyd_uid, honeyd_gid);
} else {
syslog(LOG_WARNING, "Running with root privileges.");
}
/*
* Check that this version of Honeyd does not have any critical
* security holes.
*/
if (!honeyd_disable_update)
update_check();
#ifdef HAVE_PYTHON
/* Verify that the webserver space is setup correctly */
if (honeyd_is_webserver_enabled())
pyextend_webserver_verify_setup(honeyd_webserver_root);
else
syslog(LOG_INFO, "Internal webserver has been disabled.");
#endif
/* Setup signal handler */
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
perror("signal");
return (-1);
}
signal_set(&sigint_ev, SIGINT, honeyd_signal, NULL);
signal_add(&sigint_ev, NULL);
signal_set(&sigterm_ev, SIGTERM, honeyd_signal, NULL);
signal_add(&sigterm_ev, NULL);
signal_set(&sigchld_ev, SIGCHLD, honeyd_sigchld, NULL);
signal_add(&sigchld_ev, NULL);
signal_set(&sighup_ev, SIGHUP, honeyd_sighup, NULL);
signal_add(&sighup_ev, NULL);
signal_set(&sigusr_ev, SIGUSR1, honeyd_sigusr, NULL);
signal_add(&sigusr_ev, NULL);
/* Start logging via rrd */
if (honeyd_rrdtool_path != NULL && strlen(honeyd_rrdtool_path))
honeyd_rrd_start(honeyd_rrdtool_path);
/* Potential dependency on the timestamp used for rrdtool */
count_init();
if (logfile != NULL)
honeyd_logfp = honeyd_logstart(logfile);
if (servicelog != NULL)
honeyd_servicefp = honeyd_logstart(servicelog);
event_dispatch();
syslog(LOG_ERR, "Kqueue does not recognize bpf filedescriptor.");
return (0);
}
/*
* Determine if Honeyd should automatically demote the user id it is
* going to use.
*/
void
honeyd_use_uid(uid_t uid)
{
if (!honeyd_needsroot && uid != honeyd_uid)
honeyd_needsroot = 1;
}
void
honeyd_use_gid(gid_t gid)
{
if (!honeyd_needsroot && gid != honeyd_gid)
honeyd_needsroot = 1;
}
syntax highlighted by Code2HTML, v. 0.9.1