6545 fix for 6540 did miss the pam_sm_close_session()
[unleashed.git] / usr / src / lib / pam_modules / unix_session / unix_session.c
blob0c10906e470ef0dc4181f2e35ce47eb2c30ff344
1 /*
2 * CDDL HEADER START
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
7 * with the License.
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]
20 * CDDL HEADER END
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>
29 #include <strings.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <stdlib.h>
35 #include <security/pam_appl.h>
36 #include <security/pam_modules.h>
37 #include <security/pam_impl.h>
38 #include <syslog.h>
39 #include <pwd.h>
40 #include <shadow.h>
41 #include <lastlog.h>
42 #include <ctype.h>
43 #include <unistd.h>
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <libintl.h>
47 #include <signal.h>
48 #include <thread.h>
49 #include <synch.h>
50 #include <errno.h>
51 #include <time.h>
52 #include <string.h>
53 #include <crypt.h>
54 #include <assert.h>
55 #include <nss_dbdefs.h>
57 #define LASTLOG_LEGACY "/var/adm/lastlog"
58 struct lastlog_legacy {
59 #ifdef _LP64
60 time32_t ll_time;
61 #else
62 time_t ll_time;
63 #endif
64 char ll_line[8];
65 char ll_host[16];
69 * pam_sm_close_session - Terminate a PAM authenticated session
71 /*ARGSUSED*/
72 int
73 pam_sm_close_session(pam_handle_t *pamh, int flags, int argc,
74 const char **argv)
76 int i;
77 int debug = 0;
79 for (i = 0; i < argc; i++) {
80 if (strcasecmp(argv[i], "debug") == 0)
81 debug = 1;
82 else if (strcasecmp(argv[i], "nowarn") != 0)
83 syslog(LOG_ERR, "illegal option %s", argv[i]);
86 if (debug)
87 syslog(LOG_DEBUG,
88 "pam_unix_session: inside pam_sm_close_session()");
90 return (PAM_SUCCESS);
93 static int
94 lastlog_seek(int fdl, uid_t uid, boolean_t legacy)
96 offset_t offset;
98 offset = uid;
99 if (legacy)
100 offset *= sizeof (struct lastlog_legacy);
101 else
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);
107 return (-1);
109 return (0);
112 static int
113 lastlog_read(int fdl, uid_t uid, struct lastlog *out, boolean_t legacy)
115 ssize_t nread = 0;
116 ssize_t llsize;
117 struct lastlog ll;
118 struct lastlog_legacy ll_legacy;
119 void *llp;
121 if (legacy) {
122 llp = &ll_legacy;
123 llsize = sizeof (ll_legacy);
124 } else {
125 llp = &ll;
126 llsize = sizeof (ll);
129 if (lastlog_seek(fdl, uid, legacy) == -1)
130 return (-1);
132 while (nread < llsize) {
133 ssize_t ret;
134 reread:
135 ret = read(fdl, ((char *)llp) + nread, llsize - nread);
136 if (ret < 0) {
137 if (errno == EINTR)
138 goto reread;
139 syslog(LOG_ERR, "pam_unix_session: read %slastlog "
140 "failed for uid %d: %m", (legacy ? "legacy " : ""),
141 uid);
142 return (-1);
143 } else if (ret == 0) {
144 if (nread == 0) {
145 out->ll_time = 0;
146 return (-1);
148 syslog(LOG_ERR, "pam_unix_session: %slastlog short "
149 "read for uid %d", (legacy ? "legacy " : ""), uid);
150 return (-1);
152 nread += ret;
154 if (legacy) {
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));
162 } else {
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));
171 return (0);
174 static int
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))
179 return (-1);
181 while (nwritten < sizeof (*ll)) {
182 ssize_t ret;
183 rewrite:
184 ret = write(fdl, ((char *)ll) + nwritten,
185 sizeof (*ll) - nwritten);
186 if (ret < 0) {
187 if (errno == EINTR)
188 goto rewrite;
189 syslog(LOG_ERR, "pam_unix_session: write lastlog "
190 "failed for uid %d: %m", uid);
191 return (-1);
192 } else if (ret == 0) {
193 syslog(LOG_ERR, "pam_unix_session: lastlog short "
194 "write for uid %d", uid);
195 return (-1);
197 nwritten += ret;
199 return (0);
202 /*ARGSUSED*/
204 pam_sm_open_session(pam_handle_t *pamh, int flags, int argc,
205 const char **argv)
207 int error;
208 char *ttyn, *rhost, *user;
209 int fdl;
210 struct lastlog newll = { 0 };
211 struct lastlog legacyll;
212 struct lastlog ll;
213 struct lastlog *llp = NULL;
214 struct passwd pwd;
215 char buffer[NSS_BUFLEN_PASSWD];
216 int i;
217 int debug = 0;
218 time_t cur_time;
220 for (i = 0; i < argc; i++) {
221 if (strcasecmp(argv[i], "debug") == 0)
222 debug = 1;
223 else if (strcasecmp(argv[i], "nowarn") == 0)
224 flags = flags | PAM_SILENT;
225 else
226 syslog(LOG_ERR, "illegal option %s", argv[i]);
229 if (debug)
230 syslog(LOG_DEBUG,
231 "pam_unix_session: inside pam_sm_open_session()");
233 if ((error = pam_get_item(pamh, PAM_TTY, (void **)&ttyn))
234 != PAM_SUCCESS ||
235 (error = pam_get_item(pamh, PAM_USER, (void **)&user))
236 != PAM_SUCCESS ||
237 (error = pam_get_item(pamh, PAM_RHOST, (void **)&rhost))
238 != PAM_SUCCESS) {
239 return (error);
242 if (user == NULL || *user == '\0')
243 return (PAM_USER_UNKNOWN);
245 /* report error if ttyn not set */
246 if (ttyn == NULL)
247 return (PAM_SESSION_ERR);
249 if (getpwnam_r(user, &pwd, buffer, sizeof (buffer)) == NULL) {
250 return (PAM_USER_UNKNOWN);
253 ll.ll_time = 0;
254 reopenll_ro:
255 fdl = open(_PATH_LASTLOG, O_RDONLY);
256 if (fdl < 0) {
257 if (errno == EINTR)
258 goto reopenll_ro;
259 if (errno != ENOENT)
260 syslog(LOG_ERR, "pam_unix_session: unable to open "
261 "lastlog for uid %d: %m", pwd.pw_uid);
262 } else {
263 if (lastlog_read(fdl, pwd.pw_uid, &ll, B_FALSE) == 0)
264 llp = &ll;
265 (void) close(fdl);
268 if ((fdl = open(LASTLOG_LEGACY, O_RDONLY)) >= 0) {
269 if (lastlog_read(fdl, pwd.pw_uid, &legacyll, B_TRUE) == 0 &&
270 legacyll.ll_time > ll.ll_time)
271 llp = &legacyll;
272 (void) close(fdl);
275 if (llp != NULL && llp->ll_time != 0 && !(flags & PAM_SILENT)) {
276 char timestr[26];
277 char msg[PAM_MAX_MSG_SIZE];
278 int ret;
279 time_t t = llp->ll_time;
280 (void) ctime_r(&t, timestr, sizeof (timestr));
281 timestr[strcspn(timestr, "\n")] = '\0';
282 if (strcmp(llp->ll_host, "") != 0) {
283 ret = snprintf(msg, PAM_MAX_MSG_SIZE,
284 "Last login: %s from %s", timestr, llp->ll_host);
285 } else if (strcmp(llp->ll_line, "") != 0) {
286 ret = snprintf(msg, PAM_MAX_MSG_SIZE,
287 "Last login: %s on %s", timestr, llp->ll_line);
288 } else {
289 ret = snprintf(msg, PAM_MAX_MSG_SIZE,
290 "Last login: %s", timestr);
292 if (!(ret < 0 || ret >= PAM_MAX_MSG_SIZE)) {
293 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, &msg,
294 NULL);
298 reopenll_rw:
299 fdl = open(_PATH_LASTLOG, O_RDWR|O_CREAT|O_DSYNC, 0444);
300 if (fdl < 0) {
301 if (errno == EINTR)
302 goto reopenll_rw;
303 syslog(LOG_ERR, "pam_unix_session: unable to open lastlog for "
304 "writing for uid %d: %m", pwd.pw_uid);
305 return (PAM_SUCCESS);
308 (void) time(&cur_time);
310 newll.ll_time = cur_time;
311 if ((strncmp(ttyn, "/dev/", 5) == 0)) {
312 (void) strlcpy(newll.ll_line,
313 (ttyn + sizeof ("/dev/")-1),
314 sizeof (newll.ll_line));
315 } else {
316 (void) strlcpy(newll.ll_line, ttyn,
317 sizeof (newll.ll_line));
319 if (rhost != NULL) {
320 (void) strlcpy(newll.ll_host, rhost,
321 sizeof (newll.ll_host));
324 if (debug) {
325 char buf[26];
327 (void) ctime_r((const time_t *)&cur_time, buf,
328 sizeof (buf));
329 buf[24] = '\000';
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);