💾 Archived View for thrig.me › tech › openbsd › fingerd.gmi captured on 2024-06-16 at 13:37:28. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-11-14)
-=-=-=-=-=-=-
fingerd can be enabled on OpenBSD, though the default configuration is perhaps not suitable to expose to the whole internet, as this will expose the IP addresses that one logs in from. This may not be a good idea, especially if one is prone to annoy young males at their "wild age", as Stilgar puts it, or for various other reasons. It is difficult to avoid creepy corporate tracking these days, but one may not want to give them free hand-outs.
You could only login over a wireguard tunnel, in which case the IP address shown will probably be RFC 1918 something, but it's probably more sensible to expose only the information you want to the whole internet, which probably does not include the IP addresses you use to connect from.
The last step should be to open up TCP/79 in the firewall to the internet. The first step might be to ensure that TCP/79 is blocked by the firewall. Next, write a custom script for fingerd to execute. Most simply:
$ cat /usr/local/libexec/lawn #!/bin/sh echo get off my lawn $ chmod +x /usr/local/libexec/lawn
And then to modify /etc/inetd.conf to execute this program:
$ grep fingerd /etc/inetd.conf finger stream tcp nowait _fingerd /usr/local/libexec/fingerd fingerd -lsmP /usr/libexec/lawn finger stream tcp6 nowait _fingerd /usr/local/libexec/fingerd fingerd -lsmP /usr/libexec/lawn
You may want to remove the -l flag to cut down on log noise? Once things are debugged, that is.
inetd will need to be enabled and started. This should properly be done with configuration management (Ansible or whatnot), if you are that organized.
$ doas rcctl enable inetd $ doas rcctl start inetd $ finger root@localhost [localhost/127.0.0.1] get off my lawn
The observant may notice that our lawn script gives the same answer regardless of the user, or lack of user given:
finger @localhost finger squarebobspongepants@localhost ...
Here the dreaded "second-system effect" comes into play, as we must bloat our elegant lawn script with features and wingdings and cover various annoying edge cases, such as someone connecting but then leaving the connection open without sending anything.
// myfingerd - a small finger server (to be called by inetd(8)) // // this program reads from a ~/.lawn file if there is one for a valid // user, or otherwise returns a generic message or otherwise exits with // a non-zero exit status word should something go awry #include <sys/time.h> #include <ctype.h> #include <fcntl.h> #include <pwd.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define MAXLEN 32 + 2 // max length of username + CRNL void handle_alarm(int sig) { exit(1); } int main(void) { if (pledge("stdio rpath unveil", NULL) == -1) goto NOPE; // fail if the request takes too long, e.g. when the remote end // does a `telnet example.org 79` and then walks away struct itimerval timeout = {0}; timeout.it_value.tv_sec = 5; if (signal(SIGALRM, handle_alarm) == SIG_ERR) goto NOPE; if (setitimer(ITIMER_REAL, &timeout, NULL) == -1) goto NOPE; char user[MAXLEN + 1]; if (fgets(user, MAXLEN + 1, stdin) == NULL) goto NOPE; size_t len = strnlen(user, MAXLEN); if (len < 2) goto NOPE; if (!(user[len - 1] == '\n' && user[len - 2] == '\r')) { if (len == MAXLEN) goto ALLUSERS; // the input filled our buffer else goto NOPE; // did not terminate with CRNL } len -= 2; if (len == 0) goto ALLUSERS; user[len] = '\0'; char *ch = user; // maybe you have _ or other such in your usernames? I don't while (*ch != '\0') { if (!isalnum(*ch)) goto ALLUSERS; ch++; } struct passwd *pw = getpwnam(user); if (!pw) goto ALLUSERS; char *home = pw->pw_dir; if (!home) goto ALLUSERS; if (chdir(home) == -1) goto ALLUSERS; // traditional would be ".plan" and ".project" but those // might be better reserved for internal use if (unveil(".lawn", "r") == -1) goto NOPE; if (unveil(NULL, NULL) == -1) goto NOPE; int fd = open(".lawn", O_RDONLY | O_NOFOLLOW); if (fd < 0) goto ALLUSERS; if (pledge("stdio", NULL) == -1) goto NOPE; char buf[4096]; while (1) { ssize_t amount = read(fd, buf, sizeof(buf)); if (amount <= 0) break; write(STDOUT_FILENO, buf, (size_t) amount); } //close(fd); exit(0); ALLUSERS: if (pledge("stdio", NULL) == -1) goto NOPE; write(STDOUT_FILENO, "get off our lawns\n", 18); exit(0); NOPE: exit(1); }
finger stream tcp nowait _fingerd /usr/local/libexec/myfingerd finger stream tcp6 nowait _fingerd /usr/local/libexec/myfingerd
A security risk here is that an attacker might use this script to enumerate users on the system, perhaps to find people to spam, phish, check for weak passwords by way of SMTP, etc. Attackers can be terribly clever here and may be able to use timing information to determine if an account exists or not. If this is a concern, maybe delay the responses so that "no account exists" cannot be distinguished from "there was an account, but they did not have a .lawn file".
gemini://gemini.thebackupbox.net/~epoch/blog/fingering
Probably you should not run finger on the squeaky clean corporate and spammer overrun internet. But where is the fun in that?
tags #c #finger #inetd #openbsd