/* Copyright (C) 2001,2002,2005 Dmitry V. Levin A privileged helper for utmp/wtmp updates. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _GNU_SOURCE # define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #ifdef __GLIBC__ # include #elif defined(__FreeBSD__) # include #else # error Unsupported platform #endif /* __GLIBC__ || __FreeBSD__ */ #define DEV_PREFIX "/dev/" #define DEV_PREFIX_LEN (sizeof(DEV_PREFIX)-1) static void __attribute__ ((__noreturn__)) usage(void) { #ifdef UTEMPTER_DEBUG fprintf(stderr, "Usage: utempter add []\n" " utempter del\n"); #endif exit(EXIT_FAILURE); } static void validate_device(const char *device) { int flags; struct stat stb; if (strncmp(device, DEV_PREFIX, DEV_PREFIX_LEN)) { #ifdef UTEMPTER_DEBUG fprintf(stderr, "utempter: invalid device name\n"); #endif exit(EXIT_FAILURE); } if ((flags = fcntl(STDIN_FILENO, F_GETFL, 0)) < 0) { #ifdef UTEMPTER_DEBUG fprintf(stderr, "utempter: fcntl: %s\n", strerror(errno)); #endif exit(EXIT_FAILURE); } if ((flags & O_RDWR) != O_RDWR) { #ifdef UTEMPTER_DEBUG fprintf(stderr, "utempter: invalid descriptor mode\n"); #endif exit(EXIT_FAILURE); } if (stat(device, &stb) < 0) { #ifdef UTEMPTER_DEBUG fprintf(stderr, "utempter: %s: %s\n", device, strerror(errno)); #endif exit(EXIT_FAILURE); } if (getuid() != stb.st_uid) { #ifdef UTEMPTER_DEBUG fprintf(stderr, "utempter: %s belongs to another user\n", device); #endif exit(EXIT_FAILURE); } } static int write_uwtmp_record(const char *user, const char *term, const char *host, #ifdef __GLIBC__ pid_t pid, #endif int add) { struct utmp ut; struct timeval tv; #ifdef __GLIBC__ int offset; #endif memset(&ut, 0, sizeof(ut)); memset(&tv, 0, sizeof(tv)); (void) gettimeofday(&tv, 0); strncpy(ut.ut_name, user, sizeof(ut.ut_name)); strncpy(ut.ut_line, term, sizeof(ut.ut_line)); if (host) strncpy(ut.ut_host, host, sizeof(ut.ut_host)); #ifdef __GLIBC__ offset = strlen(term) - sizeof(ut.ut_id); if (offset < 0) offset = 0; strncpy(ut.ut_id, term + offset, sizeof(ut.ut_id)); if (add) ut.ut_type = USER_PROCESS; else ut.ut_type = DEAD_PROCESS; ut.ut_pid = pid; ut.ut_tv.tv_sec = tv.tv_sec; ut.ut_tv.tv_usec = tv.tv_usec; setutent(); if (!pututline(&ut)) { #ifdef UTEMPTER_DEBUG fprintf(stderr, "utempter: pututline: %s\n", strerror(errno)); #endif exit(EXIT_FAILURE); } endutent(); (void) updwtmp(_PATH_WTMP, &ut); #elif defined(__FreeBSD__) ut.ut_time = tv.tv_sec; if (add) { login(&ut); } else { if (logout(term) != 1) { #ifdef UTEMPTER_DEBUG fprintf(stderr, "utempter: logout: %s\n", strerror(errno)); #endif exit(EXIT_FAILURE); } } #endif /* __GLIBC__ || __FreeBSD__ */ #ifdef UTEMPTER_DEBUG fprintf(stderr, "utempter: DEBUG: utmp/wtmp record %s for terminal '%s'\n", add ? "added" : "removed", term); #endif return EXIT_SUCCESS; } int main(int argc, const char *argv[]) { const char *device, *host; struct passwd *pw; pid_t pid; int add = 0, i; for (i = 0; i <= 2; ++i) { struct stat sb; if (fstat(i, &sb) < 0) /* At this stage, we shouldn't even report error. */ exit(EXIT_FAILURE); } if (argc < 2) usage(); if (!strcmp(argv[1], "add")) { if (argc > 3) usage(); add = 1; } else if (!strcmp(argv[1], "del")) { if (argc != 2) usage(); add = 0; } else usage(); host = argv[2]; pid = getppid(); if (pid == 1) { #ifdef UTEMPTER_DEBUG fprintf(stderr, "utempter: parent process should not be init\n"); #endif exit(EXIT_FAILURE); } pw = getpwuid(getuid()); if (!pw || !pw->pw_name) { #ifdef UTEMPTER_DEBUG fprintf(stderr, "utempter: cannot find valid user with uid=%u\n", getuid()); #endif exit(EXIT_FAILURE); } device = ptsname(STDIN_FILENO); if (!device) { #ifdef UTEMPTER_DEBUG fprintf(stderr, "utempter: cannot find slave pty: %s\n", strerror(errno)); #endif exit(EXIT_FAILURE); } validate_device(device); return write_uwtmp_record(pw->pw_name, device + DEV_PREFIX_LEN, host, #ifdef __GLIBC__ pid, #endif add); }