123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873 |
- /******************************************************************************
- * A module for Linux-PAM that will cache authentication results, inspired by
- * (and implemented with an eye toward being mixable with) sudo.
- *
- * Copyright (c) 2002 Red Hat, Inc.
- * Written by Nalin Dahyabhai <nalin@redhat.com>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * ALTERNATIVELY, this product may be distributed under the terms of
- * the GNU Public License, in which case the provisions of the GPL are
- * required INSTEAD OF the above restrictions. (This clause is
- * necessary due to a potential bad interaction between the GPL and
- * the restrictions contained in a BSD-style copyright.)
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
- #include "config.h"
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <limits.h>
- #include <pwd.h>
- #include <signal.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <sys/time.h>
- #include <unistd.h>
- #include <utmp.h>
- #include <syslog.h>
- #include <paths.h>
- #ifdef WITH_OPENSSL
- #include "hmac_openssl_wrapper.h"
- #else
- #include "hmacsha1.h"
- #endif /* WITH_OPENSSL */
- #include <security/pam_modules.h>
- #include <security/_pam_macros.h>
- #include <security/pam_ext.h>
- #include <security/pam_modutil.h>
- #include "pam_inline.h"
- /* The default timeout we use is 5 minutes, which matches the sudo default
- * for the timestamp_timeout parameter. */
- #define DEFAULT_TIMESTAMP_TIMEOUT (5 * 60)
- #define MODULE "pam_timestamp"
- #define TIMESTAMPDIR _PATH_VARRUN MODULE
- #define TIMESTAMPKEY TIMESTAMPDIR "/_pam_timestamp_key"
- /* Various buffers we use need to be at least as large as either PATH_MAX or
- * LINE_MAX, so choose the larger of the two. */
- #if (LINE_MAX > PATH_MAX)
- #define BUFLEN LINE_MAX
- #else
- #define BUFLEN PATH_MAX
- #endif
- #define ROOT_USER 0
- #define ROOT_GROUP 0
- /* Return PAM_SUCCESS if the given directory looks "safe". */
- static int
- check_dir_perms(pam_handle_t *pamh, const char *tdir)
- {
- char scratch[BUFLEN];
- struct stat st;
- int i;
- /* Check that the directory is "safe". */
- if ((tdir == NULL) || (strlen(tdir) == 0)) {
- return PAM_AUTH_ERR;
- }
- /* Iterate over the path, checking intermediate directories. */
- memset(scratch, 0, sizeof(scratch));
- for (i = 0; (tdir[i] != '\0') && (i < (int)sizeof(scratch)); i++) {
- scratch[i] = tdir[i];
- if ((scratch[i] == '/') || (tdir[i + 1] == '\0')) {
- /* We now have the name of a directory in the path, so
- * we need to check it. */
- if ((lstat(scratch, &st) == -1) && (errno != ENOENT)) {
- pam_syslog(pamh, LOG_ERR,
- "unable to read `%s': %m",
- scratch);
- return PAM_AUTH_ERR;
- }
- if (!S_ISDIR(st.st_mode)) {
- pam_syslog(pamh, LOG_ERR,
- "`%s' is not a directory",
- scratch);
- return PAM_AUTH_ERR;
- }
- if (S_ISLNK(st.st_mode)) {
- pam_syslog(pamh, LOG_ERR,
- "`%s' is a symbolic link",
- scratch);
- return PAM_AUTH_ERR;
- }
- if (st.st_uid != 0) {
- pam_syslog(pamh, LOG_ERR,
- "`%s' owner UID != 0",
- scratch);
- return PAM_AUTH_ERR;
- }
- if (st.st_gid != 0) {
- pam_syslog(pamh, LOG_ERR,
- "`%s' owner GID != 0",
- scratch);
- return PAM_AUTH_ERR;
- }
- if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
- pam_syslog(pamh, LOG_ERR,
- "`%s' permissions are lax",
- scratch);
- return PAM_AUTH_ERR;
- }
- }
- }
- return PAM_SUCCESS;
- }
- /* Validate a tty pathname as actually belonging to a tty, and return its base
- * name if it's valid. */
- static const char *
- check_tty(const char *tty)
- {
- /* Check that we're not being set up to take a fall. */
- if ((tty == NULL) || (strlen(tty) == 0)) {
- return NULL;
- }
- /* Pull out the meaningful part of the tty's name. */
- if (strchr(tty, '/') != NULL) {
- if (pam_str_skip_prefix(tty, "/dev/") == NULL) {
- /* Make sure the device node is actually in /dev/,
- * noted by Michal Zalewski. */
- return NULL;
- }
- tty = strrchr(tty, '/') + 1;
- }
- /* Make sure the tty wasn't actually a directory (no basename). */
- if (!strlen(tty) || !strcmp(tty, ".") || !strcmp(tty, "..")) {
- return NULL;
- }
- return tty;
- }
- /* Determine the right path name for a given user's timestamp. */
- static int
- format_timestamp_name(char *path, size_t len,
- const char *timestamp_dir,
- const char *tty,
- const char *ruser,
- const char *user)
- {
- if (strcmp(ruser, user) == 0) {
- return snprintf(path, len, "%s/%s/%s", timestamp_dir,
- ruser, tty);
- } else {
- return snprintf(path, len, "%s/%s/%s:%s", timestamp_dir,
- ruser, tty, user);
- }
- }
- /* Check if a given timestamp date, when compared to a current time, fits
- * within the given interval. */
- static int
- timestamp_good(time_t then, time_t now, time_t interval)
- {
- if (((now >= then) && ((now - then) < interval)) ||
- ((now < then) && ((then - now) < (2 * interval)))) {
- return PAM_SUCCESS;
- }
- return PAM_AUTH_ERR;
- }
- static int
- check_login_time(const char *ruser, time_t timestamp)
- {
- struct utmp utbuf, *ut;
- time_t oldest_login = 0;
- setutent();
- while(
- #ifdef HAVE_GETUTENT_R
- !getutent_r(&utbuf, &ut)
- #else
- (ut = getutent()) != NULL
- #endif
- ) {
- if (ut->ut_type != USER_PROCESS) {
- continue;
- }
- if (strncmp(ruser, ut->ut_user, sizeof(ut->ut_user)) != 0) {
- continue;
- }
- if (oldest_login == 0 || oldest_login > ut->ut_tv.tv_sec) {
- oldest_login = ut->ut_tv.tv_sec;
- }
- }
- endutent();
- if(oldest_login == 0 || timestamp < oldest_login) {
- return PAM_AUTH_ERR;
- }
- return PAM_SUCCESS;
- }
- #ifndef PAM_TIMESTAMP_MAIN
- static int
- get_ruser(pam_handle_t *pamh, char *ruserbuf, size_t ruserbuflen)
- {
- const void *ruser;
- struct passwd *pwd;
- if (ruserbuf == NULL || ruserbuflen < 1)
- return -2;
- /* Get the name of the source user. */
- if (pam_get_item(pamh, PAM_RUSER, &ruser) != PAM_SUCCESS) {
- ruser = NULL;
- }
- if ((ruser == NULL) || (strlen(ruser) == 0)) {
- /* Barring that, use the current RUID. */
- pwd = pam_modutil_getpwuid(pamh, getuid());
- if (pwd != NULL) {
- ruser = pwd->pw_name;
- }
- } else {
- /*
- * This ruser is used by format_timestamp_name as a component
- * of constructed timestamp pathname, so ".", "..", and '/'
- * are disallowed to avoid potential path traversal issues.
- */
- if (!strcmp(ruser, ".") ||
- !strcmp(ruser, "..") ||
- strchr(ruser, '/')) {
- ruser = NULL;
- }
- }
- if (ruser == NULL || strlen(ruser) >= ruserbuflen) {
- *ruserbuf = '\0';
- return -1;
- }
- strcpy(ruserbuf, ruser);
- return 0;
- }
- /* Get the path to the timestamp to use. */
- static int
- get_timestamp_name(pam_handle_t *pamh, int argc, const char **argv,
- char *path, size_t len)
- {
- const char *user, *tty;
- const void *void_tty;
- const char *tdir = TIMESTAMPDIR;
- char ruser[BUFLEN];
- int i, debug = 0;
- /* Parse arguments. */
- for (i = 0; i < argc; i++) {
- if (strcmp(argv[i], "debug") == 0) {
- debug = 1;
- }
- }
- for (i = 0; i < argc; i++) {
- const char *str;
- if ((str = pam_str_skip_prefix(argv[i], "timestampdir=")) != NULL) {
- tdir = str;
- if (debug) {
- pam_syslog(pamh, LOG_DEBUG,
- "storing timestamps in `%s'",
- tdir);
- }
- }
- }
- i = check_dir_perms(pamh, tdir);
- if (i != PAM_SUCCESS) {
- return i;
- }
- /* Get the name of the target user. */
- if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || user[0] == '\0') {
- return PAM_AUTH_ERR;
- }
- if (debug) {
- pam_syslog(pamh, LOG_DEBUG, "becoming user `%s'", user);
- }
- /* Get the name of the source user. */
- if (get_ruser(pamh, ruser, sizeof(ruser)) || strlen(ruser) == 0) {
- return PAM_AUTH_ERR;
- }
- if (debug) {
- pam_syslog(pamh, LOG_DEBUG, "currently user `%s'", ruser);
- }
- /* Get the name of the terminal. */
- if (pam_get_item(pamh, PAM_TTY, &void_tty) != PAM_SUCCESS) {
- tty = NULL;
- } else {
- tty = void_tty;
- }
- if ((tty == NULL) || (strlen(tty) == 0)) {
- tty = ttyname(STDIN_FILENO);
- if ((tty == NULL) || (strlen(tty) == 0)) {
- tty = ttyname(STDOUT_FILENO);
- }
- if ((tty == NULL) || (strlen(tty) == 0)) {
- tty = ttyname(STDERR_FILENO);
- }
- if ((tty == NULL) || (strlen(tty) == 0)) {
- /* Match sudo's behavior for this case. */
- tty = "unknown";
- }
- }
- if (debug) {
- pam_syslog(pamh, LOG_DEBUG, "tty is `%s'", tty);
- }
- /* Snip off all but the last part of the tty name. */
- tty = check_tty(tty);
- if (tty == NULL) {
- return PAM_AUTH_ERR;
- }
- /* Generate the name of the file used to cache auth results. These
- * paths should jive with sudo's per-tty naming scheme. */
- if (format_timestamp_name(path, len, tdir, tty, ruser, user) >= (int)len) {
- return PAM_AUTH_ERR;
- }
- if (debug) {
- pam_syslog(pamh, LOG_DEBUG, "using timestamp file `%s'", path);
- }
- return PAM_SUCCESS;
- }
- /* Tell the user that access has been granted. */
- static void
- verbose_success(pam_handle_t *pamh, long diff)
- {
- pam_info(pamh, _("Access has been granted"
- " (last access was %ld seconds ago)."), diff);
- }
- int
- pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
- {
- struct stat st;
- time_t interval = DEFAULT_TIMESTAMP_TIMEOUT;
- int i, fd, debug = 0, verbose = 0;
- char path[BUFLEN], *p, *message, *message_end;
- long tmp;
- const void *void_service;
- const char *service;
- time_t now, then;
- /* Parse arguments. */
- for (i = 0; i < argc; i++) {
- if (strcmp(argv[i], "debug") == 0) {
- debug = 1;
- }
- }
- for (i = 0; i < argc; i++) {
- const char *str;
- if ((str = pam_str_skip_prefix(argv[i], "timestamp_timeout=")) != NULL) {
- tmp = strtol(str, &p, 0);
- if ((p != NULL) && (*p == '\0')) {
- interval = tmp;
- if (debug) {
- pam_syslog(pamh, LOG_DEBUG,
- "setting timeout to %ld"
- " seconds", (long)interval);
- }
- }
- } else
- if (strcmp(argv[i], "verbose") == 0) {
- verbose = 1;
- if (debug) {
- pam_syslog(pamh, LOG_DEBUG,
- "becoming more verbose");
- }
- }
- }
- if (flags & PAM_SILENT) {
- verbose = 0;
- }
- /* Get the name of the timestamp file. */
- if (get_timestamp_name(pamh, argc, argv,
- path, sizeof(path)) != PAM_SUCCESS) {
- return PAM_AUTH_ERR;
- }
- /* Get the name of the service. */
- if (pam_get_item(pamh, PAM_SERVICE, &void_service) != PAM_SUCCESS) {
- service = NULL;
- } else {
- service = void_service;
- }
- if ((service == NULL) || (strlen(service) == 0)) {
- service = "(unknown)";
- }
- /* Open the timestamp file. */
- fd = open(path, O_RDONLY | O_NOFOLLOW);
- if (fd == -1) {
- if (debug) {
- pam_syslog(pamh, LOG_DEBUG,
- "cannot open timestamp `%s': %m",
- path);
- }
- return PAM_AUTH_ERR;
- }
- if (fstat(fd, &st) == 0) {
- int count;
- void *mac;
- size_t maclen;
- char ruser[BUFLEN];
- /* Check that the file is owned by the superuser. */
- if ((st.st_uid != 0) || (st.st_gid != 0)) {
- pam_syslog(pamh, LOG_ERR, "timestamp file `%s' is "
- "not owned by root", path);
- close(fd);
- return PAM_AUTH_ERR;
- }
- /* Check that the file is a normal file. */
- if (!(S_ISREG(st.st_mode))) {
- pam_syslog(pamh, LOG_ERR, "timestamp file `%s' is "
- "not a regular file", path);
- close(fd);
- return PAM_AUTH_ERR;
- }
- #ifdef WITH_OPENSSL
- if (hmac_size(pamh, debug, &maclen)) {
- return PAM_AUTH_ERR;
- }
- #else
- maclen = hmac_sha1_size();
- #endif /* WITH_OPENSSL */
- /* Check that the file is the expected size. */
- if (st.st_size == 0) {
- /* Invalid, but may have been created by sudo. */
- close(fd);
- return PAM_AUTH_ERR;
- }
- if (st.st_size !=
- (off_t)(strlen(path) + 1 + sizeof(then) + maclen)) {
- pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' "
- "appears to be corrupted", path);
- close(fd);
- return PAM_AUTH_ERR;
- }
- /* Read the file contents. */
- message = malloc(st.st_size);
- count = 0;
- if (!message) {
- close(fd);
- return PAM_BUF_ERR;
- }
- while (count < st.st_size) {
- i = read(fd, message + count, st.st_size - count);
- if ((i == 0) || (i == -1)) {
- break;
- }
- count += i;
- }
- if (count < st.st_size) {
- pam_syslog(pamh, LOG_NOTICE, "error reading timestamp "
- "file `%s': %m", path);
- close(fd);
- free(message);
- return PAM_AUTH_ERR;
- }
- message_end = message + strlen(path) + 1 + sizeof(then);
- /* Regenerate the MAC. */
- #ifdef WITH_OPENSSL
- if (hmac_generate(pamh, debug, &mac, &maclen, TIMESTAMPKEY,
- ROOT_USER, ROOT_GROUP, message, message_end - message)) {
- close(fd);
- free(message);
- return PAM_AUTH_ERR;
- }
- #else
- hmac_sha1_generate_file(pamh, &mac, &maclen, TIMESTAMPKEY,
- ROOT_USER, ROOT_GROUP, message, message_end - message);
- #endif /* WITH_OPENSSL */
- if ((mac == NULL) ||
- (memcmp(path, message, strlen(path)) != 0) ||
- (memcmp(mac, message_end, maclen) != 0)) {
- pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' is "
- "corrupted", path);
- close(fd);
- free(mac);
- free(message);
- return PAM_AUTH_ERR;
- }
- free(mac);
- memmove(&then, message + strlen(path) + 1, sizeof(then));
- free(message);
- /* Check oldest login against timestamp */
- if (get_ruser(pamh, ruser, sizeof(ruser)))
- {
- close(fd);
- return PAM_AUTH_ERR;
- }
- if (check_login_time(ruser, then) != PAM_SUCCESS)
- {
- pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' is "
- "older than oldest login, disallowing "
- "access to %s for user %s",
- path, service, ruser);
- close(fd);
- return PAM_AUTH_ERR;
- }
- /* Compare the dates. */
- now = time(NULL);
- if (timestamp_good(then, now, interval) == PAM_SUCCESS) {
- close(fd);
- pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' is "
- "only %ld seconds old, allowing access to %s "
- "for user %s", path, (long) (now - st.st_mtime),
- service, ruser);
- if (verbose) {
- verbose_success(pamh, now - st.st_mtime);
- }
- return PAM_SUCCESS;
- } else {
- close(fd);
- pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' has "
- "unacceptable age (%ld seconds), disallowing "
- "access to %s for user %s",
- path, (long) (now - st.st_mtime),
- service, ruser);
- return PAM_AUTH_ERR;
- }
- }
- close(fd);
- /* Fail by default. */
- return PAM_AUTH_ERR;
- }
- int
- pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, int argc UNUSED, const char **argv UNUSED)
- {
- return PAM_SUCCESS;
- }
- int
- pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv)
- {
- char path[BUFLEN], subdir[BUFLEN], *text, *p;
- void *mac;
- size_t maclen;
- time_t now;
- int fd, i, debug = 0;
- /* Parse arguments. */
- for (i = 0; i < argc; i++) {
- if (strcmp(argv[i], "debug") == 0) {
- debug = 1;
- }
- }
- /* Get the name of the timestamp file. */
- if (get_timestamp_name(pamh, argc, argv,
- path, sizeof(path)) != PAM_SUCCESS) {
- return PAM_SESSION_ERR;
- }
- /* Create the directory for the timestamp file if it doesn't already
- * exist. */
- for (i = 1; i < (int) sizeof(path) && path[i] != '\0'; i++) {
- if (path[i] == '/') {
- /* Attempt to create the directory. */
- memcpy(subdir, path, i);
- subdir[i] = '\0';
- if (mkdir(subdir, 0700) == 0) {
- /* Attempt to set the owner to the superuser. */
- if (lchown(subdir, 0, 0) != 0) {
- if (debug) {
- pam_syslog(pamh, LOG_DEBUG,
- "error setting permissions on `%s': %m",
- subdir);
- }
- return PAM_SESSION_ERR;
- }
- } else {
- if (errno != EEXIST) {
- if (debug) {
- pam_syslog(pamh, LOG_DEBUG,
- "error creating directory `%s': %m",
- subdir);
- }
- return PAM_SESSION_ERR;
- }
- }
- }
- }
- #ifdef WITH_OPENSSL
- if (hmac_size(pamh, debug, &maclen)) {
- return PAM_SESSION_ERR;
- }
- #else
- maclen = hmac_sha1_size();
- #endif /* WITH_OPENSSL */
- /* Generate the message. */
- text = malloc(strlen(path) + 1 + sizeof(now) + maclen);
- if (text == NULL) {
- pam_syslog(pamh, LOG_CRIT, "unable to allocate memory: %m");
- return PAM_SESSION_ERR;
- }
- p = text;
- strcpy(text, path);
- p += strlen(path) + 1;
- now = time(NULL);
- memmove(p, &now, sizeof(now));
- p += sizeof(now);
- /* Generate the MAC and append it to the plaintext. */
- #ifdef WITH_OPENSSL
- if (hmac_generate(pamh, debug, &mac, &maclen, TIMESTAMPKEY,
- ROOT_USER, ROOT_GROUP, text, p - text)) {
- free(text);
- return PAM_SESSION_ERR;
- }
- #else
- hmac_sha1_generate_file(pamh, &mac, &maclen, TIMESTAMPKEY,
- ROOT_USER, ROOT_GROUP, text, p - text);
- if (mac == NULL) {
- pam_syslog(pamh, LOG_ERR, "failure generating MAC: %m");
- free(text);
- return PAM_SESSION_ERR;
- }
- #endif /* WITH_OPENSSL */
- memmove(p, mac, maclen);
- p += maclen;
- free(mac);
- /* Open the file. */
- fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
- if (fd == -1) {
- pam_syslog(pamh, LOG_ERR, "unable to open `%s': %m", path);
- free(text);
- return PAM_SESSION_ERR;
- }
- /* Attempt to set the owner to the superuser. */
- if (fchown(fd, 0, 0) != 0) {
- if (debug) {
- pam_syslog(pamh, LOG_DEBUG,
- "error setting ownership of `%s': %m",
- path);
- }
- close(fd);
- free(text);
- return PAM_SESSION_ERR;
- }
- /* Write the timestamp to the file. */
- if (write(fd, text, p - text) != p - text) {
- pam_syslog(pamh, LOG_ERR, "unable to write to `%s': %m", path);
- close(fd);
- free(text);
- return PAM_SESSION_ERR;
- }
- /* Close the file and return successfully. */
- close(fd);
- free(text);
- pam_syslog(pamh, LOG_DEBUG, "updated timestamp file `%s'", path);
- return PAM_SUCCESS;
- }
- int
- pam_sm_close_session(pam_handle_t *pamh UNUSED, int flags UNUSED, int argc UNUSED, const char **argv UNUSED)
- {
- return PAM_SUCCESS;
- }
- #else /* PAM_TIMESTAMP_MAIN */
- #define USAGE "Usage: %s [[-k] | [-d]] [target user]\n"
- #define CHECK_INTERVAL 7
- int
- main(int argc, char **argv)
- {
- int i, retval = 0, dflag = 0, kflag = 0;
- const char *target_user = NULL, *user = NULL, *tty = NULL;
- struct passwd *pwd;
- struct timeval tv;
- fd_set write_fds;
- char path[BUFLEN];
- struct stat st;
- /* Check that there's nothing funny going on with stdio. */
- if ((fstat(STDIN_FILENO, &st) == -1) ||
- (fstat(STDOUT_FILENO, &st) == -1) ||
- (fstat(STDERR_FILENO, &st) == -1)) {
- /* Appropriate the "no controlling tty" error code. */
- return 3;
- }
- /* Parse arguments. */
- while ((i = getopt(argc, argv, "dk")) != -1) {
- switch (i) {
- case 'd':
- dflag++;
- break;
- case 'k':
- kflag++;
- break;
- default:
- fprintf(stderr, USAGE, argv[0]);
- return 1;
- break;
- }
- }
- /* Bail if both -k and -d are given together. */
- if ((kflag + dflag) > 1) {
- fprintf(stderr, USAGE, argv[0]);
- return 1;
- }
- /* Check that we're setuid. */
- if (geteuid() != 0) {
- fprintf(stderr, "%s must be setuid root\n",
- argv[0]);
- retval = 2;
- }
- /* Check that we have a controlling tty. */
- tty = ttyname(STDIN_FILENO);
- if ((tty == NULL) || (strlen(tty) == 0)) {
- tty = ttyname(STDOUT_FILENO);
- }
- if ((tty == NULL) || (strlen(tty) == 0)) {
- tty = ttyname(STDERR_FILENO);
- }
- if ((tty == NULL) || (strlen(tty) == 0)) {
- tty = "unknown";
- }
- /* Get the name of the invoking (requesting) user. */
- pwd = getpwuid(getuid());
- if (pwd == NULL) {
- retval = 4;
- }
- /* Get the name of the target user. */
- user = strdup(pwd->pw_name);
- if (user == NULL) {
- retval = 4;
- } else {
- target_user = (optind < argc) ? argv[optind] : user;
- if ((strchr(target_user, '.') != NULL) ||
- (strchr(target_user, '/') != NULL) ||
- (strchr(target_user, '%') != NULL)) {
- fprintf(stderr, "unknown user: %s\n",
- target_user);
- retval = 4;
- }
- }
- /* Sanity check the tty to make sure we should be checking
- * for timestamps which pertain to it. */
- if (retval == 0) {
- tty = check_tty(tty);
- if (tty == NULL) {
- fprintf(stderr, "invalid tty\n");
- retval = 6;
- }
- }
- do {
- /* Sanity check the timestamp directory itself. */
- if (retval == 0) {
- if (check_dir_perms(NULL, TIMESTAMPDIR) != PAM_SUCCESS) {
- retval = 5;
- }
- }
- if (retval == 0) {
- /* Generate the name of the timestamp file. */
- format_timestamp_name(path, sizeof(path), TIMESTAMPDIR,
- tty, user, target_user);
- }
- if (retval == 0) {
- if (kflag) {
- /* Remove the timestamp. */
- if (lstat(path, &st) != -1) {
- retval = unlink(path);
- }
- } else {
- /* Check the timestamp. */
- if (lstat(path, &st) != -1) {
- /* Check oldest login against timestamp */
- if (check_login_time(user, st.st_mtime) != PAM_SUCCESS) {
- retval = 7;
- } else if (timestamp_good(st.st_mtime, time(NULL),
- DEFAULT_TIMESTAMP_TIMEOUT) != PAM_SUCCESS) {
- retval = 7;
- }
- } else {
- retval = 7;
- }
- }
- }
- if (dflag > 0) {
- struct timeval now;
- /* Send the would-be-returned value to our parent. */
- signal(SIGPIPE, SIG_DFL);
- fprintf(stdout, "%d\n", retval);
- fflush(stdout);
- /* Wait. */
- gettimeofday(&now, NULL);
- tv.tv_sec = CHECK_INTERVAL;
- /* round the sleep time to get woken up on a whole second */
- tv.tv_usec = 1000000 - now.tv_usec;
- if (now.tv_usec < 500000)
- tv.tv_sec--;
- FD_ZERO(&write_fds);
- FD_SET(STDOUT_FILENO, &write_fds);
- select(STDOUT_FILENO + 1,
- NULL, NULL, &write_fds,
- &tv);
- retval = 0;
- }
- } while (dflag > 0);
- return retval;
- }
- #endif
|