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