blob: 0ba16c72c820dff48b1bd42daba8373105d54e40 [file] [log] [blame]
Darren Tucker8fe07942005-02-20 21:08:00 +11001/*
2 * TODO
3 *
4 * - deal with overlap between this and sys_auth_allowed_user
5 * sys_auth_record_login and record_failed_login.
6 */
7
8/*
9 * Copyright 1988-2002 Sun Microsystems, Inc. All rights reserved.
10 * Use is subject to license terms.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33/* #pragma ident "@(#)bsmaudit.c 1.1 01/09/17 SMI" */
34
35#include "includes.h"
36#if defined(USE_BSM_AUDIT)
37
Damien Miller56799c32006-08-16 11:40:45 +100038#include <sys/types.h>
39
Darren Tucker47bda1f2006-10-01 08:09:50 +100040#include <errno.h>
Darren Tuckeracada072008-02-25 21:05:04 +110041#include <netdb.h>
Damien Millerded319c2006-09-01 15:38:36 +100042#include <stdarg.h>
Darren Tuckeracada072008-02-25 21:05:04 +110043#include <string.h>
Damien Miller56799c32006-08-16 11:40:45 +100044#include <unistd.h>
45
Darren Tucker93a2d412012-02-24 10:40:41 +110046#ifdef BROKEN_BSM_API
47#include <libscf.h>
48#endif
49
Darren Tucker8fe07942005-02-20 21:08:00 +110050#include "ssh.h"
51#include "log.h"
Darren Tucker3e714512006-08-06 00:12:54 +100052#include "hostfile.h"
Darren Tucker8fe07942005-02-20 21:08:00 +110053#include "auth.h"
54#include "xmalloc.h"
55
56#ifndef AUE_openssh
57# define AUE_openssh 32800
58#endif
59#include <bsm/audit.h>
60#include <bsm/libbsm.h>
61#include <bsm/audit_uevents.h>
62#include <bsm/audit_record.h>
63#include <locale.h>
64
65#if defined(HAVE_GETAUDIT_ADDR)
66#define AuditInfoStruct auditinfo_addr
67#define AuditInfoTermID au_tid_addr_t
Darren Tucker8fe07942005-02-20 21:08:00 +110068#define SetAuditFunc(a,b) setaudit_addr((a),(b))
69#define SetAuditFuncText "setaudit_addr"
70#define AUToSubjectFunc au_to_subject_ex
71#define AUToReturnFunc(a,b) au_to_return32((a), (int32_t)(b))
72#else
73#define AuditInfoStruct auditinfo
74#define AuditInfoTermID au_tid_t
Darren Tucker8fe07942005-02-20 21:08:00 +110075#define SetAuditFunc(a,b) setaudit(a)
76#define SetAuditFuncText "setaudit"
77#define AUToSubjectFunc au_to_subject
78#define AUToReturnFunc(a,b) au_to_return((a), (u_int)(b))
79#endif
80
Darren Tuckeracada072008-02-25 21:05:04 +110081#ifndef cannot_audit
Darren Tucker8fe07942005-02-20 21:08:00 +110082extern int cannot_audit(int);
Darren Tuckeracada072008-02-25 21:05:04 +110083#endif
Darren Tucker8fe07942005-02-20 21:08:00 +110084extern void aug_init(void);
Darren Tucker8fe07942005-02-20 21:08:00 +110085extern void aug_save_auid(au_id_t);
86extern void aug_save_uid(uid_t);
87extern void aug_save_euid(uid_t);
88extern void aug_save_gid(gid_t);
89extern void aug_save_egid(gid_t);
90extern void aug_save_pid(pid_t);
91extern void aug_save_asid(au_asid_t);
92extern void aug_save_tid(dev_t, unsigned int);
93extern void aug_save_tid_ex(dev_t, u_int32_t *, u_int32_t);
94extern int aug_save_me(void);
95extern int aug_save_namask(void);
96extern void aug_save_event(au_event_t);
97extern void aug_save_sorf(int);
98extern void aug_save_text(char *);
99extern void aug_save_text1(char *);
100extern void aug_save_text2(char *);
101extern void aug_save_na(int);
102extern void aug_save_user(char *);
103extern void aug_save_path(char *);
104extern int aug_save_policy(void);
105extern void aug_save_afunc(int (*)(int));
106extern int aug_audit(void);
107extern int aug_na_selected(void);
108extern int aug_selected(void);
109extern int aug_daemon_session(void);
110
111#ifndef HAVE_GETTEXT
112# define gettext(a) (a)
113#endif
114
115extern Authctxt *the_authctxt;
116static AuditInfoTermID ssh_bsm_tid;
117
Darren Tucker93a2d412012-02-24 10:40:41 +1100118#ifdef BROKEN_BSM_API
119/* For some reason this constant is no longer defined
120 in Solaris 11. */
121#define BSM_TEXTBUFSZ 256
122#endif
123
Darren Tucker8fe07942005-02-20 21:08:00 +1100124/* Below is the low-level BSM interface code */
125
126/*
Darren Tuckeracada072008-02-25 21:05:04 +1100127 * aug_get_machine is only required on IPv6 capable machines, we use a
128 * different mechanism in audit_connection_from() for IPv4-only machines.
129 * getaudit_addr() is only present on IPv6 capable machines.
130 */
131#if defined(HAVE_AUG_GET_MACHINE) || !defined(HAVE_GETAUDIT_ADDR)
132extern int aug_get_machine(char *, u_int32_t *, u_int32_t *);
133#else
134static int
135aug_get_machine(char *host, u_int32_t *addr, u_int32_t *type)
136{
137 struct addrinfo *ai;
138 struct sockaddr_in *in4;
139 struct sockaddr_in6 *in6;
140 int ret = 0, r;
141
142 if ((r = getaddrinfo(host, NULL, NULL, &ai)) != 0) {
143 error("BSM audit: getaddrinfo failed for %.100s: %.100s", host,
144 r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r));
145 return -1;
146 }
147
148 switch (ai->ai_family) {
149 case AF_INET:
150 in4 = (struct sockaddr_in *)ai->ai_addr;
151 *type = AU_IPv4;
152 memcpy(addr, &in4->sin_addr, sizeof(struct in_addr));
153 break;
154#ifdef AU_IPv6
155 case AF_INET6:
156 in6 = (struct sockaddr_in6 *)ai->ai_addr;
157 *type = AU_IPv6;
158 memcpy(addr, &in6->sin6_addr, sizeof(struct in6_addr));
159 break;
160#endif
161 default:
162 error("BSM audit: unknown address family for %.100s: %d",
163 host, ai->ai_family);
164 ret = -1;
165 }
166 freeaddrinfo(ai);
167 return ret;
168}
169#endif
170
Darren Tucker93a2d412012-02-24 10:40:41 +1100171#ifdef BROKEN_BSM_API
172/*
173 In Solaris 11 the audit daemon has been moved to SMF. In the process
174 they simply dropped getacna() from the API, since it read from a now
175 non-existent config file. This function re-implements getacna() to
176 read from the SMF repository instead.
177 */
178int
179getacna(char *auditstring, int len)
180{
181 scf_handle_t *handle = NULL;
182 scf_property_t *property = NULL;
183 scf_value_t *value = NULL;
184 int ret = 0;
185
186 handle = scf_handle_create(SCF_VERSION);
187 if (handle == NULL)
188 return -2; /* The man page for getacna on Solaris 10 states
189 we should return -2 in case of error and set
190 errno to indicate the error. We don't bother
191 with errno here, though, since the only use
192 of this function below doesn't check for errors
193 anyway.
194 */
195
196 ret = scf_handle_bind(handle);
197 if (ret == -1)
198 return -2;
199
200 property = scf_property_create(handle);
201 if (property == NULL)
202 return -2;
203
204 ret = scf_handle_decode_fmri(handle,
205 "svc:/system/auditd:default/:properties/preselection/naflags",
206 NULL, NULL, NULL, NULL, property, 0);
207 if (ret == -1)
208 return -2;
209
210 value = scf_value_create(handle);
211 if (value == NULL)
212 return -2;
213
214 ret = scf_property_get_value(property, value);
215 if (ret == -1)
216 return -2;
217
218 ret = scf_value_get_astring(value, auditstring, len);
219 if (ret == -1)
220 return -2;
221
222 scf_value_destroy(value);
223 scf_property_destroy(property);
224 scf_handle_destroy(handle);
225
226 return 0;
227}
228#endif
229
Darren Tuckeracada072008-02-25 21:05:04 +1100230/*
Darren Tucker8fe07942005-02-20 21:08:00 +1100231 * Check if the specified event is selected (enabled) for auditing.
232 * Returns 1 if the event is selected, 0 if not and -1 on failure.
233 */
234static int
235selected(char *username, uid_t uid, au_event_t event, int sf)
236{
237 int rc, sorf;
238 char naflags[512];
239 struct au_mask mask;
240
241 mask.am_success = mask.am_failure = 0;
242 if (uid < 0) {
243 /* get flags for non-attributable (to a real user) events */
244 rc = getacna(naflags, sizeof(naflags));
245 if (rc == 0)
246 (void) getauditflagsbin(naflags, &mask);
247 } else
248 rc = au_user_mask(username, &mask);
249
250 sorf = (sf == 0) ? AU_PRS_SUCCESS : AU_PRS_FAILURE;
251 return(au_preselect(event, &mask, sorf, AU_PRS_REREAD));
252}
253
254static void
255bsm_audit_record(int typ, char *string, au_event_t event_no)
256{
257 int ad, rc, sel;
258 uid_t uid = -1;
259 gid_t gid = -1;
260 pid_t pid = getpid();
261 AuditInfoTermID tid = ssh_bsm_tid;
262
263 if (the_authctxt != NULL && the_authctxt->valid) {
264 uid = the_authctxt->pw->pw_uid;
265 gid = the_authctxt->pw->pw_gid;
266 }
267
268 rc = (typ == 0) ? 0 : -1;
269 sel = selected(the_authctxt->user, uid, event_no, rc);
270 debug3("BSM audit: typ %d rc %d \"%s\"", typ, rc, string);
271 if (!sel)
272 return; /* audit event does not match mask, do not write */
273
274 debug3("BSM audit: writing audit new record");
275 ad = au_open();
276
277 (void) au_write(ad, AUToSubjectFunc(uid, uid, gid, uid, gid,
278 pid, pid, &tid));
279 (void) au_write(ad, au_to_text(string));
280 (void) au_write(ad, AUToReturnFunc(typ, rc));
281
Darren Tucker93a2d412012-02-24 10:40:41 +1100282#ifdef BROKEN_BSM_API
283 /* The last argument is the event modifier flags. For
284 some seemingly undocumented reason it was added in
285 Solaris 11. */
286 rc = au_close(ad, AU_TO_WRITE, event_no, 0);
287#else
Darren Tucker8fe07942005-02-20 21:08:00 +1100288 rc = au_close(ad, AU_TO_WRITE, event_no);
Darren Tucker93a2d412012-02-24 10:40:41 +1100289#endif
290
Darren Tucker8fe07942005-02-20 21:08:00 +1100291 if (rc < 0)
292 error("BSM audit: %s failed to write \"%s\" record: %s",
293 __func__, string, strerror(errno));
294}
295
296static void
297bsm_audit_session_setup(void)
298{
299 int rc;
300 struct AuditInfoStruct info;
301 au_mask_t mask;
302
303 if (the_authctxt == NULL) {
304 error("BSM audit: session setup internal error (NULL ctxt)");
305 return;
306 }
307
308 if (the_authctxt->valid)
309 info.ai_auid = the_authctxt->pw->pw_uid;
310 else
311 info.ai_auid = -1;
312 info.ai_asid = getpid();
313 mask.am_success = 0;
314 mask.am_failure = 0;
315
316 (void) au_user_mask(the_authctxt->user, &mask);
317
318 info.ai_mask.am_success = mask.am_success;
319 info.ai_mask.am_failure = mask.am_failure;
320
321 info.ai_termid = ssh_bsm_tid;
322
323 rc = SetAuditFunc(&info, sizeof(info));
324 if (rc < 0)
325 error("BSM audit: %s: %s failed: %s", __func__,
326 SetAuditFuncText, strerror(errno));
327}
328
329static void
330bsm_audit_bad_login(const char *what)
331{
332 char textbuf[BSM_TEXTBUFSZ];
333
334 if (the_authctxt->valid) {
335 (void) snprintf(textbuf, sizeof (textbuf),
336 gettext("invalid %s for user %s"),
337 what, the_authctxt->user);
338 bsm_audit_record(4, textbuf, AUE_openssh);
339 } else {
340 (void) snprintf(textbuf, sizeof (textbuf),
341 gettext("invalid user name \"%s\""),
342 the_authctxt->user);
343 bsm_audit_record(3, textbuf, AUE_openssh);
344 }
345}
346
347/* Below is the sshd audit API code */
348
349void
350audit_connection_from(const char *host, int port)
351{
352 AuditInfoTermID *tid = &ssh_bsm_tid;
353 char buf[1024];
354
355 if (cannot_audit(0))
356 return;
357 debug3("BSM audit: connection from %.100s port %d", host, port);
358
359 /* populate our terminal id structure */
360#if defined(HAVE_GETAUDIT_ADDR)
361 tid->at_port = (dev_t)port;
362 aug_get_machine((char *)host, &(tid->at_addr[0]), &(tid->at_type));
363 snprintf(buf, sizeof(buf), "%08x %08x %08x %08x", tid->at_addr[0],
364 tid->at_addr[1], tid->at_addr[2], tid->at_addr[3]);
365 debug3("BSM audit: iptype %d machine ID %s", (int)tid->at_type, buf);
366#else
367 /* this is used on IPv4-only machines */
368 tid->port = (dev_t)port;
369 tid->machine = inet_addr(host);
370 snprintf(buf, sizeof(buf), "%08x", tid->machine);
371 debug3("BSM audit: machine ID %s", buf);
372#endif
373}
374
375void
376audit_run_command(const char *command)
377{
378 /* not implemented */
379}
380
381void
Darren Tuckerea52a822011-01-17 21:15:27 +1100382audit_session_open(struct logininfo *li)
Darren Tucker8fe07942005-02-20 21:08:00 +1100383{
384 /* not implemented */
385}
386
387void
Darren Tuckerea52a822011-01-17 21:15:27 +1100388audit_session_close(struct logininfo *li)
Darren Tucker8fe07942005-02-20 21:08:00 +1100389{
390 /* not implemented */
391}
392
393void
Damien Miller9b655dc2019-01-20 14:55:27 +1100394audit_event(struct ssh *ssh, ssh_audit_event_t event)
Darren Tucker8fe07942005-02-20 21:08:00 +1100395{
396 char textbuf[BSM_TEXTBUFSZ];
397 static int logged_in = 0;
398 const char *user = the_authctxt ? the_authctxt->user : "(unknown user)";
399
400 if (cannot_audit(0))
401 return;
402
403 switch(event) {
404 case SSH_AUTH_SUCCESS:
405 logged_in = 1;
406 bsm_audit_session_setup();
407 snprintf(textbuf, sizeof(textbuf),
408 gettext("successful login %s"), user);
409 bsm_audit_record(0, textbuf, AUE_openssh);
410 break;
411
412 case SSH_CONNECTION_CLOSE:
413 /*
414 * We can also get a close event if the user attempted auth
415 * but never succeeded.
416 */
417 if (logged_in) {
418 snprintf(textbuf, sizeof(textbuf),
419 gettext("sshd logout %s"), the_authctxt->user);
420 bsm_audit_record(0, textbuf, AUE_logout);
421 } else {
422 debug("%s: connection closed without authentication",
423 __func__);
424 }
425 break;
426
427 case SSH_NOLOGIN:
428 bsm_audit_record(1,
429 gettext("logins disabled by /etc/nologin"), AUE_openssh);
430 break;
431
432 case SSH_LOGIN_EXCEED_MAXTRIES:
433 snprintf(textbuf, sizeof(textbuf),
434 gettext("too many tries for user %s"), the_authctxt->user);
435 bsm_audit_record(1, textbuf, AUE_openssh);
436 break;
437
438 case SSH_LOGIN_ROOT_DENIED:
439 bsm_audit_record(2, gettext("not_console"), AUE_openssh);
440 break;
441
442 case SSH_AUTH_FAIL_PASSWD:
443 bsm_audit_bad_login("password");
444 break;
445
446 case SSH_AUTH_FAIL_KBDINT:
447 bsm_audit_bad_login("interactive password entry");
448 break;
449
450 default:
451 debug("%s: unhandled event %d", __func__, event);
452 }
453}
454#endif /* BSM */