]>
Commit | Line | Data |
---|---|---|
7b578f7d | 1 | /* $Id$ */ |
2 | ||
3 | /* | |
4 | * TODO | |
5 | * | |
6 | * - deal with overlap between this and sys_auth_allowed_user | |
7 | * sys_auth_record_login and record_failed_login. | |
8 | */ | |
9 | ||
10 | /* | |
11 | * Copyright 1988-2002 Sun Microsystems, Inc. All rights reserved. | |
12 | * Use is subject to license terms. | |
13 | * | |
14 | * Redistribution and use in source and binary forms, with or without | |
15 | * modification, are permitted provided that the following conditions | |
16 | * are met: | |
17 | * 1. Redistributions of source code must retain the above copyright | |
18 | * notice, this list of conditions and the following disclaimer. | |
19 | * 2. Redistributions in binary form must reproduce the above copyright | |
20 | * notice, this list of conditions and the following disclaimer in the | |
21 | * documentation and/or other materials provided with the distribution. | |
22 | * | |
23 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
24 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
25 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
26 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
27 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
28 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
32 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
33 | * | |
34 | */ | |
35 | /* #pragma ident "@(#)bsmaudit.c 1.1 01/09/17 SMI" */ | |
36 | ||
37 | #include "includes.h" | |
38 | #if defined(USE_BSM_AUDIT) | |
39 | ||
5388904a | 40 | #include <sys/types.h> |
41 | ||
00dea73e | 42 | #include <errno.h> |
a74e9b64 | 43 | #include <netdb.h> |
24436b92 | 44 | #include <stdarg.h> |
a74e9b64 | 45 | #include <string.h> |
5388904a | 46 | #include <unistd.h> |
47 | ||
7b578f7d | 48 | #include "ssh.h" |
49 | #include "log.h" | |
6f54ce27 | 50 | #include "key.h" |
51 | #include "hostfile.h" | |
7b578f7d | 52 | #include "auth.h" |
53 | #include "xmalloc.h" | |
54 | ||
55 | #ifndef AUE_openssh | |
56 | # define AUE_openssh 32800 | |
57 | #endif | |
58 | #include <bsm/audit.h> | |
59 | #include <bsm/libbsm.h> | |
60 | #include <bsm/audit_uevents.h> | |
61 | #include <bsm/audit_record.h> | |
62 | #include <locale.h> | |
63 | ||
64 | #if defined(HAVE_GETAUDIT_ADDR) | |
65 | #define AuditInfoStruct auditinfo_addr | |
66 | #define AuditInfoTermID au_tid_addr_t | |
7b578f7d | 67 | #define SetAuditFunc(a,b) setaudit_addr((a),(b)) |
68 | #define SetAuditFuncText "setaudit_addr" | |
69 | #define AUToSubjectFunc au_to_subject_ex | |
70 | #define AUToReturnFunc(a,b) au_to_return32((a), (int32_t)(b)) | |
71 | #else | |
72 | #define AuditInfoStruct auditinfo | |
73 | #define AuditInfoTermID au_tid_t | |
7b578f7d | 74 | #define SetAuditFunc(a,b) setaudit(a) |
75 | #define SetAuditFuncText "setaudit" | |
76 | #define AUToSubjectFunc au_to_subject | |
77 | #define AUToReturnFunc(a,b) au_to_return((a), (u_int)(b)) | |
78 | #endif | |
79 | ||
a74e9b64 | 80 | #ifndef cannot_audit |
7b578f7d | 81 | extern int cannot_audit(int); |
a74e9b64 | 82 | #endif |
7b578f7d | 83 | extern void aug_init(void); |
7b578f7d | 84 | extern void aug_save_auid(au_id_t); |
85 | extern void aug_save_uid(uid_t); | |
86 | extern void aug_save_euid(uid_t); | |
87 | extern void aug_save_gid(gid_t); | |
88 | extern void aug_save_egid(gid_t); | |
89 | extern void aug_save_pid(pid_t); | |
90 | extern void aug_save_asid(au_asid_t); | |
91 | extern void aug_save_tid(dev_t, unsigned int); | |
92 | extern void aug_save_tid_ex(dev_t, u_int32_t *, u_int32_t); | |
93 | extern int aug_save_me(void); | |
94 | extern int aug_save_namask(void); | |
95 | extern void aug_save_event(au_event_t); | |
96 | extern void aug_save_sorf(int); | |
97 | extern void aug_save_text(char *); | |
98 | extern void aug_save_text1(char *); | |
99 | extern void aug_save_text2(char *); | |
100 | extern void aug_save_na(int); | |
101 | extern void aug_save_user(char *); | |
102 | extern void aug_save_path(char *); | |
103 | extern int aug_save_policy(void); | |
104 | extern void aug_save_afunc(int (*)(int)); | |
105 | extern int aug_audit(void); | |
106 | extern int aug_na_selected(void); | |
107 | extern int aug_selected(void); | |
108 | extern int aug_daemon_session(void); | |
109 | ||
110 | #ifndef HAVE_GETTEXT | |
111 | # define gettext(a) (a) | |
112 | #endif | |
113 | ||
114 | extern Authctxt *the_authctxt; | |
115 | static AuditInfoTermID ssh_bsm_tid; | |
116 | ||
117 | /* Below is the low-level BSM interface code */ | |
118 | ||
a74e9b64 | 119 | /* |
120 | * aug_get_machine is only required on IPv6 capable machines, we use a | |
121 | * different mechanism in audit_connection_from() for IPv4-only machines. | |
122 | * getaudit_addr() is only present on IPv6 capable machines. | |
123 | */ | |
124 | #if defined(HAVE_AUG_GET_MACHINE) || !defined(HAVE_GETAUDIT_ADDR) | |
125 | extern int aug_get_machine(char *, u_int32_t *, u_int32_t *); | |
126 | #else | |
127 | static int | |
128 | aug_get_machine(char *host, u_int32_t *addr, u_int32_t *type) | |
129 | { | |
130 | struct addrinfo *ai; | |
131 | struct sockaddr_in *in4; | |
132 | struct sockaddr_in6 *in6; | |
133 | int ret = 0, r; | |
134 | ||
135 | if ((r = getaddrinfo(host, NULL, NULL, &ai)) != 0) { | |
136 | error("BSM audit: getaddrinfo failed for %.100s: %.100s", host, | |
137 | r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r)); | |
138 | return -1; | |
139 | } | |
140 | ||
141 | switch (ai->ai_family) { | |
142 | case AF_INET: | |
143 | in4 = (struct sockaddr_in *)ai->ai_addr; | |
144 | *type = AU_IPv4; | |
145 | memcpy(addr, &in4->sin_addr, sizeof(struct in_addr)); | |
146 | break; | |
147 | #ifdef AU_IPv6 | |
148 | case AF_INET6: | |
149 | in6 = (struct sockaddr_in6 *)ai->ai_addr; | |
150 | *type = AU_IPv6; | |
151 | memcpy(addr, &in6->sin6_addr, sizeof(struct in6_addr)); | |
152 | break; | |
153 | #endif | |
154 | default: | |
155 | error("BSM audit: unknown address family for %.100s: %d", | |
156 | host, ai->ai_family); | |
157 | ret = -1; | |
158 | } | |
159 | freeaddrinfo(ai); | |
160 | return ret; | |
161 | } | |
162 | #endif | |
163 | ||
7b578f7d | 164 | /* |
165 | * Check if the specified event is selected (enabled) for auditing. | |
166 | * Returns 1 if the event is selected, 0 if not and -1 on failure. | |
167 | */ | |
168 | static int | |
169 | selected(char *username, uid_t uid, au_event_t event, int sf) | |
170 | { | |
171 | int rc, sorf; | |
172 | char naflags[512]; | |
173 | struct au_mask mask; | |
174 | ||
175 | mask.am_success = mask.am_failure = 0; | |
176 | if (uid < 0) { | |
177 | /* get flags for non-attributable (to a real user) events */ | |
178 | rc = getacna(naflags, sizeof(naflags)); | |
179 | if (rc == 0) | |
180 | (void) getauditflagsbin(naflags, &mask); | |
181 | } else | |
182 | rc = au_user_mask(username, &mask); | |
183 | ||
184 | sorf = (sf == 0) ? AU_PRS_SUCCESS : AU_PRS_FAILURE; | |
185 | return(au_preselect(event, &mask, sorf, AU_PRS_REREAD)); | |
186 | } | |
187 | ||
188 | static void | |
189 | bsm_audit_record(int typ, char *string, au_event_t event_no) | |
190 | { | |
191 | int ad, rc, sel; | |
192 | uid_t uid = -1; | |
193 | gid_t gid = -1; | |
194 | pid_t pid = getpid(); | |
195 | AuditInfoTermID tid = ssh_bsm_tid; | |
196 | ||
197 | if (the_authctxt != NULL && the_authctxt->valid) { | |
198 | uid = the_authctxt->pw->pw_uid; | |
199 | gid = the_authctxt->pw->pw_gid; | |
200 | } | |
201 | ||
202 | rc = (typ == 0) ? 0 : -1; | |
203 | sel = selected(the_authctxt->user, uid, event_no, rc); | |
204 | debug3("BSM audit: typ %d rc %d \"%s\"", typ, rc, string); | |
205 | if (!sel) | |
206 | return; /* audit event does not match mask, do not write */ | |
207 | ||
208 | debug3("BSM audit: writing audit new record"); | |
209 | ad = au_open(); | |
210 | ||
211 | (void) au_write(ad, AUToSubjectFunc(uid, uid, gid, uid, gid, | |
212 | pid, pid, &tid)); | |
213 | (void) au_write(ad, au_to_text(string)); | |
214 | (void) au_write(ad, AUToReturnFunc(typ, rc)); | |
215 | ||
216 | rc = au_close(ad, AU_TO_WRITE, event_no); | |
217 | if (rc < 0) | |
218 | error("BSM audit: %s failed to write \"%s\" record: %s", | |
219 | __func__, string, strerror(errno)); | |
220 | } | |
221 | ||
222 | static void | |
223 | bsm_audit_session_setup(void) | |
224 | { | |
225 | int rc; | |
226 | struct AuditInfoStruct info; | |
227 | au_mask_t mask; | |
228 | ||
229 | if (the_authctxt == NULL) { | |
230 | error("BSM audit: session setup internal error (NULL ctxt)"); | |
231 | return; | |
232 | } | |
233 | ||
234 | if (the_authctxt->valid) | |
235 | info.ai_auid = the_authctxt->pw->pw_uid; | |
236 | else | |
237 | info.ai_auid = -1; | |
238 | info.ai_asid = getpid(); | |
239 | mask.am_success = 0; | |
240 | mask.am_failure = 0; | |
241 | ||
242 | (void) au_user_mask(the_authctxt->user, &mask); | |
243 | ||
244 | info.ai_mask.am_success = mask.am_success; | |
245 | info.ai_mask.am_failure = mask.am_failure; | |
246 | ||
247 | info.ai_termid = ssh_bsm_tid; | |
248 | ||
249 | rc = SetAuditFunc(&info, sizeof(info)); | |
250 | if (rc < 0) | |
251 | error("BSM audit: %s: %s failed: %s", __func__, | |
252 | SetAuditFuncText, strerror(errno)); | |
253 | } | |
254 | ||
255 | static void | |
256 | bsm_audit_bad_login(const char *what) | |
257 | { | |
258 | char textbuf[BSM_TEXTBUFSZ]; | |
259 | ||
260 | if (the_authctxt->valid) { | |
261 | (void) snprintf(textbuf, sizeof (textbuf), | |
262 | gettext("invalid %s for user %s"), | |
263 | what, the_authctxt->user); | |
264 | bsm_audit_record(4, textbuf, AUE_openssh); | |
265 | } else { | |
266 | (void) snprintf(textbuf, sizeof (textbuf), | |
267 | gettext("invalid user name \"%s\""), | |
268 | the_authctxt->user); | |
269 | bsm_audit_record(3, textbuf, AUE_openssh); | |
270 | } | |
271 | } | |
272 | ||
273 | /* Below is the sshd audit API code */ | |
274 | ||
275 | void | |
276 | audit_connection_from(const char *host, int port) | |
277 | { | |
278 | AuditInfoTermID *tid = &ssh_bsm_tid; | |
279 | char buf[1024]; | |
280 | ||
281 | if (cannot_audit(0)) | |
282 | return; | |
283 | debug3("BSM audit: connection from %.100s port %d", host, port); | |
284 | ||
285 | /* populate our terminal id structure */ | |
286 | #if defined(HAVE_GETAUDIT_ADDR) | |
287 | tid->at_port = (dev_t)port; | |
288 | aug_get_machine((char *)host, &(tid->at_addr[0]), &(tid->at_type)); | |
289 | snprintf(buf, sizeof(buf), "%08x %08x %08x %08x", tid->at_addr[0], | |
290 | tid->at_addr[1], tid->at_addr[2], tid->at_addr[3]); | |
291 | debug3("BSM audit: iptype %d machine ID %s", (int)tid->at_type, buf); | |
292 | #else | |
293 | /* this is used on IPv4-only machines */ | |
294 | tid->port = (dev_t)port; | |
295 | tid->machine = inet_addr(host); | |
296 | snprintf(buf, sizeof(buf), "%08x", tid->machine); | |
297 | debug3("BSM audit: machine ID %s", buf); | |
298 | #endif | |
299 | } | |
300 | ||
301 | void | |
302 | audit_run_command(const char *command) | |
303 | { | |
304 | /* not implemented */ | |
305 | } | |
306 | ||
307 | void | |
308 | audit_session_open(const char *ttyn) | |
309 | { | |
310 | /* not implemented */ | |
311 | } | |
312 | ||
313 | void | |
314 | audit_session_close(const char *ttyn) | |
315 | { | |
316 | /* not implemented */ | |
317 | } | |
318 | ||
319 | void | |
320 | audit_event(ssh_audit_event_t event) | |
321 | { | |
322 | char textbuf[BSM_TEXTBUFSZ]; | |
323 | static int logged_in = 0; | |
324 | const char *user = the_authctxt ? the_authctxt->user : "(unknown user)"; | |
325 | ||
326 | if (cannot_audit(0)) | |
327 | return; | |
328 | ||
329 | switch(event) { | |
330 | case SSH_AUTH_SUCCESS: | |
331 | logged_in = 1; | |
332 | bsm_audit_session_setup(); | |
333 | snprintf(textbuf, sizeof(textbuf), | |
334 | gettext("successful login %s"), user); | |
335 | bsm_audit_record(0, textbuf, AUE_openssh); | |
336 | break; | |
337 | ||
338 | case SSH_CONNECTION_CLOSE: | |
339 | /* | |
340 | * We can also get a close event if the user attempted auth | |
341 | * but never succeeded. | |
342 | */ | |
343 | if (logged_in) { | |
344 | snprintf(textbuf, sizeof(textbuf), | |
345 | gettext("sshd logout %s"), the_authctxt->user); | |
346 | bsm_audit_record(0, textbuf, AUE_logout); | |
347 | } else { | |
348 | debug("%s: connection closed without authentication", | |
349 | __func__); | |
350 | } | |
351 | break; | |
352 | ||
353 | case SSH_NOLOGIN: | |
354 | bsm_audit_record(1, | |
355 | gettext("logins disabled by /etc/nologin"), AUE_openssh); | |
356 | break; | |
357 | ||
358 | case SSH_LOGIN_EXCEED_MAXTRIES: | |
359 | snprintf(textbuf, sizeof(textbuf), | |
360 | gettext("too many tries for user %s"), the_authctxt->user); | |
361 | bsm_audit_record(1, textbuf, AUE_openssh); | |
362 | break; | |
363 | ||
364 | case SSH_LOGIN_ROOT_DENIED: | |
365 | bsm_audit_record(2, gettext("not_console"), AUE_openssh); | |
366 | break; | |
367 | ||
368 | case SSH_AUTH_FAIL_PASSWD: | |
369 | bsm_audit_bad_login("password"); | |
370 | break; | |
371 | ||
372 | case SSH_AUTH_FAIL_KBDINT: | |
373 | bsm_audit_bad_login("interactive password entry"); | |
374 | break; | |
375 | ||
376 | default: | |
377 | debug("%s: unhandled event %d", __func__, event); | |
378 | } | |
379 | } | |
380 | #endif /* BSM */ |