1*45405cceSAlexander Eremin /* 2*45405cceSAlexander Eremin * This file and its contents are supplied under the terms of the 3*45405cceSAlexander Eremin * Common Development and Distribution License ("CDDL"), version 1.0. 4*45405cceSAlexander Eremin * You may only use this file in accordance with the terms of version 5*45405cceSAlexander Eremin * 1.0 of the CDDL. 6*45405cceSAlexander Eremin * 7*45405cceSAlexander Eremin * A full copy of the text of the CDDL should have accompanied this 8*45405cceSAlexander Eremin * source. A copy of the CDDL is also available via the Internet at 9*45405cceSAlexander Eremin * http://www.illumos.org/license/CDDL. 10*45405cceSAlexander Eremin */ 11*45405cceSAlexander Eremin /* 12*45405cceSAlexander Eremin * Copyright 2014 Nexenta Systems, Inc. 13*45405cceSAlexander Eremin */ 14*45405cceSAlexander Eremin 15*45405cceSAlexander Eremin #include <stdio.h> 16*45405cceSAlexander Eremin #include <stdlib.h> 17*45405cceSAlexander Eremin #include <strings.h> 18*45405cceSAlexander Eremin #include <fcntl.h> 19*45405cceSAlexander Eremin #include <security/pam_appl.h> 20*45405cceSAlexander Eremin #include <security/pam_modules.h> 21*45405cceSAlexander Eremin #include <security/pam_impl.h> 22*45405cceSAlexander Eremin #include <sys/param.h> 23*45405cceSAlexander Eremin #include <sys/stat.h> 24*45405cceSAlexander Eremin #include <sys/types.h> 25*45405cceSAlexander Eremin #include <syslog.h> 26*45405cceSAlexander Eremin #include <unistd.h> 27*45405cceSAlexander Eremin #include <libgen.h> 28*45405cceSAlexander Eremin #include <errno.h> 29*45405cceSAlexander Eremin 30*45405cceSAlexander Eremin #define TIMESTAMP_DIR "/var/run/tty_timestamps" 31*45405cceSAlexander Eremin #define TIMESTAMP_TIMEOUT 5 /* default timeout */ 32*45405cceSAlexander Eremin #define ROOT_UID 0 /* root uid */ 33*45405cceSAlexander Eremin #define ROOT_GID 0 /* root gid */ 34*45405cceSAlexander Eremin 35*45405cceSAlexander Eremin struct user_info { 36*45405cceSAlexander Eremin dev_t dev; /* ID of device tty resides on */ 37*45405cceSAlexander Eremin dev_t rdev; /* tty device ID */ 38*45405cceSAlexander Eremin ino_t ino; /* tty inode number */ 39*45405cceSAlexander Eremin uid_t uid; /* user's uid */ 40*45405cceSAlexander Eremin pid_t ppid; /* parent pid */ 41*45405cceSAlexander Eremin pid_t sid; /* session ID associated with tty/ppid */ 42*45405cceSAlexander Eremin timestruc_t ts; /* time of tty last status change */ 43*45405cceSAlexander Eremin }; 44*45405cceSAlexander Eremin 45*45405cceSAlexander Eremin int debug = 0; 46*45405cceSAlexander Eremin 47*45405cceSAlexander Eremin int 48*45405cceSAlexander Eremin validate_basic( 49*45405cceSAlexander Eremin pam_handle_t *pamh, 50*45405cceSAlexander Eremin char *user_tty, 51*45405cceSAlexander Eremin char *timestampfile) 52*45405cceSAlexander Eremin { 53*45405cceSAlexander Eremin char *user; 54*45405cceSAlexander Eremin char *auser; 55*45405cceSAlexander Eremin char *ttyn; 56*45405cceSAlexander Eremin 57*45405cceSAlexander Eremin /* get user, auser and users's tty */ 58*45405cceSAlexander Eremin (void) pam_get_item(pamh, PAM_USER, (void **)&user); 59*45405cceSAlexander Eremin (void) pam_get_item(pamh, PAM_AUSER, (void **)&auser); 60*45405cceSAlexander Eremin (void) pam_get_item(pamh, PAM_TTY, (void **)&ttyn); 61*45405cceSAlexander Eremin 62*45405cceSAlexander Eremin if (user == NULL || *user == '\0') { 63*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 64*45405cceSAlexander Eremin "PAM_USER NULL or empty"); 65*45405cceSAlexander Eremin return (PAM_IGNORE); 66*45405cceSAlexander Eremin } 67*45405cceSAlexander Eremin 68*45405cceSAlexander Eremin if (auser == NULL || *auser == '\0') { 69*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 70*45405cceSAlexander Eremin "PAM_AUSER NULL or empty"); 71*45405cceSAlexander Eremin return (PAM_IGNORE); 72*45405cceSAlexander Eremin } 73*45405cceSAlexander Eremin 74*45405cceSAlexander Eremin if (ttyn == NULL || *ttyn == '\0') { 75*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 76*45405cceSAlexander Eremin "PAM_TTY NULL or empty"); 77*45405cceSAlexander Eremin return (PAM_IGNORE); 78*45405cceSAlexander Eremin } 79*45405cceSAlexander Eremin 80*45405cceSAlexander Eremin if (debug) 81*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_DEBUG, "pam_timestamp: " 82*45405cceSAlexander Eremin "user = %s, auser = %s, tty = %s", user, auser, ttyn); 83*45405cceSAlexander Eremin 84*45405cceSAlexander Eremin (void) strlcpy(user_tty, ttyn, MAXPATHLEN); 85*45405cceSAlexander Eremin 86*45405cceSAlexander Eremin if (strchr(ttyn, '/') == NULL || strncmp(ttyn, "/dev/", 5) == 0) { 87*45405cceSAlexander Eremin ttyn = strrchr(ttyn, '/') + 1; 88*45405cceSAlexander Eremin } else { 89*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 90*45405cceSAlexander Eremin "invalid tty: %s", ttyn); 91*45405cceSAlexander Eremin return (PAM_IGNORE); 92*45405cceSAlexander Eremin } 93*45405cceSAlexander Eremin 94*45405cceSAlexander Eremin /* format timestamp file name */ 95*45405cceSAlexander Eremin (void) snprintf(timestampfile, MAXPATHLEN, "%s/%s/%s:%s", TIMESTAMP_DIR, 96*45405cceSAlexander Eremin auser, ttyn, user); 97*45405cceSAlexander Eremin 98*45405cceSAlexander Eremin return (PAM_SUCCESS); 99*45405cceSAlexander Eremin } 100*45405cceSAlexander Eremin 101*45405cceSAlexander Eremin int 102*45405cceSAlexander Eremin validate_dir(const char *dir) 103*45405cceSAlexander Eremin { 104*45405cceSAlexander Eremin struct stat sb; 105*45405cceSAlexander Eremin 106*45405cceSAlexander Eremin /* 107*45405cceSAlexander Eremin * check that the directory exist and has 108*45405cceSAlexander Eremin * right owner and permissions. 109*45405cceSAlexander Eremin */ 110*45405cceSAlexander Eremin if (lstat(dir, &sb) < 0) { 111*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 112*45405cceSAlexander Eremin "directory %s does not exist", dir); 113*45405cceSAlexander Eremin return (PAM_IGNORE); 114*45405cceSAlexander Eremin } 115*45405cceSAlexander Eremin 116*45405cceSAlexander Eremin if (!S_ISDIR(sb.st_mode)) { 117*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 118*45405cceSAlexander Eremin "%s is not a directory", dir); 119*45405cceSAlexander Eremin return (PAM_IGNORE); 120*45405cceSAlexander Eremin } 121*45405cceSAlexander Eremin 122*45405cceSAlexander Eremin if (S_ISLNK(sb.st_mode)) { 123*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 124*45405cceSAlexander Eremin "%s is a symbolic link", dir); 125*45405cceSAlexander Eremin return (PAM_IGNORE); 126*45405cceSAlexander Eremin } 127*45405cceSAlexander Eremin 128*45405cceSAlexander Eremin if (sb.st_uid != 0 || sb.st_gid != 0) { 129*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 130*45405cceSAlexander Eremin "%s is not owned by root", dir); 131*45405cceSAlexander Eremin return (PAM_IGNORE); 132*45405cceSAlexander Eremin } 133*45405cceSAlexander Eremin 134*45405cceSAlexander Eremin if (sb.st_mode & (S_IWGRP | S_IWOTH | S_IROTH)) { 135*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 136*45405cceSAlexander Eremin "%s has wrong permissions", dir); 137*45405cceSAlexander Eremin return (PAM_IGNORE); 138*45405cceSAlexander Eremin } 139*45405cceSAlexander Eremin 140*45405cceSAlexander Eremin return (PAM_SUCCESS); 141*45405cceSAlexander Eremin } 142*45405cceSAlexander Eremin 143*45405cceSAlexander Eremin int 144*45405cceSAlexander Eremin create_dir(char *dir) 145*45405cceSAlexander Eremin { 146*45405cceSAlexander Eremin /* 147*45405cceSAlexander Eremin * create directory if it doesn't exist and attempt to set 148*45405cceSAlexander Eremin * the owner to root. 149*45405cceSAlexander Eremin */ 150*45405cceSAlexander Eremin if (mkdir(dir, S_IRWXU) < 0) { 151*45405cceSAlexander Eremin if (errno != EEXIST) { 152*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 153*45405cceSAlexander Eremin "can't create directory %s", dir); 154*45405cceSAlexander Eremin return (PAM_IGNORE); 155*45405cceSAlexander Eremin } 156*45405cceSAlexander Eremin } else if (lchown(dir, ROOT_UID, ROOT_GID) < 0) { 157*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 158*45405cceSAlexander Eremin "can't set permissions on directory %s", dir); 159*45405cceSAlexander Eremin return (PAM_IGNORE); 160*45405cceSAlexander Eremin } 161*45405cceSAlexander Eremin return (PAM_SUCCESS); 162*45405cceSAlexander Eremin } 163*45405cceSAlexander Eremin 164*45405cceSAlexander Eremin /* 165*45405cceSAlexander Eremin * pam_sm_authenticate 166*45405cceSAlexander Eremin * 167*45405cceSAlexander Eremin * Read authentication from user, using cached successful authentication 168*45405cceSAlexander Eremin * attempts. 169*45405cceSAlexander Eremin * 170*45405cceSAlexander Eremin * returns PAM_SUCCESS on success, otherwise always returns PAM_IGNORE: 171*45405cceSAlexander Eremin * while this module has "sufficient" control value, in case of any failure 172*45405cceSAlexander Eremin * user will be authenticated with the pam_unix_auth module. 173*45405cceSAlexander Eremin * options - 174*45405cceSAlexander Eremin * debug 175*45405cceSAlexander Eremin * timeout= timeout in min, default is 5 176*45405cceSAlexander Eremin */ 177*45405cceSAlexander Eremin /*ARGSUSED*/ 178*45405cceSAlexander Eremin int 179*45405cceSAlexander Eremin pam_sm_authenticate( 180*45405cceSAlexander Eremin pam_handle_t *pamh, 181*45405cceSAlexander Eremin int flags, 182*45405cceSAlexander Eremin int argc, 183*45405cceSAlexander Eremin const char **argv) 184*45405cceSAlexander Eremin { 185*45405cceSAlexander Eremin struct user_info info; 186*45405cceSAlexander Eremin struct stat sb, tty; 187*45405cceSAlexander Eremin time_t timeout = 0; 188*45405cceSAlexander Eremin long tmp = 0; 189*45405cceSAlexander Eremin int result = PAM_IGNORE; 190*45405cceSAlexander Eremin int i; 191*45405cceSAlexander Eremin int fd = -1; 192*45405cceSAlexander Eremin char *p; 193*45405cceSAlexander Eremin char user_tty[MAXPATHLEN]; 194*45405cceSAlexander Eremin char timestampdir[MAXPATHLEN]; 195*45405cceSAlexander Eremin char timestampfile[MAXPATHLEN]; 196*45405cceSAlexander Eremin char *sudir; 197*45405cceSAlexander Eremin 198*45405cceSAlexander Eremin timeout = TIMESTAMP_TIMEOUT; 199*45405cceSAlexander Eremin 200*45405cceSAlexander Eremin /* check options passed to this module */ 201*45405cceSAlexander Eremin for (i = 0; i < argc; i++) { 202*45405cceSAlexander Eremin if (strcmp(argv[i], "debug") == 0) { 203*45405cceSAlexander Eremin debug = 1; 204*45405cceSAlexander Eremin } else if (strncmp(argv[i], "timeout=", 8) == 0) { 205*45405cceSAlexander Eremin tmp = strtol(argv[i] + 8, &p, 0); 206*45405cceSAlexander Eremin if ((p != NULL) && (*p == '\0') && tmp > 0) { 207*45405cceSAlexander Eremin timeout = tmp; 208*45405cceSAlexander Eremin } 209*45405cceSAlexander Eremin } 210*45405cceSAlexander Eremin } 211*45405cceSAlexander Eremin 212*45405cceSAlexander Eremin if (validate_basic(pamh, user_tty, timestampfile) != PAM_SUCCESS) 213*45405cceSAlexander Eremin return (result); 214*45405cceSAlexander Eremin 215*45405cceSAlexander Eremin sudir = TIMESTAMP_DIR; 216*45405cceSAlexander Eremin if (validate_dir(sudir) != PAM_SUCCESS) 217*45405cceSAlexander Eremin return (result); 218*45405cceSAlexander Eremin 219*45405cceSAlexander Eremin (void) strlcpy(timestampdir, timestampfile, MAXPATHLEN); 220*45405cceSAlexander Eremin 221*45405cceSAlexander Eremin if (validate_dir(dirname(timestampdir)) != PAM_SUCCESS) 222*45405cceSAlexander Eremin return (result); 223*45405cceSAlexander Eremin 224*45405cceSAlexander Eremin /* 225*45405cceSAlexander Eremin * check that timestamp file is exist and has right owner 226*45405cceSAlexander Eremin * and permissions. 227*45405cceSAlexander Eremin */ 228*45405cceSAlexander Eremin if (lstat(timestampfile, &sb) == 0 && sb.st_size != 0) { 229*45405cceSAlexander Eremin if (!S_ISREG(sb.st_mode)) { 230*45405cceSAlexander Eremin (void) unlink(timestampfile); 231*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 232*45405cceSAlexander Eremin "timestamp file %s is not a regular file", 233*45405cceSAlexander Eremin timestampfile); 234*45405cceSAlexander Eremin return (result); 235*45405cceSAlexander Eremin } 236*45405cceSAlexander Eremin 237*45405cceSAlexander Eremin if (sb.st_uid != 0 || sb.st_gid != 0) { 238*45405cceSAlexander Eremin (void) unlink(timestampfile); 239*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 240*45405cceSAlexander Eremin "timestamp file %s is not owned by root", 241*45405cceSAlexander Eremin timestampfile); 242*45405cceSAlexander Eremin return (result); 243*45405cceSAlexander Eremin } 244*45405cceSAlexander Eremin 245*45405cceSAlexander Eremin if (sb.st_nlink != 1 || S_ISLNK(sb.st_mode)) { 246*45405cceSAlexander Eremin (void) unlink(timestampfile); 247*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 248*45405cceSAlexander Eremin "timestamp file %s is a symbolic link", 249*45405cceSAlexander Eremin timestampfile); 250*45405cceSAlexander Eremin return (result); 251*45405cceSAlexander Eremin } 252*45405cceSAlexander Eremin 253*45405cceSAlexander Eremin if (sb.st_mode & (S_IRWXG | S_IRWXO)) { 254*45405cceSAlexander Eremin (void) unlink(timestampfile); 255*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 256*45405cceSAlexander Eremin "timestamp file %s has wrong permissions", 257*45405cceSAlexander Eremin timestampfile); 258*45405cceSAlexander Eremin return (result); 259*45405cceSAlexander Eremin } 260*45405cceSAlexander Eremin } else { 261*45405cceSAlexander Eremin if (debug) 262*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_DEBUG, "pam_timestamp: " 263*45405cceSAlexander Eremin "timestamp file %s does not exist: %m", 264*45405cceSAlexander Eremin timestampfile); 265*45405cceSAlexander Eremin return (result); 266*45405cceSAlexander Eremin } 267*45405cceSAlexander Eremin 268*45405cceSAlexander Eremin 269*45405cceSAlexander Eremin if (stat(user_tty, &tty) < 0) { 270*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 271*45405cceSAlexander Eremin "can't stat tty: %m"); 272*45405cceSAlexander Eremin return (result); 273*45405cceSAlexander Eremin } 274*45405cceSAlexander Eremin 275*45405cceSAlexander Eremin if ((fd = open(timestampfile, O_RDONLY)) < 0) { 276*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 277*45405cceSAlexander Eremin "can't open timestamp file %s for reading: %m", 278*45405cceSAlexander Eremin timestampfile); 279*45405cceSAlexander Eremin return (result); 280*45405cceSAlexander Eremin } 281*45405cceSAlexander Eremin 282*45405cceSAlexander Eremin if (read(fd, &info, sizeof (info)) != sizeof (info)) { 283*45405cceSAlexander Eremin (void) close(fd); 284*45405cceSAlexander Eremin (void) unlink(timestampfile); 285*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 286*45405cceSAlexander Eremin "timestamp file '%s' is corrupt: %m", timestampfile); 287*45405cceSAlexander Eremin return (result); 288*45405cceSAlexander Eremin } 289*45405cceSAlexander Eremin 290*45405cceSAlexander Eremin if (info.dev != tty.st_dev || info.ino != tty.st_ino || 291*45405cceSAlexander Eremin info.rdev != tty.st_rdev || info.sid != getsid(getpid()) || 292*45405cceSAlexander Eremin info.uid != getuid() || info.ts.tv_sec != tty.st_ctim.tv_sec || 293*45405cceSAlexander Eremin info.ts.tv_nsec != tty.st_ctim.tv_nsec) { 294*45405cceSAlexander Eremin (void) close(fd); 295*45405cceSAlexander Eremin (void) unlink(timestampfile); 296*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 297*45405cceSAlexander Eremin "the content of the timestamp file '%s' is not valid", 298*45405cceSAlexander Eremin timestampfile); 299*45405cceSAlexander Eremin return (result); 300*45405cceSAlexander Eremin } 301*45405cceSAlexander Eremin 302*45405cceSAlexander Eremin if (time((time_t *)0) - sb.st_mtime > 60 * timeout) { 303*45405cceSAlexander Eremin (void) unlink(timestampfile); 304*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 305*45405cceSAlexander Eremin "timestamp file '%s' has expired, disallowing access", 306*45405cceSAlexander Eremin timestampfile); 307*45405cceSAlexander Eremin return (result); 308*45405cceSAlexander Eremin } else { 309*45405cceSAlexander Eremin if (debug) 310*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_DEBUG, "pam_timestamp: " 311*45405cceSAlexander Eremin "timestamp file %s is not expired, " 312*45405cceSAlexander Eremin "allowing access ", timestampfile); 313*45405cceSAlexander Eremin result = PAM_SUCCESS; 314*45405cceSAlexander Eremin } 315*45405cceSAlexander Eremin 316*45405cceSAlexander Eremin return (result); 317*45405cceSAlexander Eremin } 318*45405cceSAlexander Eremin 319*45405cceSAlexander Eremin /* 320*45405cceSAlexander Eremin * pam_sm_setcred 321*45405cceSAlexander Eremin * 322*45405cceSAlexander Eremin * Creates timestamp directory and writes 323*45405cceSAlexander Eremin * timestamp file if it doesn't exist. 324*45405cceSAlexander Eremin * 325*45405cceSAlexander Eremin * returns PAM_SUCCESS on success, otherwise PAM_IGNORE 326*45405cceSAlexander Eremin */ 327*45405cceSAlexander Eremin /*ARGSUSED*/ 328*45405cceSAlexander Eremin int 329*45405cceSAlexander Eremin pam_sm_setcred( 330*45405cceSAlexander Eremin pam_handle_t *pamh, 331*45405cceSAlexander Eremin int flags, 332*45405cceSAlexander Eremin int argc, 333*45405cceSAlexander Eremin const char **argv) 334*45405cceSAlexander Eremin { 335*45405cceSAlexander Eremin struct stat sb; 336*45405cceSAlexander Eremin struct stat tty; 337*45405cceSAlexander Eremin struct user_info info; 338*45405cceSAlexander Eremin int result = PAM_IGNORE; 339*45405cceSAlexander Eremin int fd = -1; 340*45405cceSAlexander Eremin char user_tty[MAXPATHLEN]; 341*45405cceSAlexander Eremin char timestampdir[MAXPATHLEN]; 342*45405cceSAlexander Eremin char timestampfile[MAXPATHLEN]; 343*45405cceSAlexander Eremin 344*45405cceSAlexander Eremin /* validate flags */ 345*45405cceSAlexander Eremin if (flags && !(flags & PAM_ESTABLISH_CRED) && 346*45405cceSAlexander Eremin !(flags & PAM_REINITIALIZE_CRED) && 347*45405cceSAlexander Eremin !(flags & PAM_REFRESH_CRED) && 348*45405cceSAlexander Eremin !(flags & PAM_DELETE_CRED) && 349*45405cceSAlexander Eremin !(flags & PAM_SILENT)) { 350*45405cceSAlexander Eremin syslog(LOG_ERR, "pam_timestamp: illegal flag %d", flags); 351*45405cceSAlexander Eremin return (result); 352*45405cceSAlexander Eremin } 353*45405cceSAlexander Eremin 354*45405cceSAlexander Eremin if (validate_basic(pamh, user_tty, timestampfile) != PAM_SUCCESS) 355*45405cceSAlexander Eremin return (result); 356*45405cceSAlexander Eremin 357*45405cceSAlexander Eremin /* 358*45405cceSAlexander Eremin * user doesn't need to authenticate for PAM_DELETE_CRED 359*45405cceSAlexander Eremin */ 360*45405cceSAlexander Eremin if (flags & PAM_DELETE_CRED) { 361*45405cceSAlexander Eremin (void) unlink(timestampfile); 362*45405cceSAlexander Eremin return (result); 363*45405cceSAlexander Eremin } 364*45405cceSAlexander Eremin 365*45405cceSAlexander Eremin /* if the timestamp file exist, there is nothing to do */ 366*45405cceSAlexander Eremin if (lstat(timestampfile, &sb) == 0) { 367*45405cceSAlexander Eremin if (debug) 368*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_DEBUG, "pam_timestamp: " 369*45405cceSAlexander Eremin "timestamp file %s is not expired", timestampfile); 370*45405cceSAlexander Eremin return (result); 371*45405cceSAlexander Eremin } 372*45405cceSAlexander Eremin 373*45405cceSAlexander Eremin if (create_dir(TIMESTAMP_DIR) != PAM_SUCCESS) 374*45405cceSAlexander Eremin return (result); 375*45405cceSAlexander Eremin 376*45405cceSAlexander Eremin (void) strlcpy(timestampdir, timestampfile, MAXPATHLEN); 377*45405cceSAlexander Eremin 378*45405cceSAlexander Eremin if (create_dir(dirname(timestampdir)) != PAM_SUCCESS) 379*45405cceSAlexander Eremin return (result); 380*45405cceSAlexander Eremin 381*45405cceSAlexander Eremin if (stat(user_tty, &tty) < 0) { 382*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 383*45405cceSAlexander Eremin "can't stat tty: %m"); 384*45405cceSAlexander Eremin return (result); 385*45405cceSAlexander Eremin } 386*45405cceSAlexander Eremin 387*45405cceSAlexander Eremin info.dev = tty.st_dev; 388*45405cceSAlexander Eremin info.ino = tty.st_ino; 389*45405cceSAlexander Eremin info.rdev = tty.st_rdev; 390*45405cceSAlexander Eremin info.sid = getsid(getpid()); 391*45405cceSAlexander Eremin info.uid = getuid(); 392*45405cceSAlexander Eremin info.ts = tty.st_ctim; 393*45405cceSAlexander Eremin 394*45405cceSAlexander Eremin if ((fd = open(timestampfile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR)) < 0) { 395*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 396*45405cceSAlexander Eremin "can't open timestamp file %s for writing: %m", 397*45405cceSAlexander Eremin timestampfile); 398*45405cceSAlexander Eremin return (result); 399*45405cceSAlexander Eremin } else if (fchown(fd, ROOT_UID, ROOT_GID) != 0) { 400*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 401*45405cceSAlexander Eremin "can't set permissions on timestamp file %s: %m", 402*45405cceSAlexander Eremin timestampfile); 403*45405cceSAlexander Eremin (void) close(fd); 404*45405cceSAlexander Eremin return (result); 405*45405cceSAlexander Eremin } 406*45405cceSAlexander Eremin 407*45405cceSAlexander Eremin if (write(fd, &info, sizeof (info)) != sizeof (info)) { 408*45405cceSAlexander Eremin (void) close(fd); 409*45405cceSAlexander Eremin syslog(LOG_AUTH | LOG_ERR, "pam_timestamp: " 410*45405cceSAlexander Eremin "can't write timestamp file %s: %m", timestampfile); 411*45405cceSAlexander Eremin return (result); 412*45405cceSAlexander Eremin } 413*45405cceSAlexander Eremin (void) close(fd); 414*45405cceSAlexander Eremin 415*45405cceSAlexander Eremin return (PAM_SUCCESS); 416*45405cceSAlexander Eremin } 417