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
validate_basic(pam_handle_t * pamh,char * user_tty,char * timestampfile)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
validate_dir(const char * dir)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
create_dir(char * dir)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
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char ** argv)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
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char ** argv)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