4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2015 Lauri Tirkkonen.
26 * Copyright 2016 Toomas Soome <tsoome@me.com>
30 #include <sys/types.h>
35 #include <security/pam_appl.h>
36 #include <security/pam_modules.h>
37 #include <security/pam_impl.h>
55 #include <nss_dbdefs.h>
57 #define LASTLOG_LEGACY "/var/adm/lastlog"
58 struct lastlog_legacy
{
69 * pam_sm_close_session - Terminate a PAM authenticated session
73 pam_sm_close_session(pam_handle_t
*pamh
, int flags
, int argc
,
79 for (i
= 0; i
< argc
; i
++) {
80 if (strcasecmp(argv
[i
], "debug") == 0)
82 else if (strcasecmp(argv
[i
], "nowarn") != 0)
83 syslog(LOG_ERR
, "illegal option %s", argv
[i
]);
88 "pam_unix_session: inside pam_sm_close_session()");
94 lastlog_seek(int fdl
, uid_t uid
, boolean_t legacy
)
100 offset
*= sizeof (struct lastlog_legacy
);
102 offset
*= sizeof (struct lastlog
);
104 if (llseek(fdl
, offset
, SEEK_SET
) != offset
) {
105 syslog(LOG_ERR
, "pam_unix_session: %slastlog seek failed for "
106 "uid %d: %m", (legacy
? "legacy " : ""), uid
);
113 lastlog_read(int fdl
, uid_t uid
, struct lastlog
*out
, boolean_t legacy
)
118 struct lastlog_legacy ll_legacy
;
123 llsize
= sizeof (ll_legacy
);
126 llsize
= sizeof (ll
);
129 if (lastlog_seek(fdl
, uid
, legacy
) == -1)
132 while (nread
< llsize
) {
135 ret
= read(fdl
, ((char *)llp
) + nread
, llsize
- nread
);
139 syslog(LOG_ERR
, "pam_unix_session: read %slastlog "
140 "failed for uid %d: %m", (legacy
? "legacy " : ""),
143 } else if (ret
== 0) {
148 syslog(LOG_ERR
, "pam_unix_session: %slastlog short "
149 "read for uid %d", (legacy
? "legacy " : ""), uid
);
155 out
->ll_time
= ll_legacy
.ll_time
;
156 ll_legacy
.ll_line
[sizeof (ll_legacy
.ll_line
) - 1] = '\0';
157 ll_legacy
.ll_host
[sizeof (ll_legacy
.ll_host
) - 1] = '\0';
158 (void) strlcpy(out
->ll_line
, ll_legacy
.ll_line
,
159 sizeof (out
->ll_line
));
160 (void) strlcpy(out
->ll_host
, ll_legacy
.ll_host
,
161 sizeof (out
->ll_line
));
163 out
->ll_time
= ll
.ll_time
;
164 ll
.ll_line
[sizeof (ll
.ll_line
) - 1] = '\0';
165 ll
.ll_host
[sizeof (ll
.ll_host
) - 1] = '\0';
166 (void) strlcpy(out
->ll_line
, ll
.ll_line
,
167 sizeof (out
->ll_line
));
168 (void) strlcpy(out
->ll_host
, ll
.ll_host
,
169 sizeof (out
->ll_host
));
175 lastlog_write(int fdl
, uid_t uid
, const struct lastlog
*ll
)
177 ssize_t nwritten
= 0;
178 if (lastlog_seek(fdl
, uid
, B_FALSE
))
181 while (nwritten
< sizeof (*ll
)) {
184 ret
= write(fdl
, ((char *)ll
) + nwritten
,
185 sizeof (*ll
) - nwritten
);
189 syslog(LOG_ERR
, "pam_unix_session: write lastlog "
190 "failed for uid %d: %m", uid
);
192 } else if (ret
== 0) {
193 syslog(LOG_ERR
, "pam_unix_session: lastlog short "
194 "write for uid %d", uid
);
204 pam_sm_open_session(pam_handle_t
*pamh
, int flags
, int argc
,
208 char *ttyn
, *rhost
, *user
;
210 struct lastlog newll
= { 0 };
211 struct lastlog legacyll
;
213 struct lastlog
*llp
= NULL
;
216 char buffer
[NSS_BUFLEN_PASSWD
];
221 for (i
= 0; i
< argc
; i
++) {
222 if (strcasecmp(argv
[i
], "debug") == 0)
224 else if (strcasecmp(argv
[i
], "nowarn") == 0)
225 flags
= flags
| PAM_SILENT
;
227 syslog(LOG_ERR
, "illegal option %s", argv
[i
]);
232 "pam_unix_session: inside pam_sm_open_session()");
234 if ((error
= pam_get_item(pamh
, PAM_TTY
, (void **)&ttyn
))
236 (error
= pam_get_item(pamh
, PAM_USER
, (void **)&user
))
238 (error
= pam_get_item(pamh
, PAM_RHOST
, (void **)&rhost
))
243 if (user
== NULL
|| *user
== '\0')
244 return (PAM_USER_UNKNOWN
);
246 /* report error if ttyn not set */
248 return (PAM_SESSION_ERR
);
250 getpwnam_r(user
, &pwd
, buffer
, sizeof (buffer
), &pwdp
);
252 return (PAM_USER_UNKNOWN
);
256 fdl
= open(_PATH_LASTLOG
, O_RDONLY
);
261 syslog(LOG_ERR
, "pam_unix_session: unable to open "
262 "lastlog for uid %d: %m", pwd
.pw_uid
);
264 if (lastlog_read(fdl
, pwd
.pw_uid
, &ll
, B_FALSE
) == 0)
269 if ((fdl
= open(LASTLOG_LEGACY
, O_RDONLY
)) >= 0) {
270 if (lastlog_read(fdl
, pwd
.pw_uid
, &legacyll
, B_TRUE
) == 0 &&
271 legacyll
.ll_time
> ll
.ll_time
)
276 if (llp
!= NULL
&& llp
->ll_time
!= 0 && !(flags
& PAM_SILENT
)) {
278 char msg
[PAM_MAX_MSG_SIZE
];
280 time_t t
= llp
->ll_time
;
281 (void) ctime_r(&t
, timestr
);
282 timestr
[strcspn(timestr
, "\n")] = '\0';
283 if (strcmp(llp
->ll_host
, "") != 0) {
284 ret
= snprintf(msg
, PAM_MAX_MSG_SIZE
,
285 "Last login: %s from %s", timestr
, llp
->ll_host
);
286 } else if (strcmp(llp
->ll_line
, "") != 0) {
287 ret
= snprintf(msg
, PAM_MAX_MSG_SIZE
,
288 "Last login: %s on %s", timestr
, llp
->ll_line
);
290 ret
= snprintf(msg
, PAM_MAX_MSG_SIZE
,
291 "Last login: %s", timestr
);
293 if (!(ret
< 0 || ret
>= PAM_MAX_MSG_SIZE
)) {
294 (void) __pam_display_msg(pamh
, PAM_TEXT_INFO
, 1, &msg
,
300 fdl
= open(_PATH_LASTLOG
, O_RDWR
|O_CREAT
|O_DSYNC
, 0444);
304 syslog(LOG_ERR
, "pam_unix_session: unable to open lastlog for "
305 "writing for uid %d: %m", pwd
.pw_uid
);
306 return (PAM_SUCCESS
);
309 (void) time(&cur_time
);
311 newll
.ll_time
= cur_time
;
312 if ((strncmp(ttyn
, "/dev/", 5) == 0)) {
313 (void) strlcpy(newll
.ll_line
,
314 (ttyn
+ sizeof ("/dev/")-1),
315 sizeof (newll
.ll_line
));
317 (void) strlcpy(newll
.ll_line
, ttyn
,
318 sizeof (newll
.ll_line
));
321 (void) strlcpy(newll
.ll_host
, rhost
,
322 sizeof (newll
.ll_host
));
328 (void) ctime_r((const time_t *)&cur_time
, buf
);
329 buf
[strcspn(buf
, "\n")] = '\0';
330 syslog(LOG_DEBUG
, "pam_unix_session: "
331 "user = %s, time = %s, tty = %s, host = %s.",
332 user
, buf
, newll
.ll_line
, newll
.ll_host
);
334 (void) lastlog_write(fdl
, pwd
.pw_uid
, &newll
);
335 if (close(fdl
) < 0) {
336 syslog(LOG_ERR
, "pam_unix_session: unable to close lastlog for"
337 " uid %d: %m", pwd
.pw_uid
);
339 return (PAM_SUCCESS
);