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