// 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 #include #include #include #include #include #include #include #include #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); }