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