1240075Sdes/* $Id: audit-bsm.c,v 1.8 2012/02/23 23:40:43 dtucker Exp $ */ 2146998Sdes 3146998Sdes/* 4146998Sdes * TODO 5146998Sdes * 6146998Sdes * - deal with overlap between this and sys_auth_allowed_user 7146998Sdes * sys_auth_record_login and record_failed_login. 8146998Sdes */ 9146998Sdes 10146998Sdes/* 11146998Sdes * Copyright 1988-2002 Sun Microsystems, Inc. All rights reserved. 12146998Sdes * Use is subject to license terms. 13146998Sdes * 14146998Sdes * Redistribution and use in source and binary forms, with or without 15146998Sdes * modification, are permitted provided that the following conditions 16146998Sdes * are met: 17146998Sdes * 1. Redistributions of source code must retain the above copyright 18146998Sdes * notice, this list of conditions and the following disclaimer. 19146998Sdes * 2. Redistributions in binary form must reproduce the above copyright 20146998Sdes * notice, this list of conditions and the following disclaimer in the 21146998Sdes * documentation and/or other materials provided with the distribution. 22146998Sdes * 23146998Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 24146998Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25146998Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26146998Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 27146998Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 28146998Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29146998Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30146998Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31146998Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32146998Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33146998Sdes * 34146998Sdes */ 35146998Sdes/* #pragma ident "@(#)bsmaudit.c 1.1 01/09/17 SMI" */ 36146998Sdes 37146998Sdes#include "includes.h" 38146998Sdes#if defined(USE_BSM_AUDIT) 39146998Sdes 40162852Sdes#include <sys/types.h> 41162852Sdes 42162859Sdes#include <errno.h> 43181111Sdes#include <netdb.h> 44162852Sdes#include <stdarg.h> 45181111Sdes#include <string.h> 46162852Sdes#include <unistd.h> 47162852Sdes 48240075Sdes#ifdef BROKEN_BSM_API 49240075Sdes#include <libscf.h> 50240075Sdes#endif 51240075Sdes 52146998Sdes#include "ssh.h" 53146998Sdes#include "log.h" 54162852Sdes#include "key.h" 55162852Sdes#include "hostfile.h" 56146998Sdes#include "auth.h" 57146998Sdes#include "xmalloc.h" 58146998Sdes 59146998Sdes#ifndef AUE_openssh 60146998Sdes# define AUE_openssh 32800 61146998Sdes#endif 62146998Sdes#include <bsm/audit.h> 63146998Sdes#include <bsm/libbsm.h> 64146998Sdes#include <bsm/audit_uevents.h> 65146998Sdes#include <bsm/audit_record.h> 66146998Sdes#include <locale.h> 67146998Sdes 68146998Sdes#if defined(HAVE_GETAUDIT_ADDR) 69146998Sdes#define AuditInfoStruct auditinfo_addr 70146998Sdes#define AuditInfoTermID au_tid_addr_t 71146998Sdes#define SetAuditFunc(a,b) setaudit_addr((a),(b)) 72146998Sdes#define SetAuditFuncText "setaudit_addr" 73146998Sdes#define AUToSubjectFunc au_to_subject_ex 74146998Sdes#define AUToReturnFunc(a,b) au_to_return32((a), (int32_t)(b)) 75146998Sdes#else 76146998Sdes#define AuditInfoStruct auditinfo 77146998Sdes#define AuditInfoTermID au_tid_t 78146998Sdes#define SetAuditFunc(a,b) setaudit(a) 79146998Sdes#define SetAuditFuncText "setaudit" 80146998Sdes#define AUToSubjectFunc au_to_subject 81146998Sdes#define AUToReturnFunc(a,b) au_to_return((a), (u_int)(b)) 82146998Sdes#endif 83146998Sdes 84181111Sdes#ifndef cannot_audit 85146998Sdesextern int cannot_audit(int); 86181111Sdes#endif 87146998Sdesextern void aug_init(void); 88146998Sdesextern void aug_save_auid(au_id_t); 89146998Sdesextern void aug_save_uid(uid_t); 90146998Sdesextern void aug_save_euid(uid_t); 91146998Sdesextern void aug_save_gid(gid_t); 92146998Sdesextern void aug_save_egid(gid_t); 93146998Sdesextern void aug_save_pid(pid_t); 94146998Sdesextern void aug_save_asid(au_asid_t); 95146998Sdesextern void aug_save_tid(dev_t, unsigned int); 96146998Sdesextern void aug_save_tid_ex(dev_t, u_int32_t *, u_int32_t); 97146998Sdesextern int aug_save_me(void); 98146998Sdesextern int aug_save_namask(void); 99146998Sdesextern void aug_save_event(au_event_t); 100146998Sdesextern void aug_save_sorf(int); 101146998Sdesextern void aug_save_text(char *); 102146998Sdesextern void aug_save_text1(char *); 103146998Sdesextern void aug_save_text2(char *); 104146998Sdesextern void aug_save_na(int); 105146998Sdesextern void aug_save_user(char *); 106146998Sdesextern void aug_save_path(char *); 107146998Sdesextern int aug_save_policy(void); 108146998Sdesextern void aug_save_afunc(int (*)(int)); 109146998Sdesextern int aug_audit(void); 110146998Sdesextern int aug_na_selected(void); 111146998Sdesextern int aug_selected(void); 112146998Sdesextern int aug_daemon_session(void); 113146998Sdes 114146998Sdes#ifndef HAVE_GETTEXT 115146998Sdes# define gettext(a) (a) 116146998Sdes#endif 117146998Sdes 118146998Sdesextern Authctxt *the_authctxt; 119146998Sdesstatic AuditInfoTermID ssh_bsm_tid; 120146998Sdes 121240075Sdes#ifdef BROKEN_BSM_API 122240075Sdes/* For some reason this constant is no longer defined 123240075Sdes in Solaris 11. */ 124240075Sdes#define BSM_TEXTBUFSZ 256 125240075Sdes#endif 126240075Sdes 127146998Sdes/* Below is the low-level BSM interface code */ 128146998Sdes 129146998Sdes/* 130181111Sdes * aug_get_machine is only required on IPv6 capable machines, we use a 131181111Sdes * different mechanism in audit_connection_from() for IPv4-only machines. 132181111Sdes * getaudit_addr() is only present on IPv6 capable machines. 133181111Sdes */ 134181111Sdes#if defined(HAVE_AUG_GET_MACHINE) || !defined(HAVE_GETAUDIT_ADDR) 135181111Sdesextern int aug_get_machine(char *, u_int32_t *, u_int32_t *); 136181111Sdes#else 137181111Sdesstatic int 138181111Sdesaug_get_machine(char *host, u_int32_t *addr, u_int32_t *type) 139181111Sdes{ 140181111Sdes struct addrinfo *ai; 141181111Sdes struct sockaddr_in *in4; 142181111Sdes struct sockaddr_in6 *in6; 143181111Sdes int ret = 0, r; 144181111Sdes 145181111Sdes if ((r = getaddrinfo(host, NULL, NULL, &ai)) != 0) { 146181111Sdes error("BSM audit: getaddrinfo failed for %.100s: %.100s", host, 147181111Sdes r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r)); 148181111Sdes return -1; 149181111Sdes } 150181111Sdes 151181111Sdes switch (ai->ai_family) { 152181111Sdes case AF_INET: 153181111Sdes in4 = (struct sockaddr_in *)ai->ai_addr; 154181111Sdes *type = AU_IPv4; 155181111Sdes memcpy(addr, &in4->sin_addr, sizeof(struct in_addr)); 156181111Sdes break; 157181111Sdes#ifdef AU_IPv6 158181111Sdes case AF_INET6: 159181111Sdes in6 = (struct sockaddr_in6 *)ai->ai_addr; 160181111Sdes *type = AU_IPv6; 161181111Sdes memcpy(addr, &in6->sin6_addr, sizeof(struct in6_addr)); 162181111Sdes break; 163181111Sdes#endif 164181111Sdes default: 165181111Sdes error("BSM audit: unknown address family for %.100s: %d", 166181111Sdes host, ai->ai_family); 167181111Sdes ret = -1; 168181111Sdes } 169181111Sdes freeaddrinfo(ai); 170181111Sdes return ret; 171181111Sdes} 172181111Sdes#endif 173181111Sdes 174240075Sdes#ifdef BROKEN_BSM_API 175181111Sdes/* 176240075Sdes In Solaris 11 the audit daemon has been moved to SMF. In the process 177240075Sdes they simply dropped getacna() from the API, since it read from a now 178240075Sdes non-existent config file. This function re-implements getacna() to 179240075Sdes read from the SMF repository instead. 180240075Sdes */ 181240075Sdesint 182240075Sdesgetacna(char *auditstring, int len) 183240075Sdes{ 184240075Sdes scf_handle_t *handle = NULL; 185240075Sdes scf_property_t *property = NULL; 186240075Sdes scf_value_t *value = NULL; 187240075Sdes int ret = 0; 188240075Sdes 189240075Sdes handle = scf_handle_create(SCF_VERSION); 190240075Sdes if (handle == NULL) 191240075Sdes return -2; /* The man page for getacna on Solaris 10 states 192240075Sdes we should return -2 in case of error and set 193240075Sdes errno to indicate the error. We don't bother 194240075Sdes with errno here, though, since the only use 195240075Sdes of this function below doesn't check for errors 196240075Sdes anyway. 197240075Sdes */ 198240075Sdes 199240075Sdes ret = scf_handle_bind(handle); 200240075Sdes if (ret == -1) 201240075Sdes return -2; 202240075Sdes 203240075Sdes property = scf_property_create(handle); 204240075Sdes if (property == NULL) 205240075Sdes return -2; 206240075Sdes 207240075Sdes ret = scf_handle_decode_fmri(handle, 208240075Sdes "svc:/system/auditd:default/:properties/preselection/naflags", 209240075Sdes NULL, NULL, NULL, NULL, property, 0); 210240075Sdes if (ret == -1) 211240075Sdes return -2; 212240075Sdes 213240075Sdes value = scf_value_create(handle); 214240075Sdes if (value == NULL) 215240075Sdes return -2; 216240075Sdes 217240075Sdes ret = scf_property_get_value(property, value); 218240075Sdes if (ret == -1) 219240075Sdes return -2; 220240075Sdes 221240075Sdes ret = scf_value_get_astring(value, auditstring, len); 222240075Sdes if (ret == -1) 223240075Sdes return -2; 224240075Sdes 225240075Sdes scf_value_destroy(value); 226240075Sdes scf_property_destroy(property); 227240075Sdes scf_handle_destroy(handle); 228240075Sdes 229240075Sdes return 0; 230240075Sdes} 231240075Sdes#endif 232240075Sdes 233240075Sdes/* 234146998Sdes * Check if the specified event is selected (enabled) for auditing. 235146998Sdes * Returns 1 if the event is selected, 0 if not and -1 on failure. 236146998Sdes */ 237146998Sdesstatic int 238146998Sdesselected(char *username, uid_t uid, au_event_t event, int sf) 239146998Sdes{ 240146998Sdes int rc, sorf; 241146998Sdes char naflags[512]; 242146998Sdes struct au_mask mask; 243146998Sdes 244146998Sdes mask.am_success = mask.am_failure = 0; 245146998Sdes if (uid < 0) { 246146998Sdes /* get flags for non-attributable (to a real user) events */ 247146998Sdes rc = getacna(naflags, sizeof(naflags)); 248146998Sdes if (rc == 0) 249146998Sdes (void) getauditflagsbin(naflags, &mask); 250146998Sdes } else 251146998Sdes rc = au_user_mask(username, &mask); 252146998Sdes 253146998Sdes sorf = (sf == 0) ? AU_PRS_SUCCESS : AU_PRS_FAILURE; 254146998Sdes return(au_preselect(event, &mask, sorf, AU_PRS_REREAD)); 255146998Sdes} 256146998Sdes 257146998Sdesstatic void 258146998Sdesbsm_audit_record(int typ, char *string, au_event_t event_no) 259146998Sdes{ 260146998Sdes int ad, rc, sel; 261146998Sdes uid_t uid = -1; 262146998Sdes gid_t gid = -1; 263146998Sdes pid_t pid = getpid(); 264146998Sdes AuditInfoTermID tid = ssh_bsm_tid; 265146998Sdes 266146998Sdes if (the_authctxt != NULL && the_authctxt->valid) { 267146998Sdes uid = the_authctxt->pw->pw_uid; 268146998Sdes gid = the_authctxt->pw->pw_gid; 269146998Sdes } 270146998Sdes 271146998Sdes rc = (typ == 0) ? 0 : -1; 272146998Sdes sel = selected(the_authctxt->user, uid, event_no, rc); 273146998Sdes debug3("BSM audit: typ %d rc %d \"%s\"", typ, rc, string); 274146998Sdes if (!sel) 275146998Sdes return; /* audit event does not match mask, do not write */ 276146998Sdes 277146998Sdes debug3("BSM audit: writing audit new record"); 278146998Sdes ad = au_open(); 279146998Sdes 280146998Sdes (void) au_write(ad, AUToSubjectFunc(uid, uid, gid, uid, gid, 281146998Sdes pid, pid, &tid)); 282146998Sdes (void) au_write(ad, au_to_text(string)); 283146998Sdes (void) au_write(ad, AUToReturnFunc(typ, rc)); 284146998Sdes 285240075Sdes#ifdef BROKEN_BSM_API 286240075Sdes /* The last argument is the event modifier flags. For 287240075Sdes some seemingly undocumented reason it was added in 288240075Sdes Solaris 11. */ 289240075Sdes rc = au_close(ad, AU_TO_WRITE, event_no, 0); 290240075Sdes#else 291146998Sdes rc = au_close(ad, AU_TO_WRITE, event_no); 292240075Sdes#endif 293240075Sdes 294146998Sdes if (rc < 0) 295146998Sdes error("BSM audit: %s failed to write \"%s\" record: %s", 296146998Sdes __func__, string, strerror(errno)); 297146998Sdes} 298146998Sdes 299146998Sdesstatic void 300146998Sdesbsm_audit_session_setup(void) 301146998Sdes{ 302146998Sdes int rc; 303146998Sdes struct AuditInfoStruct info; 304146998Sdes au_mask_t mask; 305146998Sdes 306146998Sdes if (the_authctxt == NULL) { 307146998Sdes error("BSM audit: session setup internal error (NULL ctxt)"); 308146998Sdes return; 309146998Sdes } 310146998Sdes 311146998Sdes if (the_authctxt->valid) 312146998Sdes info.ai_auid = the_authctxt->pw->pw_uid; 313146998Sdes else 314146998Sdes info.ai_auid = -1; 315146998Sdes info.ai_asid = getpid(); 316146998Sdes mask.am_success = 0; 317146998Sdes mask.am_failure = 0; 318146998Sdes 319146998Sdes (void) au_user_mask(the_authctxt->user, &mask); 320146998Sdes 321146998Sdes info.ai_mask.am_success = mask.am_success; 322146998Sdes info.ai_mask.am_failure = mask.am_failure; 323146998Sdes 324146998Sdes info.ai_termid = ssh_bsm_tid; 325146998Sdes 326146998Sdes rc = SetAuditFunc(&info, sizeof(info)); 327146998Sdes if (rc < 0) 328146998Sdes error("BSM audit: %s: %s failed: %s", __func__, 329146998Sdes SetAuditFuncText, strerror(errno)); 330146998Sdes} 331146998Sdes 332146998Sdesstatic void 333146998Sdesbsm_audit_bad_login(const char *what) 334146998Sdes{ 335146998Sdes char textbuf[BSM_TEXTBUFSZ]; 336146998Sdes 337146998Sdes if (the_authctxt->valid) { 338146998Sdes (void) snprintf(textbuf, sizeof (textbuf), 339146998Sdes gettext("invalid %s for user %s"), 340146998Sdes what, the_authctxt->user); 341146998Sdes bsm_audit_record(4, textbuf, AUE_openssh); 342146998Sdes } else { 343146998Sdes (void) snprintf(textbuf, sizeof (textbuf), 344146998Sdes gettext("invalid user name \"%s\""), 345146998Sdes the_authctxt->user); 346146998Sdes bsm_audit_record(3, textbuf, AUE_openssh); 347146998Sdes } 348146998Sdes} 349146998Sdes 350146998Sdes/* Below is the sshd audit API code */ 351146998Sdes 352146998Sdesvoid 353146998Sdesaudit_connection_from(const char *host, int port) 354146998Sdes{ 355146998Sdes AuditInfoTermID *tid = &ssh_bsm_tid; 356146998Sdes char buf[1024]; 357146998Sdes 358146998Sdes if (cannot_audit(0)) 359146998Sdes return; 360146998Sdes debug3("BSM audit: connection from %.100s port %d", host, port); 361146998Sdes 362146998Sdes /* populate our terminal id structure */ 363146998Sdes#if defined(HAVE_GETAUDIT_ADDR) 364146998Sdes tid->at_port = (dev_t)port; 365146998Sdes aug_get_machine((char *)host, &(tid->at_addr[0]), &(tid->at_type)); 366146998Sdes snprintf(buf, sizeof(buf), "%08x %08x %08x %08x", tid->at_addr[0], 367146998Sdes tid->at_addr[1], tid->at_addr[2], tid->at_addr[3]); 368146998Sdes debug3("BSM audit: iptype %d machine ID %s", (int)tid->at_type, buf); 369146998Sdes#else 370146998Sdes /* this is used on IPv4-only machines */ 371146998Sdes tid->port = (dev_t)port; 372146998Sdes tid->machine = inet_addr(host); 373146998Sdes snprintf(buf, sizeof(buf), "%08x", tid->machine); 374146998Sdes debug3("BSM audit: machine ID %s", buf); 375146998Sdes#endif 376146998Sdes} 377146998Sdes 378146998Sdesvoid 379146998Sdesaudit_run_command(const char *command) 380146998Sdes{ 381146998Sdes /* not implemented */ 382146998Sdes} 383146998Sdes 384146998Sdesvoid 385221420Sdesaudit_session_open(struct logininfo *li) 386146998Sdes{ 387146998Sdes /* not implemented */ 388146998Sdes} 389146998Sdes 390146998Sdesvoid 391221420Sdesaudit_session_close(struct logininfo *li) 392146998Sdes{ 393146998Sdes /* not implemented */ 394146998Sdes} 395146998Sdes 396146998Sdesvoid 397146998Sdesaudit_event(ssh_audit_event_t event) 398146998Sdes{ 399146998Sdes char textbuf[BSM_TEXTBUFSZ]; 400146998Sdes static int logged_in = 0; 401146998Sdes const char *user = the_authctxt ? the_authctxt->user : "(unknown user)"; 402146998Sdes 403146998Sdes if (cannot_audit(0)) 404146998Sdes return; 405146998Sdes 406146998Sdes switch(event) { 407146998Sdes case SSH_AUTH_SUCCESS: 408146998Sdes logged_in = 1; 409146998Sdes bsm_audit_session_setup(); 410146998Sdes snprintf(textbuf, sizeof(textbuf), 411146998Sdes gettext("successful login %s"), user); 412146998Sdes bsm_audit_record(0, textbuf, AUE_openssh); 413146998Sdes break; 414146998Sdes 415146998Sdes case SSH_CONNECTION_CLOSE: 416146998Sdes /* 417146998Sdes * We can also get a close event if the user attempted auth 418146998Sdes * but never succeeded. 419146998Sdes */ 420146998Sdes if (logged_in) { 421146998Sdes snprintf(textbuf, sizeof(textbuf), 422146998Sdes gettext("sshd logout %s"), the_authctxt->user); 423146998Sdes bsm_audit_record(0, textbuf, AUE_logout); 424146998Sdes } else { 425146998Sdes debug("%s: connection closed without authentication", 426146998Sdes __func__); 427146998Sdes } 428146998Sdes break; 429146998Sdes 430146998Sdes case SSH_NOLOGIN: 431146998Sdes bsm_audit_record(1, 432146998Sdes gettext("logins disabled by /etc/nologin"), AUE_openssh); 433146998Sdes break; 434146998Sdes 435146998Sdes case SSH_LOGIN_EXCEED_MAXTRIES: 436146998Sdes snprintf(textbuf, sizeof(textbuf), 437146998Sdes gettext("too many tries for user %s"), the_authctxt->user); 438146998Sdes bsm_audit_record(1, textbuf, AUE_openssh); 439146998Sdes break; 440146998Sdes 441146998Sdes case SSH_LOGIN_ROOT_DENIED: 442146998Sdes bsm_audit_record(2, gettext("not_console"), AUE_openssh); 443146998Sdes break; 444146998Sdes 445146998Sdes case SSH_AUTH_FAIL_PASSWD: 446146998Sdes bsm_audit_bad_login("password"); 447146998Sdes break; 448146998Sdes 449146998Sdes case SSH_AUTH_FAIL_KBDINT: 450146998Sdes bsm_audit_bad_login("interactive password entry"); 451146998Sdes break; 452146998Sdes 453146998Sdes default: 454146998Sdes debug("%s: unhandled event %d", __func__, event); 455146998Sdes } 456146998Sdes} 457146998Sdes#endif /* BSM */ 458