fingerd

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/libexec/lawn
    #!/bin/sh
    echo get off my lawn
    $ chmod +x /usr/libexec/lawn

And then to modify /etc/inetd.conf to execute this program:

    $ grep fingerd /etc/inetd.conf
    finger          stream  tcp     nowait  _fingerd /usr/libexec/fingerd   fingerd -lsmP /usr/libexec/lawn
    finger          stream  tcp6    nowait  _fingerd /usr/libexec/fingerd   fingerd -lsmP /usr/libexec/lawn

You may want to remove the -l flag to cut down on log noise?

inetd will need to be enabled and started. This all could be done with configuration management, 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

But What About Multiple Users?

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.

    // lawn2-electric-boogaloo - lawn with features and bloat. it
    // reads a ~/.lawn file or falls back to a generic message
    
    #include <ctype.h>
    #include <fcntl.h>
    #include <pwd.h>
    #include <stdio.h>
    #include <unistd.h>
    
    #define MAX_USERNAME_LEN 32
    
    int
    main(int argc, char *argv[])
    {
        close(STDIN_FILENO);
        if (pledge("stdio rpath unveil", NULL) == -1) goto NOPE;
    
        // fingerd on OpenBSD inserts a -- so we must remove that or
        // any other flags along for the ride, and ape the flags that
        // finger(1) supports
        int unused;
        while ((unused = getopt(argc, argv, "lmMpsho")) != -1)
            ;
        argc -= optind;
        argv += optind;
        if (argc == 0) goto ALLUSERS;
    
        // is the username somewhat sane?
        char *ch = *argv;
        if (*ch == '\0') goto ALLUSERS;
        int i    = 0;
        while (*ch != '\0') {
            if (++i > MAX_USERNAME_LEN) goto ALLUSERS;
            if (!isalnum(*ch)) goto ALLUSERS;
            ch++;
        }
    
        // does the user have a home directory?
        struct passwd *pw = getpwnam(*argv);
        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
        unveil(".lawn", "r");
        unveil(NULL, NULL);
        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(STDOUT_FILENO);
        exit(0);
    
    ALLUSERS:
        if (pledge("stdio", NULL) == -1) goto NOPE;
        write(STDOUT_FILENO, "get off our lawns\n", 18);
        close(STDOUT_FILENO);
        exit(0);

    NOPE:
        close(STDOUT_FILENO);
        exit(1);
    }

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.

On the squeaky clean corporate but spammer and attacker overrun internet you probably do not want to be running a fingerd. But where is the fun in that?

Piled Higher and Deeper

An even more complicated option would be to run a custom fingerd with all sorts of features and hopefully no security exploits. This could be less expensive than having to fork off programs for every request. Implementations can be found on the internet, or it might be good practice to write one.

External Link

gemini://gemini.thebackupbox.net/~epoch/blog/fingering

Link Making Not-Link

Would be text that contains #finger and some other words?

tags #finger #openbsd #c