xref: /illumos-gate/usr/src/cmd/login/login_audit.c (revision 3db86aab)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
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.
13  *
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]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <assert.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 
44 /*
45  * Key assumption:  login is single threaded.
46  */
47 static void audit_logout(adt_session_data_t *);
48 
49 /*
50  * if audit is not enabled, the adt_*() functions simply return without
51  * doing anything.  In the success case, the credential has already been
52  * setup with audit data by PAM.
53  */
54 
55 /*
56  * There is no information passed to login.c from rlogin or telnet
57  * about the terminal id.  They both set the tid before they
58  * exec login; the value is picked up by adt_start_session() and is
59  * carefully *not* overwritten by adt_load_hostname().
60  */
61 
62 void
63 audit_success(uint_t event_id, struct passwd *pwd, char *optional_text)
64 {
65 	adt_session_data_t	*ah;
66 	adt_event_data_t	*event;
67 	int			rc;
68 
69 	assert(pwd != NULL);
70 
71 	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA)) {
72 		syslog(LOG_AUTH | LOG_ALERT, "audit: %m");
73 		return;
74 	}
75 	if (adt_set_user(ah, pwd->pw_uid,  pwd->pw_gid,
76 	    pwd->pw_uid,  pwd->pw_gid, NULL, ADT_USER)) {
77 		syslog(LOG_AUTH | LOG_ALERT, "audit: %m");
78 		(void) adt_end_session(ah);
79 		return;
80 	}
81 	event = adt_alloc_event(ah, event_id);
82 
83 	if (event == NULL)
84 		return;
85 
86 	switch (event_id) {
87 	case ADT_zlogin:
88 		event->adt_zlogin.message = optional_text;
89 		break;
90 	default:
91 		break;
92 	}
93 	rc = adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS);
94 
95 	(void) adt_free_event(event);
96 	if (rc) {
97 		(void) adt_end_session(ah);
98 		syslog(LOG_AUTH | LOG_ALERT, "audit: %m");
99 		return;
100 	}
101 	/*
102 	 * The code above executes whether or not audit is enabled.
103 	 * However audit_logout must only execute if audit is
104 	 * enabled so we don't fork unnecessarily.
105 	 */
106 	if (adt_audit_enabled()) {
107 		switch (event_id) {
108 		case ADT_login:
109 		case ADT_rlogin:
110 		case ADT_telnet:
111 		case ADT_zlogin:
112 			audit_logout(ah);	/* fork to catch logout */
113 			break;
114 		}
115 	}
116 	(void) adt_end_session(ah);
117 }
118 
119 /*
120  * errors are ignored since there is no action to take on error
121  */
122 static void
123 audit_logout(adt_session_data_t *ah)
124 {
125 	adt_event_data_t	*logout;
126 	int			status;		/* wait status */
127 	pid_t			pid;
128 
129 	if ((pid = fork()) == 0) {
130 		return;
131 	} else if (pid == -1) {
132 		syslog(LOG_AUTH | LOG_ALERT, "login: could not fork: %m");
133 		exit(1);
134 	} else {
135 		/*
136 		 * When this routine is called, the current working
137 		 * directory is the user's home directory. Change it
138 		 * to root for the waiting process so that the user's
139 		 * home directory can be unmounted if necessary.
140 		 */
141 		if (chdir("/") != 0) {
142 			syslog(LOG_AUTH | LOG_ALERT,
143 			    "login: could not chdir: %m");
144 			/* since we let the child finish we just bail */
145 			exit(0);
146 		}
147 		while (pid != waitpid(pid, &status, 0))
148 			continue;
149 
150 		logout = adt_alloc_event(ah, ADT_logout);
151 		if (logout == NULL)
152 			exit(0);
153 
154 		(void) adt_put_event(logout, ADT_SUCCESS, ADT_SUCCESS);
155 
156 		adt_free_event(logout);
157 		exit(0);
158 	}
159 }
160 
161 /*
162  * errors are ignored since there is no action to take on error.
163  *
164  * If the user id is invalid, pwd is NULL.
165  */
166 void
167 audit_failure(uint_t event_id, int failure_code, struct passwd *pwd,
168     const char *hostname, const char *ttyname, char *optional_text)
169 {
170 	adt_session_data_t	*ah;
171 	adt_event_data_t	*event;
172 	uid_t			uid;
173 	gid_t			gid;
174 	adt_termid_t		*p_tid;
175 
176 	if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA))
177 		return;
178 
179 	uid = ADT_NO_ATTRIB;
180 	gid = ADT_NO_ATTRIB;
181 	if (pwd != NULL) {
182 		uid = pwd->pw_uid;
183 		gid = pwd->pw_gid;
184 	}
185 	/*
186 	 * If this is a remote login, in.rlogind or in.telnetd has
187 	 * already set the terminal id, in which case
188 	 * adt_load_hostname() will use the preset terminal id and
189 	 * ignore hostname.  (If no remote host and ttyname is NULL,
190 	 * let adt_load_ttyname() figure out what to do.)
191 	 */
192 	if (*hostname == '\0')
193 		(void) adt_load_ttyname(ttyname, &p_tid);
194 	else
195 		(void) adt_load_hostname(hostname, &p_tid);
196 
197 	if (adt_set_user(ah, uid, gid, uid, gid, p_tid, ADT_NEW)) {
198 		(void) adt_end_session(ah);
199 		if (p_tid != NULL)
200 			free(p_tid);
201 		return;
202 	}
203 	if (p_tid != NULL)
204 		free(p_tid);
205 
206 	event = adt_alloc_event(ah, event_id);
207 	if (event == NULL) {
208 		return;
209 	}
210 	switch (event_id) {
211 	case ADT_zlogin:
212 		event->adt_zlogin.message = optional_text;
213 		break;
214 	}
215 	(void) adt_put_event(event, ADT_FAILURE, failure_code);
216 
217 	adt_free_event(event);
218 	(void) adt_end_session(ah);
219 }
220