From 90497af7bb97ebb000d0edd2f47512a7c2f62265 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Jul 2016 09:48:59 +0200 Subject: [PATCH 1/8] Add optional support for RFC863, Discard Protocol Signed-off-by: Joachim Nilsson --- README.md | 25 +++++++++++++---- configure.ac | 17 ++++++------ plugins/Makefile.am | 8 ++++++ plugins/discard.c | 67 +++++++++++++++++++++++++++++++++++++++++++++ plugins/echo.c | 6 ++-- plugins/time.c | 6 ++-- 6 files changed, 109 insertions(+), 20 deletions(-) create mode 100644 plugins/discard.c diff --git a/README.md b/README.md index c03dccf3..28d6f1af 100644 --- a/README.md +++ b/README.md @@ -73,8 +73,9 @@ interface, reducing the need for a full blown firewall. Built-in optional inetd services: -- time (rdate) RFC862 -- echo RFC868 +- echo RFC862 +- discard RFC863 +- time (rdate) RFC868 **Runlevels** @@ -484,10 +485,11 @@ The original `inetd` had a few standard services built-in: - chargen - discard -Finit currently supports the `time` and `echo` services. Both of which -are realized as plugins to provide a simple means of testing the inetd -functionality stand-alone. But this also provides both a useful network -testing/availability and a rudimentary time server for rdate clients. +Finit currently supports the `time`, `echo`, and `discard` services. +Both of which are realized as plugins to provide a simple means of +testing the inetd functionality stand-alone. But this also provides +both a useful network testing/availability and a rudimentary time server +for rdate clients. Internal inetd services are set up as follows: @@ -496,6 +498,8 @@ Internal inetd services are set up as follows: inetd time/tcp nowait [2345] internal inetd echo/udp wait [2345] internal inetd echo/tcp nowait [2345] internal + inetd discard/udp wait [2345] internal + inetd discard/tcp nowait [2345] internal ``` Then call `rdate` from a remote machine (or use localhost): @@ -512,6 +516,13 @@ Or `echoping` to reach the echo service: echoping -uv ``` +Or `echoping -d` to reach the discard service: + +```shell + echoping -dv + echoping -duv +``` + If you use `time/udp` you must use the standard rdate implementation and then call it with `rdate -up` to connect using UDP. Without the `-p` argument rdate will try to set the system clock. Please note that rdate @@ -554,6 +565,8 @@ For your convenience a set of *optional* plugins are available: * *echo.so*: RFC 862 plugin. Start as inetd service, like time below. +* *discard.so*: RFC 863 plugin. Start as inetd service, like time below. + * *hwclock.so*: Restore and save system clock from/to RTC on startup/shutdown. diff --git a/configure.ac b/configure.ac index f265edb8..8146b80b 100644 --- a/configure.ac +++ b/configure.ac @@ -53,14 +53,15 @@ AC_ARG_ENABLE(quiet, # Check for extra plugins to enable enable_all_plugins=auto -AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl]) -AC_PLUGIN([dbus], [no], [Setup and start system message bus, D-Bus]) -AC_PLUGIN([inetd-echo], [no], [Inetd plugin: echo server, RFC862]) -AC_PLUGIN([inetd-time], [no], [Inetd plugin: time (rdate) server, RFC868]) -AC_PLUGIN([lost], [no], [Example HOOK_SVC_LOST plugin, very noisy as-is!]) -AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf]) -AC_PLUGIN([x11-common], [no], [Console setup (for X)]) -AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.]) +AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl]) +AC_PLUGIN([dbus], [no], [Setup and start system message bus, D-Bus]) +AC_PLUGIN([inetd-echo], [no], [Inetd plugin: echo server, RFC862]) +AC_PLUGIN([inetd-discard], [no], [Inetd plugin: discard server, RFC862]) +AC_PLUGIN([inetd-time], [no], [Inetd plugin: time (rdate) server, RFC868]) +AC_PLUGIN([lost], [no], [Example HOOK_SVC_LOST plugin, very noisy as-is!]) +AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf]) +AC_PLUGIN([x11-common], [no], [Console setup (for X)]) +AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.]) # Check for extra arguments or packages AC_ARG_WITH(heading, diff --git a/plugins/Makefile.am b/plugins/Makefile.am index e25cf87d..528a9457 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -19,6 +19,10 @@ if BUILD_INETD_ECHO_PLUGIN libplug_la_SOURCES += echo.c endif +if BUILD_INETD_DISCARD_PLUGIN +libplug_la_SOURCES += discard.c +endif + if BUILD_INETD_TIME_PLUGIN libplug_la_SOURCES += time.c endif @@ -50,6 +54,10 @@ if BUILD_INETD_ECHO_PLUGIN pkglib_LTLIBRARIES += echo.la endif +if BUILD_INETD_DISCARD_PLUGIN +pkglib_LTLIBRARIES += discard.la +endif + if BUILD_INETD_TIME_PLUGIN pkglib_LTLIBRARIES += time.la endif diff --git a/plugins/discard.c b/plugins/discard.c new file mode 100644 index 00000000..94bc56c7 --- /dev/null +++ b/plugins/discard.c @@ -0,0 +1,67 @@ +/* Optional inetd plugin for the Discard Protocol, RFC 863 + * + * Copyright (c) 2016 Joachim Nilsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include /* STDIN_FILENO */ +#include + +#include "../plugin.h" + +static int cb(int type) +{ + int sd = STDIN_FILENO; + char buf[BUFSIZ]; + ssize_t len; + struct sockaddr_storage sa; + socklen_t sa_len = sizeof(sa); + + len = recvfrom(sd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *)&sa, &sa_len); + if (-1 == len) + return -1; /* On error, close connection. */ + + return 0; +} + +static plugin_t plugin = { + .name = "discard", /* Must match the inetd /etc/services entry */ + .inetd = { + .cmd = cb + }, +}; + +PLUGIN_INIT(plugin_init) +{ + plugin_register(&plugin); +} + +PLUGIN_EXIT(plugin_exit) +{ + plugin_unregister(&plugin); +} + +/** + * Local Variables: + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ diff --git a/plugins/echo.c b/plugins/echo.c index 6cad05a5..1e44f8df 100644 --- a/plugins/echo.c +++ b/plugins/echo.c @@ -1,4 +1,4 @@ -/* Optional inetd plugin for the echo protocol RFC 862 +/* Optional inetd plugin for the Echo Protocol, RFC 862 * * Copyright (c) 2016 Joachim Nilsson * @@ -27,7 +27,7 @@ #include "../plugin.h" -static int echo_cb(int type) +static int cb(int type) { int sd = STDIN_FILENO; char buf[BUFSIZ]; @@ -45,7 +45,7 @@ static int echo_cb(int type) static plugin_t plugin = { .name = "echo", /* Must match the inetd /etc/services entry */ .inetd = { - .cmd = echo_cb + .cmd = cb }, }; diff --git a/plugins/time.c b/plugins/time.c index 6a014342..14e10af6 100644 --- a/plugins/time.c +++ b/plugins/time.c @@ -1,4 +1,4 @@ -/* Optional inetd plugin for time (rdate) protocol RFC 868 +/* Optional inetd plugin for Time Protocol (rdate), RFC 868 * * Copyright (c) 2015 Joachim Nilsson * @@ -32,7 +32,7 @@ #define EPOCH_OFFSET 2208988800ULL /* Return number of seconds since midnight, 1st Jan, 1900 */ -static int rfc868(int type) +static int cb(int type) { int sd = STDIN_FILENO; time_t now; @@ -64,7 +64,7 @@ static int rfc868(int type) static plugin_t plugin = { .name = "time", /* Must match the inetd /etc/services entry */ .inetd = { - .cmd = rfc868 + .cmd = cb } }; From 4f7366640bc21430e5c3210c8708226a3186bfaf Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Jul 2016 11:10:19 +0200 Subject: [PATCH 2/8] Add optional support for RFC864, Character Generator Protocol Signed-off-by: Joachim Nilsson --- README.md | 34 +++++++++----- configure.ac | 1 + plugins/Makefile.am | 8 ++++ plugins/chargen.c | 110 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 11 deletions(-) create mode 100644 plugins/chargen.c diff --git a/README.md b/README.md index 28d6f1af..4231887e 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ interface, reducing the need for a full blown firewall. Built-in optional inetd services: - echo RFC862 +- chargen RFC864 - discard RFC863 - time (rdate) RFC868 @@ -478,28 +479,30 @@ Compared to Finit v1.12 you must *explicitly deny* access from `eth0`! **Internal Services** -The original `inetd` had a few standard services built-in: +Like the original `inetd`, Finit has a few standard services built-in. +They are realized as plugins to provide a simple means of testing the +inetd functionality stand-alone. But this also provides both a useful +network testing/availability, as well as a rudimentary time server for +`rdate` clients. -- time - echo - chargen - discard +- time -Finit currently supports the `time`, `echo`, and `discard` services. -Both of which are realized as plugins to provide a simple means of -testing the inetd functionality stand-alone. But this also provides -both a useful network testing/availability and a rudimentary time server -for rdate clients. - -Internal inetd services are set up as follows: +For security reasons they are all disabled by default and have to be +enabled with both the `configure` script and a special `inetd` stanza in +the `finit.conf` or `finit.d/*.conf` like this: ```shell - inetd time/udp wait [2345] internal - inetd time/tcp nowait [2345] internal inetd echo/udp wait [2345] internal inetd echo/tcp nowait [2345] internal + inetd chargen/udp wait [2345] internal + inetd chargen/tcp nowait [2345] internal inetd discard/udp wait [2345] internal inetd discard/tcp nowait [2345] internal + inetd time/udp wait [2345] internal + inetd time/tcp nowait [2345] internal ``` Then call `rdate` from a remote machine (or use localhost): @@ -523,6 +526,13 @@ Or `echoping -d` to reach the discard service: echoping -duv ``` +Or `echoping -c` to reach the chargen service: + +```shell + echoping -cv + echoping -cuv +``` + If you use `time/udp` you must use the standard rdate implementation and then call it with `rdate -up` to connect using UDP. Without the `-p` argument rdate will try to set the system clock. Please note that rdate @@ -565,6 +575,8 @@ For your convenience a set of *optional* plugins are available: * *echo.so*: RFC 862 plugin. Start as inetd service, like time below. +* *chargen.so*: RFC 864 plugin. Start as inetd service, like time below. + * *discard.so*: RFC 863 plugin. Start as inetd service, like time below. * *hwclock.so*: Restore and save system clock from/to RTC on diff --git a/configure.ac b/configure.ac index 8146b80b..2e1f4081 100644 --- a/configure.ac +++ b/configure.ac @@ -56,6 +56,7 @@ enable_all_plugins=auto AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl]) AC_PLUGIN([dbus], [no], [Setup and start system message bus, D-Bus]) AC_PLUGIN([inetd-echo], [no], [Inetd plugin: echo server, RFC862]) +AC_PLUGIN([inetd-chargen], [no], [Inetd plugin: character generator, RFC864]) AC_PLUGIN([inetd-discard], [no], [Inetd plugin: discard server, RFC862]) AC_PLUGIN([inetd-time], [no], [Inetd plugin: time (rdate) server, RFC868]) AC_PLUGIN([lost], [no], [Example HOOK_SVC_LOST plugin, very noisy as-is!]) diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 528a9457..49d6a5c4 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -19,6 +19,10 @@ if BUILD_INETD_ECHO_PLUGIN libplug_la_SOURCES += echo.c endif +if BUILD_INETD_CHARGEN_PLUGIN +libplug_la_SOURCES += chargen.c +endif + if BUILD_INETD_DISCARD_PLUGIN libplug_la_SOURCES += discard.c endif @@ -54,6 +58,10 @@ if BUILD_INETD_ECHO_PLUGIN pkglib_LTLIBRARIES += echo.la endif +if BUILD_INETD_CHARGEN_PLUGIN +pkglib_LTLIBRARIES += chargen.la +endif + if BUILD_INETD_DISCARD_PLUGIN pkglib_LTLIBRARIES += discard.la endif diff --git a/plugins/chargen.c b/plugins/chargen.c new file mode 100644 index 00000000..30528b51 --- /dev/null +++ b/plugins/chargen.c @@ -0,0 +1,110 @@ +/* Optional inetd plugin for the Character Generator Protocol, RFC 864 + * + * Copyright (c) 2016 Joachim Nilsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include /* STDIN_FILENO */ +#include + +#include "../plugin.h" + +#define PATTERN "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ " + +static char *generator(char *buf, size_t len) +{ + size_t num; + static size_t pos = 0; + const char pattern[] = PATTERN; + + len = 72; + if (pos + len > sizeof(pattern)) { + num = sizeof(pattern) - pos; + len -= num; + } else { + num = 72; + len = 0; + } + + strncpy(&buf[0], &pattern[pos], num--); + if (len++) + strncpy(&buf[num], pattern, len); + strcat(buf, "\r\n"); + + if (++pos >= sizeof(pattern) - 1) + pos = 0; + + return buf; +} + +static int recv_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, socklen_t *sa_len) +{ + len = recvfrom(sd, buf, len, MSG_DONTWAIT, sa, sa_len); + if (-1 == len) + return -1; /* On error, close connection. */ + + return 0; +} + +static int send_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, socklen_t sa_len) +{ + char *pattern = generator(buf, len); + + return sendto(sd, pattern, strlen(pattern), MSG_DONTWAIT, sa, sa_len); +} + +static int cb(int type) +{ + int sd = STDIN_FILENO; + ssize_t len; + char buf[BUFSIZ]; + struct sockaddr_storage sa; + socklen_t sa_len = sizeof(sa); + + if (recv_peer(sd, buf, sizeof(buf), (struct sockaddr *)&sa, &sa_len)) + return -1; + + return send_peer(sd, buf, sizeof(buf), (struct sockaddr *)&sa, sa_len); +} + +static plugin_t plugin = { + .name = "chargen", /* Must match the inetd /etc/services entry */ + .inetd = { + .cmd = cb + }, +}; + +PLUGIN_INIT(plugin_init) +{ + plugin_register(&plugin); +} + +PLUGIN_EXIT(plugin_exit) +{ + plugin_unregister(&plugin); +} + +/** + * Local Variables: + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ From d4fdf50636ddc14f5298cec01fdb75ce0a25c764 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Jul 2016 12:06:59 +0200 Subject: [PATCH 3/8] Add optional support for RFC867, Daytime Protocol Signed-off-by: Joachim Nilsson --- README.md | 6 +++ configure.ac | 3 +- plugins/Makefile.am | 8 ++++ plugins/daytime.c | 96 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 plugins/daytime.c diff --git a/README.md b/README.md index 4231887e..b70b13f4 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ Built-in optional inetd services: - echo RFC862 - chargen RFC864 +- daytime RFC867 - discard RFC863 - time (rdate) RFC868 @@ -487,6 +488,7 @@ network testing/availability, as well as a rudimentary time server for - echo - chargen +- daytime - discard - time @@ -499,6 +501,8 @@ the `finit.conf` or `finit.d/*.conf` like this: inetd echo/tcp nowait [2345] internal inetd chargen/udp wait [2345] internal inetd chargen/tcp nowait [2345] internal + inetd daytime/udp wait [2345] internal + inetd daytime/tcp nowait [2345] internal inetd discard/udp wait [2345] internal inetd discard/tcp nowait [2345] internal inetd time/udp wait [2345] internal @@ -577,6 +581,8 @@ For your convenience a set of *optional* plugins are available: * *chargen.so*: RFC 864 plugin. Start as inetd service, like time below. +* *daytime.so*: RFC 867 plugin. Start as inetd service, like time below. + * *discard.so*: RFC 863 plugin. Start as inetd service, like time below. * *hwclock.so*: Restore and save system clock from/to RTC on diff --git a/configure.ac b/configure.ac index 2e1f4081..772af2f3 100644 --- a/configure.ac +++ b/configure.ac @@ -57,7 +57,8 @@ AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using al AC_PLUGIN([dbus], [no], [Setup and start system message bus, D-Bus]) AC_PLUGIN([inetd-echo], [no], [Inetd plugin: echo server, RFC862]) AC_PLUGIN([inetd-chargen], [no], [Inetd plugin: character generator, RFC864]) -AC_PLUGIN([inetd-discard], [no], [Inetd plugin: discard server, RFC862]) +AC_PLUGIN([inetd-daytime], [no], [Inetd plugin: daytime server, RFC867]) +AC_PLUGIN([inetd-discard], [no], [Inetd plugin: discard server, RFC863]) AC_PLUGIN([inetd-time], [no], [Inetd plugin: time (rdate) server, RFC868]) AC_PLUGIN([lost], [no], [Example HOOK_SVC_LOST plugin, very noisy as-is!]) AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf]) diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 49d6a5c4..80533802 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -23,6 +23,10 @@ if BUILD_INETD_CHARGEN_PLUGIN libplug_la_SOURCES += chargen.c endif +if BUILD_INETD_DAYTIME_PLUGIN +libplug_la_SOURCES += daytime.c +endif + if BUILD_INETD_DISCARD_PLUGIN libplug_la_SOURCES += discard.c endif @@ -62,6 +66,10 @@ if BUILD_INETD_CHARGEN_PLUGIN pkglib_LTLIBRARIES += chargen.la endif +if BUILD_INETD_DAYTIME_PLUGIN +pkglib_LTLIBRARIES += daytime.la +endif + if BUILD_INETD_DISCARD_PLUGIN pkglib_LTLIBRARIES += discard.la endif diff --git a/plugins/daytime.c b/plugins/daytime.c new file mode 100644 index 00000000..64264176 --- /dev/null +++ b/plugins/daytime.c @@ -0,0 +1,96 @@ +/* Optional inetd plugin for Daytime Protocol, RFC 867 + * + * Copyright (c) 2016 Joachim Nilsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include /* STDIN_FILENO */ +#include + +#include "../plugin.h" + +static char *daytime(char *buf, size_t len) +{ + time_t t; + struct tm *tmp; + + t = time(NULL); + tmp = localtime(&t); + + memset(buf, 0, len); + strftime(buf, len, "%a, %B %d, %Y %T-%Z", tmp); + + return buf; +} + +static int recv_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, socklen_t *sa_len) +{ + len = recvfrom(sd, buf, sizeof(buf), MSG_DONTWAIT, sa, sa_len); + if (-1 == len) + return -1; /* On error, close connection. */ + + return 0; +} + +static int send_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, socklen_t sa_len) +{ + char *now = daytime(buf, len); + + return sendto(sd, now, strlen(now), MSG_DONTWAIT, sa, sa_len); +} + +static int cb(int type) +{ + int sd = STDIN_FILENO; + char buf[BUFSIZ]; + struct sockaddr_storage sa; + socklen_t sa_len = sizeof(sa); + + if (recv_peer(sd, buf, sizeof(buf), (struct sockaddr *)&sa, &sa_len)) + return -1; + + return send_peer(sd, buf, sizeof(buf), (struct sockaddr *)&sa, sa_len); +} + +static plugin_t plugin = { + .name = "daytime", /* Must match the inetd /etc/services entry */ + .inetd = { + .cmd = cb + } +}; + +PLUGIN_INIT(plugin_init) +{ + plugin_register(&plugin); +} + +PLUGIN_EXIT(plugin_exit) +{ + plugin_unregister(&plugin); +} + +/** + * Local Variables: + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ From 5723b2e75be3b7076d32a61062fa8e7bda951c1c Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Jul 2016 12:28:40 +0200 Subject: [PATCH 4/8] Refactor RFC 868, time protocol plugin - Always do a dummy read to get peer address. - Refactor to new format used by other built-ins Signed-off-by: Joachim Nilsson --- plugins/time.c | 61 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/plugins/time.c b/plugins/time.c index 14e10af6..019e29eb 100644 --- a/plugins/time.c +++ b/plugins/time.c @@ -32,33 +32,58 @@ #define EPOCH_OFFSET 2208988800ULL /* Return number of seconds since midnight, 1st Jan, 1900 */ -static int cb(int type) +static char *rfctime(char *buf, size_t *len) { - int sd = STDIN_FILENO; time_t now; - struct sockaddr_storage sa; - socklen_t sa_len = sizeof(sa); - - if (SOCK_DGRAM == type) { - int dummy; - - /* Read empty datagram in UDP mode before sending reply. */ - if (-1 == recvfrom(sd, &dummy, sizeof(dummy), MSG_DONTWAIT, (struct sockaddr *)&sa, &sa_len)) - return -1; /* On error, close connection. */ - } now = time(NULL); if ((time_t)-1 == now) - return -1; /* On error, close connection. */ + return NULL; - /* Account for UNIX epoch offset */ + /* + * Account for UNIX epoch offset, and + * convert to network byte order + */ now += EPOCH_OFFSET; + now = htonl(now); - /* Convert to network byte order, not explicitly stated in RFC */ - now = htonl(now); + *len = sizeof(now); + memcpy(buf, &now, *len); - /* Send reply, ignore error, simply close connection. */ - return sendto(sd, &now, sizeof(now), MSG_DONTWAIT, (struct sockaddr *)&sa, sa_len); + return buf; +} + +static int recv_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, socklen_t *sa_len) +{ + len = recvfrom(sd, buf, sizeof(buf), MSG_DONTWAIT, sa, sa_len); + if (-1 == len) + return -1; /* On error, close connection. */ + + return 0; +} + +static int send_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, socklen_t sa_len) +{ + char *now; + + now = rfctime(buf, &len); + if (!now) + return -1; /* On error, close connection. */ + + return sendto(sd, now, len, MSG_DONTWAIT, sa, sa_len); +} + +static int cb(int type) +{ + int sd = STDIN_FILENO; + char buf[BUFSIZ]; + struct sockaddr_storage sa; + socklen_t sa_len = sizeof(sa); + + if (recv_peer(sd, buf, sizeof(buf), (struct sockaddr *)&sa, &sa_len)) + return -1; /* On error, close connection. */ + + return send_peer(sd, buf, sizeof(buf), (struct sockaddr *)&sa, sa_len); } static plugin_t plugin = { From 7dfdc2395bb459e3aec3cd23c581a33d93feeac6 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Jul 2016 17:27:37 +0200 Subject: [PATCH 5/8] Add protection against UDP inetd looping attacks This patch adds protection against a common inetd attack vector wherein the reply port to UDP inetd services is forged to an internal inetd service port. Signed-off-by: Joachim Nilsson --- README.md | 6 +++++- inetd.c | 27 +++++++++++++++++++++++++++ inetd.h | 3 +++ plugins/chargen.c | 6 +++++- plugins/daytime.c | 8 +++++++- plugins/echo.c | 18 ++++++++++++++++-- plugins/time.c | 7 ++++++- service.c | 1 + 8 files changed, 70 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b70b13f4..73e45f3d 100644 --- a/README.md +++ b/README.md @@ -476,7 +476,11 @@ default port. To run ssh on port 222, and all others on port 22: ``` Compared to Finit v1.12 you must *explicitly deny* access from `eth0`! - + +To protect against looping attacks, the inetd server will refuse UDP +service if the reply port corresponds to any internal service. Similar +to how the FreeBSD inetd operates. + **Internal Services** diff --git a/inetd.c b/inetd.c index 516217e8..43555e18 100755 --- a/inetd.c +++ b/inetd.c @@ -189,6 +189,33 @@ static void socket_cb(uev_t *UNUSED(w), void *arg, int UNUSED(events)) service_step(task); } +/* + * Refuse service if the request specifies a reply port corresponding to any internal service. + * This is done as a defense against looping attacks; the remote IP address is logged. + * http://www.freebsd.org/cgi/man.cgi?inetd(8) + * https://svnweb.freebsd.org/base/head/usr.sbin/inetd/inetd.c?revision=298909&view=markup#l2094 + */ +int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name) +{ + svc_t *svc; + char pname[NI_MAXHOST]; + + for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) { + inetd_t *i = &svc->inetd; + + if (!i->builtin || i->type != SOCK_DGRAM) + continue; + + if (((const struct sockaddr_in *)sa)->sin_port == i->port) { + getnameinfo(sa, len, pname, sizeof(pname), NULL, 0, NI_NUMERICHOST); + FLOG_WARN("%s/%s:%s/%s loop request REFUSED from %s", i->name, "UDP", name, "UDP", pname); + return 1; + } + } + + return 0; +} + /* Launch Inet socket for service. * TODO: Add filtering ALLOW/DENY per interface. */ diff --git a/inetd.h b/inetd.h index 32db966e..d0f326d0 100644 --- a/inetd.h +++ b/inetd.h @@ -46,6 +46,7 @@ typedef struct { int proto; int port; int forking; + int builtin; /* Set by built-in inetd services only */ int next_id; /* Next child job's id */ char name[10]; int (*cmd)(int type); /* internal inetd service, like 'time' */ @@ -53,6 +54,8 @@ typedef struct { TAILQ_HEAD(, inetd_filter) filters; } inetd_t; +int inetd_check_loop (struct sockaddr *sa, socklen_t len, char *name); + int inetd_start (inetd_t *inetd); void inetd_stop (inetd_t *inetd); diff --git a/plugins/chargen.c b/plugins/chargen.c index 30528b51..b3e1febc 100644 --- a/plugins/chargen.c +++ b/plugins/chargen.c @@ -27,6 +27,7 @@ #include "../plugin.h" +#define NAME "chargen" #define PATTERN "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ " static char *generator(char *buf, size_t len) @@ -61,6 +62,9 @@ static int recv_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, sockle if (-1 == len) return -1; /* On error, close connection. */ + if (inetd_check_loop(sa, *sa_len, NAME)) + return -1; + return 0; } @@ -86,7 +90,7 @@ static int cb(int type) } static plugin_t plugin = { - .name = "chargen", /* Must match the inetd /etc/services entry */ + .name = NAME, /* Must match the inetd /etc/services entry */ .inetd = { .cmd = cb }, diff --git a/plugins/daytime.c b/plugins/daytime.c index 64264176..4ea9ce11 100644 --- a/plugins/daytime.c +++ b/plugins/daytime.c @@ -28,6 +28,9 @@ #include "../plugin.h" +#define NAME "daytime" + + static char *daytime(char *buf, size_t len) { time_t t; @@ -48,6 +51,9 @@ static int recv_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, sockle if (-1 == len) return -1; /* On error, close connection. */ + if (inetd_check_loop(sa, *sa_len, NAME)) + return -1; + return 0; } @@ -72,7 +78,7 @@ static int cb(int type) } static plugin_t plugin = { - .name = "daytime", /* Must match the inetd /etc/services entry */ + .name = NAME, /* Must match the inetd /etc/services entry */ .inetd = { .cmd = cb } diff --git a/plugins/echo.c b/plugins/echo.c index 1e44f8df..311a2255 100644 --- a/plugins/echo.c +++ b/plugins/echo.c @@ -27,6 +27,20 @@ #include "../plugin.h" +#define NAME "echo" + +static int recv_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, socklen_t *sa_len) +{ + len = recvfrom(sd, buf, sizeof(buf), MSG_DONTWAIT, sa, sa_len); + if (-1 == len) + return -1; /* On error, close connection. */ + + if (inetd_check_loop(sa, *sa_len, NAME)) + return -1; + + return len; +} + static int cb(int type) { int sd = STDIN_FILENO; @@ -35,7 +49,7 @@ static int cb(int type) struct sockaddr_storage sa; socklen_t sa_len = sizeof(sa); - len = recvfrom(sd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *)&sa, &sa_len); + len = recv_peer(sd, buf, sizeof(buf), (struct sockaddr *)&sa, &sa_len); if (-1 == len) return -1; /* On error, close connection. */ @@ -43,7 +57,7 @@ static int cb(int type) } static plugin_t plugin = { - .name = "echo", /* Must match the inetd /etc/services entry */ + .name = NAME, /* Must match the inetd /etc/services entry */ .inetd = { .cmd = cb }, diff --git a/plugins/time.c b/plugins/time.c index 019e29eb..de81938a 100644 --- a/plugins/time.c +++ b/plugins/time.c @@ -28,6 +28,8 @@ #include "../plugin.h" +#define NAME "time" + /* UNIX epoch starts midnight, 1st Jan, 1970 */ #define EPOCH_OFFSET 2208988800ULL @@ -59,6 +61,9 @@ static int recv_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, sockle if (-1 == len) return -1; /* On error, close connection. */ + if (inetd_check_loop(sa, *sa_len, NAME)) + return -1; + return 0; } @@ -87,7 +92,7 @@ static int cb(int type) } static plugin_t plugin = { - .name = "time", /* Must match the inetd /etc/services entry */ + .name = NAME, /* Must match the inetd /etc/services entry */ .inetd = { .cmd = cb } diff --git a/service.c b/service.c index a5137d91..afbf4820 100755 --- a/service.c +++ b/service.c @@ -590,6 +590,7 @@ recreate: if (plugin) { /* Internal plugin provides this service */ svc->inetd.cmd = plugin->inetd.cmd; + svc->inetd.builtin = 1; } else { strlcpy(svc->args[i++], cmd, sizeof(svc->args[0])); while ((cmd = strtok(NULL, " "))) From 45bdadebb6ac05daee91e0a4f437984c5f900ec7 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Jul 2016 17:30:14 +0200 Subject: [PATCH 6/8] Minor cleanup Signed-off-by: Joachim Nilsson --- finit.c | 1 - inetd.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/finit.c b/finit.c index 535f1227..4e148bf4 100644 --- a/finit.c +++ b/finit.c @@ -38,7 +38,6 @@ #include "service.h" #include "sig.h" #include "tty.h" -#include "inetd.h" #include "sm.h" int debug = 0; diff --git a/inetd.c b/inetd.c index 43555e18..d76d6f2e 100755 --- a/inetd.c +++ b/inetd.c @@ -107,8 +107,8 @@ static int inetd_stream_peek(int sd, char *ifname) static int get_stdin(svc_t *svc) { - char ifname[IF_NAMESIZE] = "UNKNOWN"; int stdin = svc->inetd.watcher.fd; + char ifname[IF_NAMESIZE] = "UNKNOWN"; if (svc->inetd.type == SOCK_STREAM) { /* Open new client socket from server socket */ From 112196516799ba90231f809f09911a3f7c300802 Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Jul 2016 17:30:29 +0200 Subject: [PATCH 7/8] Do not build inetd plugins if inetd is disabled Signed-off-by: Joachim Nilsson --- plugins/Makefile.am | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 80533802..61cd73dd 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -15,6 +15,7 @@ if BUILD_DBUS_PLUGIN libplug_la_SOURCES += dbus.c endif +if INETD if BUILD_INETD_ECHO_PLUGIN libplug_la_SOURCES += echo.c endif @@ -34,6 +35,7 @@ endif if BUILD_INETD_TIME_PLUGIN libplug_la_SOURCES += time.c endif +endif if BUILD_LOST_PLUGIN libplug_la_SOURCES += lost.c @@ -58,6 +60,7 @@ if BUILD_DBUS_PLUGIN pkglib_LTLIBRARIES += dbus.la endif +if INETD if BUILD_INETD_ECHO_PLUGIN pkglib_LTLIBRARIES += echo.la endif @@ -77,6 +80,7 @@ endif if BUILD_INETD_TIME_PLUGIN pkglib_LTLIBRARIES += time.la endif +endif if BUILD_LOST_PLUGIN pkglib_LTLIBRARIES += lost.la From 875080d1e1a74c9f4f42a5ace644efe22048bb7c Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 18 Jul 2016 17:30:56 +0200 Subject: [PATCH 8/8] Minor update Signed-off-by: Joachim Nilsson --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 73e45f3d..2b9e3dff 100644 --- a/README.md +++ b/README.md @@ -440,7 +440,8 @@ Inetd A built-in *Internet Super Server* support was added in Finit v1.12 and v1.13, along with an internal `time` inetd service, RFC 868 (rdate). The latter is supplied as a plugin to illustrate how simple it is to -extend finit with more internal inetd services. +extend finit with more internal inetd services. Today more built-in +services are available. > Please note, not all UNIX daemons are prepared to run as inetd services. > In the example below `sshd` also need the command line argument `-i`.