0
|
1 /*
|
|
2 * CDDL HEADER START
|
|
3 *
|
|
4 * The contents of this file are subject to the terms of the
|
|
5 * Common Development and Distribution License (the "License").
|
|
6 * You may not use this file except in compliance with the License.
|
|
7 *
|
|
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
|
9 * or http://www.opensolaris.org/os/licensing.
|
|
10 * See the License for the specific language governing permissions
|
|
11 * and limitations under the License.
|
|
12 *
|
|
13 * When distributing Covered Code, include this CDDL HEADER in each
|
|
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
|
15 * If applicable, add the following below this CDDL HEADER, with the
|
|
16 * fields enclosed by brackets "[]" replaced with your own identifying
|
|
17 * information: Portions Copyright [yyyy] [name of copyright owner]
|
|
18 *
|
|
19 * CDDL HEADER END
|
|
20 */
|
|
21 /*
|
|
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
|
23 * Use is subject to license terms.
|
|
24 */
|
|
25
|
|
26 #pragma ident "@(#)login_audit.c 1.4 06/11/02 SMI"
|
|
27
|
|
28 #include <assert.h>
|
|
29 #include <priv.h>
|
|
30 #include <pwd.h>
|
|
31 #include <signal.h>
|
|
32 #include <stdlib.h>
|
|
33 #include <string.h>
|
|
34 #include <syslog.h>
|
|
35 #include <unistd.h>
|
|
36 #include <sys/wait.h>
|
|
37
|
|
38 #include <bsm/adt.h>
|
|
39 #include <bsm/adt_event.h>
|
|
40 #include "login_audit.h"
|
|
41
|
|
42 /*
|
|
43 * Key assumption: login is single threaded.
|
|
44 */
|
|
45 static void audit_logout(adt_session_data_t *);
|
|
46
|
|
47 /*
|
|
48 * if audit is not enabled, the adt_*() functions simply return without
|
|
49 * doing anything. In the success case, the credential has already been
|
|
50 * setup with audit data by PAM.
|
|
51 */
|
|
52
|
|
53 /*
|
|
54 * There is no information passed to login.c from rlogin or telnet
|
|
55 * about the terminal id. They both set the tid before they
|
|
56 * exec login; the value is picked up by adt_start_session() and is
|
|
57 * carefully *not* overwritten by adt_load_hostname().
|
|
58 */
|
|
59
|
|
60 void
|
|
61 audit_success(uint_t event_id, struct passwd *pwd, char *optional_text)
|
|
62 {
|
|
63 adt_session_data_t *ah;
|
|
64 adt_event_data_t *event;
|
|
65 int rc;
|
|
66
|
|
67 assert(pwd != NULL);
|
|
68
|
|
69 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA)) {
|
|
70 syslog(LOG_AUTH | LOG_ALERT, "login adt_start_session(): %m");
|
|
71 return;
|
|
72 }
|
|
73 if (adt_set_user(ah, pwd->pw_uid, pwd->pw_gid,
|
|
74 pwd->pw_uid, pwd->pw_gid, NULL, ADT_USER)) {
|
|
75 syslog(LOG_AUTH | LOG_ALERT, "login adt_set_user(): %m");
|
|
76 (void) adt_end_session(ah);
|
|
77 return;
|
|
78 }
|
|
79 event = adt_alloc_event(ah, event_id);
|
|
80
|
|
81 if (event == NULL)
|
|
82 return;
|
|
83
|
|
84 switch (event_id) {
|
|
85 case ADT_zlogin:
|
|
86 event->adt_zlogin.message = optional_text;
|
|
87 break;
|
|
88 default:
|
|
89 break;
|
|
90 }
|
|
91 rc = adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS);
|
|
92
|
|
93 (void) adt_free_event(event);
|
|
94 if (rc) {
|
|
95 (void) adt_end_session(ah);
|
|
96 syslog(LOG_AUTH | LOG_ALERT, "login adt_put_event(): %m");
|
|
97 return;
|
|
98 }
|
|
99 /*
|
|
100 * The code above executes whether or not audit is enabled.
|
|
101 * However audit_logout must only execute if audit is
|
|
102 * enabled so we don't fork unnecessarily.
|
|
103 */
|
|
104 if (adt_audit_enabled()) {
|
|
105 switch (event_id) {
|
|
106 case ADT_login:
|
|
107 case ADT_rlogin:
|
|
108 case ADT_telnet:
|
|
109 case ADT_zlogin:
|
|
110 audit_logout(ah); /* fork to catch logout */
|
|
111 break;
|
|
112 }
|
|
113 }
|
|
114 (void) adt_end_session(ah);
|
|
115 }
|
|
116
|
|
117 /*
|
|
118 * errors are ignored since there is no action to take on error
|
|
119 */
|
|
120 static void
|
|
121 audit_logout(adt_session_data_t *ah)
|
|
122 {
|
|
123 adt_event_data_t *logout;
|
|
124 int status; /* wait status */
|
|
125 pid_t pid;
|
|
126 priv_set_t *priv; /* waiting process privs */
|
|
127
|
|
128 if ((logout = adt_alloc_event(ah, ADT_logout)) == NULL) {
|
|
129 syslog(LOG_AUTH | LOG_ALERT,
|
|
130 "adt_alloc_event(ADT_logout): %m");
|
|
131 return;
|
|
132 }
|
|
133 if ((priv = priv_allocset()) == NULL) {
|
|
134 syslog(LOG_AUTH | LOG_ALERT,
|
|
135 "login audit_logout: could not alloc privs: %m");
|
|
136 adt_free_event(logout);
|
|
137 return;
|
|
138 }
|
|
139
|
|
140 /*
|
|
141 * The child returns and continues the login processing.
|
|
142 * The parent's sole job is to wait for child exit, write the
|
|
143 * logout audit record, and replay the child's exit code.
|
|
144 */
|
|
145
|
|
146 if ((pid = fork()) == 0) {
|
|
147 /* child */
|
|
148
|
|
149 adt_free_event(logout);
|
|
150 priv_freeset(priv);
|
|
151 return;
|
|
152 }
|
|
153 if (pid == -1) {
|
|
154 /* failure */
|
|
155
|
|
156 syslog(LOG_AUTH | LOG_ALERT,
|
|
157 "login audit_logout: could not fork: %m");
|
|
158 adt_free_event(logout);
|
|
159 priv_freeset(priv);
|
|
160 return;
|
|
161 }
|
|
162
|
|
163 /* parent process */
|
|
164
|
|
165 /*
|
|
166 * When this routine is called, the current working
|
|
167 * directory is the user's home directory and there are
|
|
168 * unknown open files. For the waiting process, change the
|
|
169 * current directory to root and close files so that the
|
|
170 * user's home directory and anything else can be unmounted
|
|
171 * if necessary.
|
|
172 */
|
|
173 if (chdir("/") != 0) {
|
|
174 syslog(LOG_AUTH | LOG_ALERT,
|
|
175 "login audit_logut: could not chdir /: %m");
|
|
176 }
|
|
177 /*
|
|
178 * Reduce privileges to just those needed.
|
|
179 */
|
|
180 priv_emptyset(priv);
|
|
181 if ((priv_addset(priv, PRIV_PROC_AUDIT) != 0) ||
|
|
182 (setppriv(PRIV_SET, PRIV_PERMITTED, priv) != 0)) {
|
|
183 syslog(LOG_AUTH | LOG_ALERT,
|
|
184 "login audit_logout: could not reduce privs: %m");
|
|
185 }
|
|
186 closefrom(0);
|
|
187 priv_freeset(priv);
|
|
188 while (pid != waitpid(pid, &status, 0))
|
|
189 continue;
|
|
190
|
|
191 (void) adt_put_event(logout, ADT_SUCCESS, ADT_SUCCESS);
|
|
192 adt_free_event(logout);
|
|
193 (void) adt_end_session(ah);
|
|
194 exit(WEXITSTATUS(status));
|
|
195 }
|
|
196
|
|
197 /*
|
|
198 * errors are ignored since there is no action to take on error.
|
|
199 *
|
|
200 * If the user id is invalid, pwd is NULL.
|
|
201 */
|
|
202 void
|
|
203 audit_failure(uint_t event_id, int failure_code, struct passwd *pwd,
|
|
204 const char *hostname, const char *ttyname, char *optional_text)
|
|
205 {
|
|
206 adt_session_data_t *ah;
|
|
207 adt_event_data_t *event;
|
|
208 uid_t uid;
|
|
209 gid_t gid;
|
|
210 adt_termid_t *p_tid;
|
|
211
|
|
212 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA))
|
|
213 return;
|
|
214
|
|
215 uid = ADT_NO_ATTRIB;
|
|
216 gid = ADT_NO_ATTRIB;
|
|
217 if (pwd != NULL) {
|
|
218 uid = pwd->pw_uid;
|
|
219 gid = pwd->pw_gid;
|
|
220 }
|
|
221 /*
|
|
222 * If this is a remote login, in.rlogind or in.telnetd has
|
|
223 * already set the terminal id, in which case
|
|
224 * adt_load_hostname() will use the preset terminal id and
|
|
225 * ignore hostname. (If no remote host and ttyname is NULL,
|
|
226 * let adt_load_ttyname() figure out what to do.)
|
|
227 */
|
|
228 if (*hostname == '\0')
|
|
229 (void) adt_load_ttyname(ttyname, &p_tid);
|
|
230 else
|
|
231 (void) adt_load_hostname(hostname, &p_tid);
|
|
232
|
|
233 if (adt_set_user(ah, uid, gid, uid, gid, p_tid, ADT_NEW)) {
|
|
234 (void) adt_end_session(ah);
|
|
235 if (p_tid != NULL)
|
|
236 free(p_tid);
|
|
237 return;
|
|
238 }
|
|
239 if (p_tid != NULL)
|
|
240 free(p_tid);
|
|
241
|
|
242 event = adt_alloc_event(ah, event_id);
|
|
243 if (event == NULL) {
|
|
244 return;
|
|
245 }
|
|
246 switch (event_id) {
|
|
247 case ADT_zlogin:
|
|
248 event->adt_zlogin.message = optional_text;
|
|
249 break;
|
|
250 }
|
|
251 (void) adt_put_event(event, ADT_FAILURE, failure_code);
|
|
252
|
|
253 adt_free_event(event);
|
|
254 (void) adt_end_session(ah);
|
|
255 }
|