libc: make stdio_impl.h an internal libc header
[unleashed/tickless.git] / usr / src / lib / pam_modules / unix_session / unix_session.c
blobb580bdf2fbb1fa4a99fd56ba411e32df2baa1689
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 struct passwd *pwdp;
216 char buffer[NSS_BUFLEN_PASSWD];
217 int i;
218 int debug = 0;
219 time_t cur_time;
221 for (i = 0; i < argc; i++) {
222 if (strcasecmp(argv[i], "debug") == 0)
223 debug = 1;
224 else if (strcasecmp(argv[i], "nowarn") == 0)
225 flags = flags | PAM_SILENT;
226 else
227 syslog(LOG_ERR, "illegal option %s", argv[i]);
230 if (debug)
231 syslog(LOG_DEBUG,
232 "pam_unix_session: inside pam_sm_open_session()");
234 if ((error = pam_get_item(pamh, PAM_TTY, (void **)&ttyn))
235 != PAM_SUCCESS ||
236 (error = pam_get_item(pamh, PAM_USER, (void **)&user))
237 != PAM_SUCCESS ||
238 (error = pam_get_item(pamh, PAM_RHOST, (void **)&rhost))
239 != PAM_SUCCESS) {
240 return (error);
243 if (user == NULL || *user == '\0')
244 return (PAM_USER_UNKNOWN);
246 /* report error if ttyn not set */
247 if (ttyn == NULL)
248 return (PAM_SESSION_ERR);
250 getpwnam_r(user, &pwd, buffer, sizeof (buffer), &pwdp);
251 if (!pwdp)
252 return (PAM_USER_UNKNOWN);
254 ll.ll_time = 0;
255 reopenll_ro:
256 fdl = open(_PATH_LASTLOG, O_RDONLY);
257 if (fdl < 0) {
258 if (errno == EINTR)
259 goto reopenll_ro;
260 if (errno != ENOENT)
261 syslog(LOG_ERR, "pam_unix_session: unable to open "
262 "lastlog for uid %d: %m", pwd.pw_uid);
263 } else {
264 if (lastlog_read(fdl, pwd.pw_uid, &ll, B_FALSE) == 0)
265 llp = &ll;
266 (void) close(fdl);
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)
272 llp = &legacyll;
273 (void) close(fdl);
276 if (llp != NULL && llp->ll_time != 0 && !(flags & PAM_SILENT)) {
277 char timestr[26];
278 char msg[PAM_MAX_MSG_SIZE];
279 int ret;
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);
289 } else {
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,
295 NULL);
299 reopenll_rw:
300 fdl = open(_PATH_LASTLOG, O_RDWR|O_CREAT|O_DSYNC, 0444);
301 if (fdl < 0) {
302 if (errno == EINTR)
303 goto reopenll_rw;
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));
316 } else {
317 (void) strlcpy(newll.ll_line, ttyn,
318 sizeof (newll.ll_line));
320 if (rhost != NULL) {
321 (void) strlcpy(newll.ll_host, rhost,
322 sizeof (newll.ll_host));
325 if (debug) {
326 char buf[26];
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);