// myfingerd - a small finger server for inetd(8) // // This program reads from a ~/.lawn file if there is one that is // accessible 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 #ifdef DEBUG #include #endif #include #include #include #include #include #include #include #include #ifndef MAXULEN #define MAXULEN 32 + 2 // max length of username + CRNL #endif void handle_alarm(int sig) { exit(1); } int main(void) { if (pledge("stdio rpath unveil", NULL) == -1) { #ifdef DEBUG warn("pledge"); #endif 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, as is not // unknown from malicious hosts. struct itimerval timeout = {0}; timeout.it_value.tv_sec = 5; if (signal(SIGALRM, handle_alarm) == SIG_ERR) { #ifdef DEBUG warn("signal"); #endif goto NOPE; } if (setitimer(ITIMER_REAL, &timeout, NULL) == -1) { #ifdef DEBUG warn("setitimer"); #endif goto NOPE; } char user[MAXULEN + 1]; if (fgets(user, MAXULEN + 1, stdin) == NULL) { #ifdef DEBUG warn("fgets"); #endif goto NOPE; } size_t len = strnlen(user, MAXULEN); if (len < 2) { // need at least CRNL #ifdef DEBUG warnx("incomplete line"); #endif goto NOPE; } if (!(user[len - 1] == '\n' && user[len - 2] == '\r')) { #ifdef DEBUG warnx("no CRNL len=%zu max=%d", len, MAXULEN); #endif goto NOPE; } len -= 2; if (len == 0) { #ifdef DEBUG warnx("no user"); #endif goto ALLUSERS; } user[len] = '\0'; char *ch = user; // PORTABILITY Maybe you have _ or other such characters in your // valid usernames? I do not. while (*ch != '\0') { if (!isalnum(*ch)) { #ifdef DEBUG warnx("invalid user"); #endif goto ALLUSERS; } ch++; } struct passwd *pw = getpwnam(user); if (!pw) { #ifdef DEBUG warnx("getpwnam"); #endif goto ALLUSERS; } char *home = pw->pw_dir; if (!home) goto ALLUSERS; if (chdir(home) == -1) { #ifdef DEBUG warn("chdir"); #endif goto ALLUSERS; } // Traditional would be ".plan" and ".project" but those might // be better reserved for internal use. if (unveil(".lawn", "r") == -1) { #ifdef DEBUG warn("unveil .lawn"); #endif goto NOPE; } if (unveil(NULL, NULL) == -1) { #ifdef DEBUG warn("unveil"); #endif goto NOPE; } int fd = open(".lawn", O_RDONLY | O_NOFOLLOW); if (fd < 0) { #ifdef DEBUG warn("open"); #endif goto ALLUSERS; } if (pledge("stdio", NULL) == -1) { #ifdef DEBUG warn("pledge stdio"); #endif 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) { #ifdef DEBUG warn("pledge stdio 2"); #endif goto NOPE; } write(STDOUT_FILENO, "This is not the .plan you are looking for.\n", 44); exit(0); NOPE: exit(1); }