1/* $Id: audit-bsm.c,v 1.8 2012/02/23 23:40:43 dtucker Exp $ */ 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 40#include <sys/types.h> 41 42#include <errno.h> 43#include <netdb.h> 44#include <stdarg.h> 45#include <string.h> 46#include <unistd.h> 47 48#ifdef BROKEN_BSM_API 49#include <libscf.h> 50#endif 51 52#include "ssh.h" 53#include "log.h" 54#include "key.h" 55#include "hostfile.h" 56#include "auth.h" 57#include "xmalloc.h" 58 59#ifndef AUE_openssh 60# define AUE_openssh 32800 61#endif 62#include <bsm/audit.h> 63#include <bsm/libbsm.h> 64#include <bsm/audit_uevents.h> 65#include <bsm/audit_record.h> 66#include <locale.h> 67 68#if defined(HAVE_GETAUDIT_ADDR) 69#define AuditInfoStruct auditinfo_addr 70#define AuditInfoTermID au_tid_addr_t 71#define SetAuditFunc(a,b) setaudit_addr((a),(b)) 72#define SetAuditFuncText "setaudit_addr" 73#define AUToSubjectFunc au_to_subject_ex 74#define AUToReturnFunc(a,b) au_to_return32((a), (int32_t)(b)) 75#else 76#define AuditInfoStruct auditinfo 77#define AuditInfoTermID au_tid_t 78#define SetAuditFunc(a,b) setaudit(a) 79#define SetAuditFuncText "setaudit" 80#define AUToSubjectFunc au_to_subject 81#define AUToReturnFunc(a,b) au_to_return((a), (u_int)(b)) 82#endif 83 84#ifndef cannot_audit 85extern int cannot_audit(int); 86#endif 87extern void aug_init(void); 88extern void aug_save_auid(au_id_t); 89extern void aug_save_uid(uid_t); 90extern void aug_save_euid(uid_t); 91extern void aug_save_gid(gid_t); 92extern void aug_save_egid(gid_t); 93extern void aug_save_pid(pid_t); 94extern void aug_save_asid(au_asid_t); 95extern void aug_save_tid(dev_t, unsigned int); 96extern void aug_save_tid_ex(dev_t, u_int32_t *, u_int32_t); 97extern int aug_save_me(void); 98extern int aug_save_namask(void); 99extern void aug_save_event(au_event_t); 100extern void aug_save_sorf(int); 101extern void aug_save_text(char *); 102extern void aug_save_text1(char *); 103extern void aug_save_text2(char *); 104extern void aug_save_na(int); 105extern void aug_save_user(char *); 106extern void aug_save_path(char *); 107extern int aug_save_policy(void); 108extern void aug_save_afunc(int (*)(int)); 109extern int aug_audit(void); 110extern int aug_na_selected(void); 111extern int aug_selected(void); 112extern int aug_daemon_session(void); 113 114#ifndef HAVE_GETTEXT 115# define gettext(a) (a) 116#endif 117 118extern Authctxt *the_authctxt; 119static AuditInfoTermID ssh_bsm_tid; 120 121#ifdef BROKEN_BSM_API 122/* For some reason this constant is no longer defined 123 in Solaris 11. */ 124#define BSM_TEXTBUFSZ 256 125#endif 126 127/* Below is the low-level BSM interface code */ 128 129/* 130 * aug_get_machine is only required on IPv6 capable machines, we use a 131 * different mechanism in audit_connection_from() for IPv4-only machines. 132 * getaudit_addr() is only present on IPv6 capable machines. 133 */ 134#if defined(HAVE_AUG_GET_MACHINE) || !defined(HAVE_GETAUDIT_ADDR) 135extern int aug_get_machine(char *, u_int32_t *, u_int32_t *); 136#else 137static int 138aug_get_machine(char *host, u_int32_t *addr, u_int32_t *type) 139{ 140 struct addrinfo *ai; 141 struct sockaddr_in *in4; 142 struct sockaddr_in6 *in6; 143 int ret = 0, r; 144 145 if ((r = getaddrinfo(host, NULL, NULL, &ai)) != 0) { 146 error("BSM audit: getaddrinfo failed for %.100s: %.100s", host, 147 r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r)); 148 return -1; 149 } 150 151 switch (ai->ai_family) { 152 case AF_INET: 153 in4 = (struct sockaddr_in *)ai->ai_addr; 154 *type = AU_IPv4; 155 memcpy(addr, &in4->sin_addr, sizeof(struct in_addr)); 156 break; 157#ifdef AU_IPv6 158 case AF_INET6: 159 in6 = (struct sockaddr_in6 *)ai->ai_addr; 160 *type = AU_IPv6; 161 memcpy(addr, &in6->sin6_addr, sizeof(struct in6_addr)); 162 break; 163#endif 164 default: 165 error("BSM audit: unknown address family for %.100s: %d", 166 host, ai->ai_family); 167 ret = -1; 168 } 169 freeaddrinfo(ai); 170 return ret; 171} 172#endif 173 174#ifdef BROKEN_BSM_API 175/* 176 In Solaris 11 the audit daemon has been moved to SMF. In the process 177 they simply dropped getacna() from the API, since it read from a now 178 non-existent config file. This function re-implements getacna() to 179 read from the SMF repository instead. 180 */ 181int 182getacna(char *auditstring, int len) 183{ 184 scf_handle_t *handle = NULL; 185 scf_property_t *property = NULL; 186 scf_value_t *value = NULL; 187 int ret = 0; 188 189 handle = scf_handle_create(SCF_VERSION); 190 if (handle == NULL) 191 return -2; /* The man page for getacna on Solaris 10 states 192 we should return -2 in case of error and set 193 errno to indicate the error. We don't bother 194 with errno here, though, since the only use 195 of this function below doesn't check for errors 196 anyway. 197 */ 198 199 ret = scf_handle_bind(handle); 200 if (ret == -1) 201 return -2; 202 203 property = scf_property_create(handle); 204 if (property == NULL) 205 return -2; 206 207 ret = scf_handle_decode_fmri(handle, 208 "svc:/system/auditd:default/:properties/preselection/naflags", 209 NULL, NULL, NULL, NULL, property, 0); 210 if (ret == -1) 211 return -2; 212 213 value = scf_value_create(handle); 214 if (value == NULL) 215 return -2; 216 217 ret = scf_property_get_value(property, value); 218 if (ret == -1) 219 return -2; 220 221 ret = scf_value_get_astring(value, auditstring, len); 222 if (ret == -1) 223 return -2; 224 225 scf_value_destroy(value); 226 scf_property_destroy(property); 227 scf_handle_destroy(handle); 228 229 return 0; 230} 231#endif 232 233/* 234 * Check if the specified event is selected (enabled) for auditing. 235 * Returns 1 if the event is selected, 0 if not and -1 on failure. 236 */ 237static int 238selected(char *username, uid_t uid, au_event_t event, int sf) 239{ 240 int rc, sorf; 241 char naflags[512]; 242 struct au_mask mask; 243 244 mask.am_success = mask.am_failure = 0; 245 if (uid < 0) { 246 /* get flags for non-attributable (to a real user) events */ 247 rc = getacna(naflags, sizeof(naflags)); 248 if (rc == 0) 249 (void) getauditflagsbin(naflags, &mask); 250 } else 251 rc = au_user_mask(username, &mask); 252 253 sorf = (sf == 0) ? AU_PRS_SUCCESS : AU_PRS_FAILURE; 254 return(au_preselect(event, &mask, sorf, AU_PRS_REREAD)); 255} 256 257static void 258bsm_audit_record(int typ, char *string, au_event_t event_no) 259{ 260 int ad, rc, sel; 261 uid_t uid = -1; 262 gid_t gid = -1; 263 pid_t pid = getpid(); 264 AuditInfoTermID tid = ssh_bsm_tid; 265 266 if (the_authctxt == NULL) { 267 error("BSM audit: audit record internal error (NULL ctxt)"); 268 abort(); 269 } 270 271 if (the_authctxt->valid) { 272 uid = the_authctxt->pw->pw_uid; 273 gid = the_authctxt->pw->pw_gid; 274 } 275 276 rc = (typ == 0) ? 0 : -1; 277 sel = selected(the_authctxt->user, uid, event_no, rc); 278 debug3("BSM audit: typ %d rc %d \"%s\"", typ, rc, string); 279 if (!sel) 280 return; /* audit event does not match mask, do not write */ 281 282 debug3("BSM audit: writing audit new record"); 283 ad = au_open(); 284 285 (void) au_write(ad, AUToSubjectFunc(uid, uid, gid, uid, gid, 286 pid, pid, &tid)); 287 (void) au_write(ad, au_to_text(string)); 288 (void) au_write(ad, AUToReturnFunc(typ, rc)); 289 290#ifdef BROKEN_BSM_API 291 /* The last argument is the event modifier flags. For 292 some seemingly undocumented reason it was added in 293 Solaris 11. */ 294 rc = au_close(ad, AU_TO_WRITE, event_no, 0); 295#else 296 rc = au_close(ad, AU_TO_WRITE, event_no); 297#endif 298 299 if (rc < 0) 300 error("BSM audit: %s failed to write \"%s\" record: %s", 301 __func__, string, strerror(errno)); 302} 303 304static void 305bsm_audit_session_setup(void) 306{ 307 int rc; 308 struct AuditInfoStruct info; 309 au_mask_t mask; 310 311 if (the_authctxt == NULL) { 312 error("BSM audit: session setup internal error (NULL ctxt)"); 313 return; 314 } 315 316 bzero(&info, sizeof (info)); 317 318 if (the_authctxt->valid) 319 info.ai_auid = the_authctxt->pw->pw_uid; 320 else 321 info.ai_auid = -1; 322 info.ai_asid = getpid(); 323 mask.am_success = 0; 324 mask.am_failure = 0; 325 326 (void) au_user_mask(the_authctxt->user, &mask); 327 328 info.ai_mask.am_success = mask.am_success; 329 info.ai_mask.am_failure = mask.am_failure; 330 331 info.ai_termid = ssh_bsm_tid; 332 333 rc = SetAuditFunc(&info, sizeof(info)); 334 if (rc < 0) 335 error("BSM audit: %s: %s failed: %s", __func__, 336 SetAuditFuncText, strerror(errno)); 337} 338 339static void 340bsm_audit_bad_login(const char *what) 341{ 342 char textbuf[BSM_TEXTBUFSZ]; 343 344 if (the_authctxt->valid) { 345 (void) snprintf(textbuf, sizeof (textbuf), 346 gettext("invalid %s for user %s"), 347 what, the_authctxt->user); 348 bsm_audit_record(4, textbuf, AUE_openssh); 349 } else { 350 (void) snprintf(textbuf, sizeof (textbuf), 351 gettext("invalid user name \"%s\""), 352 the_authctxt->user); 353 bsm_audit_record(3, textbuf, AUE_openssh); 354 } 355} 356 357/* Below is the sshd audit API code */ 358 359void 360audit_connection_from(const char *host, int port) 361{ 362 AuditInfoTermID *tid = &ssh_bsm_tid; 363 char buf[1024]; 364 365 if (cannot_audit(0)) 366 return; 367 debug3("BSM audit: connection from %.100s port %d", host, port); 368 369 /* populate our terminal id structure */ 370#if defined(HAVE_GETAUDIT_ADDR) 371 tid->at_port = (dev_t)port; 372 aug_get_machine((char *)host, &(tid->at_addr[0]), &(tid->at_type)); 373 snprintf(buf, sizeof(buf), "%08x %08x %08x %08x", tid->at_addr[0], 374 tid->at_addr[1], tid->at_addr[2], tid->at_addr[3]); 375 debug3("BSM audit: iptype %d machine ID %s", (int)tid->at_type, buf); 376#else 377 /* this is used on IPv4-only machines */ 378 tid->port = (dev_t)port; 379 tid->machine = inet_addr(host); 380 snprintf(buf, sizeof(buf), "%08x", tid->machine); 381 debug3("BSM audit: machine ID %s", buf); 382#endif 383} 384 385void 386audit_run_command(const char *command) 387{ 388 /* not implemented */ 389} 390 391void 392audit_session_open(struct logininfo *li) 393{ 394 /* not implemented */ 395} 396 397void 398audit_session_close(struct logininfo *li) 399{ 400 /* not implemented */ 401} 402 403void 404audit_event(ssh_audit_event_t event) 405{ 406 char textbuf[BSM_TEXTBUFSZ]; 407 static int logged_in = 0; 408 const char *user = the_authctxt ? the_authctxt->user : "(unknown user)"; 409 410 if (cannot_audit(0)) 411 return; 412 413 switch(event) { 414 case SSH_AUTH_SUCCESS: 415 logged_in = 1; 416 bsm_audit_session_setup(); 417 snprintf(textbuf, sizeof(textbuf), 418 gettext("successful login %s"), user); 419 bsm_audit_record(0, textbuf, AUE_openssh); 420 break; 421 422 case SSH_CONNECTION_CLOSE: 423 /* 424 * We can also get a close event if the user attempted auth 425 * but never succeeded. 426 */ 427 if (logged_in) { 428 snprintf(textbuf, sizeof(textbuf), 429 gettext("sshd logout %s"), the_authctxt->user); 430 bsm_audit_record(0, textbuf, AUE_logout); 431 } else { 432 debug("%s: connection closed without authentication", 433 __func__); 434 } 435 break; 436 437 case SSH_NOLOGIN: 438 bsm_audit_record(1, 439 gettext("logins disabled by /etc/nologin"), AUE_openssh); 440 break; 441 442 case SSH_LOGIN_EXCEED_MAXTRIES: 443 snprintf(textbuf, sizeof(textbuf), 444 gettext("too many tries for user %s"), the_authctxt->user); 445 bsm_audit_record(1, textbuf, AUE_openssh); 446 break; 447 448 case SSH_LOGIN_ROOT_DENIED: 449 bsm_audit_record(2, gettext("not_console"), AUE_openssh); 450 break; 451 452 case SSH_AUTH_FAIL_PASSWD: 453 bsm_audit_bad_login("password"); 454 break; 455 456 case SSH_AUTH_FAIL_KBDINT: 457 bsm_audit_bad_login("interactive password entry"); 458 break; 459 460 default: 461 debug("%s: unhandled event %d", __func__, event); 462 } 463} 464#endif /* BSM */ 465