From 7b578f7d5bbef771b41f8a782f679444efd76c98 Mon Sep 17 00:00:00 2001 From: dtucker Date: Sun, 20 Feb 2005 10:01:48 +0000 Subject: [PATCH] - (dtucker) [LICENCE Makefile.in README.platform audit-bsm.c configure.ac defines.h] Bug #125: Add *EXPERIMENTAL* BSM audit support. Configure --with-audit=bsm to enable. Patch originally from Sun Microsystems, parts by John R. Jackson. ok djm@ --- ChangeLog | 6 + LICENCE | 1 + Makefile.in | 3 +- README.platform | 15 ++- audit-bsm.c | 329 ++++++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 37 ++++++ defines.h | 9 ++ 7 files changed, 396 insertions(+), 4 deletions(-) create mode 100644 audit-bsm.c diff --git a/ChangeLog b/ChangeLog index 2542682b..a3c32305 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +20050220 + - (dtucker) [LICENCE Makefile.in README.platform audit-bsm.c configure.ac + defines.h] Bug #125: Add *EXPERIMENTAL* BSM audit support. Configure + --with-audit=bsm to enable. Patch originally from Sun Microsystems, + parts by John R. Jackson. ok djm@ + 20050216 - (djm) write seed to temporary file and atomically rename into place; ok dtucker@ diff --git a/LICENCE b/LICENCE index f9062bd5..ae03eb3a 100644 --- a/LICENCE +++ b/LICENCE @@ -203,6 +203,7 @@ OpenSSH contains no GPL code. Wayne Schroeder William Jones Darren Tucker + Sun Microsystems * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/Makefile.in b/Makefile.in index b2e33336..734c0404 100644 --- a/Makefile.in +++ b/Makefile.in @@ -85,7 +85,8 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \ auth-krb5.o \ auth2-gss.o gss-serv.o gss-serv-krb5.o \ - loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o audit.o + loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ + audit.o audit-bsm.o MANPAGES = scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-rand-helper.8.out ssh-keysign.8.out sshd_config.5.out ssh_config.5.out MANPAGES_IN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-rand-helper.8 ssh-keysign.8 sshd_config.5 ssh_config.5 diff --git a/README.platform b/README.platform index ba555d08..47e4b946 100644 --- a/README.platform +++ b/README.platform @@ -32,8 +32,17 @@ openssl-devel, zlib, minres, minires-devel. Solaris ------- -Currently, sshd does not support BSM auditting. This can show up as errors -when editting cron entries via crontab. See. -http://bugzilla.mindrot.org/show_bug.cgi?id=125 +If you enable BSM auditing on Solaris, you need to update audit_event(4) +for praudit(1m) to give sensible output. The following line needs to be +added to /etc/security/audit_event: + + 32800:AUE_openssh:OpenSSH login:lo + +The BSM audit event range available for third party TCB applications is +32768 - 65535. Event number 32800 has been choosen for AUE_openssh. +There is no official registry of 3rd party event numbers, so if this +number is already in use on your system, you may change it at build time +by configure'ing --with-cflags=-DAUE_openssh=32801 then rebuilding. + $Id$ diff --git a/audit-bsm.c b/audit-bsm.c new file mode 100644 index 00000000..41a8ed04 --- /dev/null +++ b/audit-bsm.c @@ -0,0 +1,329 @@ +/* $Id$ */ + +/* + * TODO + * + * - deal with overlap between this and sys_auth_allowed_user + * sys_auth_record_login and record_failed_login. + */ + +/* + * Copyright 1988-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* #pragma ident "@(#)bsmaudit.c 1.1 01/09/17 SMI" */ + +#include "includes.h" +#if defined(USE_BSM_AUDIT) + +#include "ssh.h" +#include "log.h" +#include "auth.h" +#include "xmalloc.h" + +#ifndef AUE_openssh +# define AUE_openssh 32800 +#endif +#include +#include +#include +#include +#include + +#if defined(HAVE_GETAUDIT_ADDR) +#define AuditInfoStruct auditinfo_addr +#define AuditInfoTermID au_tid_addr_t +#define GetAuditFunc(a,b) getaudit_addr((a),(b)) +#define GetAuditFuncText "getaudit_addr" +#define SetAuditFunc(a,b) setaudit_addr((a),(b)) +#define SetAuditFuncText "setaudit_addr" +#define AUToSubjectFunc au_to_subject_ex +#define AUToReturnFunc(a,b) au_to_return32((a), (int32_t)(b)) +#else +#define AuditInfoStruct auditinfo +#define AuditInfoTermID au_tid_t +#define GetAuditFunc(a,b) getaudit(a) +#define GetAuditFuncText "getaudit" +#define SetAuditFunc(a,b) setaudit(a) +#define SetAuditFuncText "setaudit" +#define AUToSubjectFunc au_to_subject +#define AUToReturnFunc(a,b) au_to_return((a), (u_int)(b)) +#endif + +extern int cannot_audit(int); +extern void aug_init(void); +extern dev_t aug_get_port(void); +extern int aug_get_machine(char *, u_int32_t *, u_int32_t *); +extern void aug_save_auid(au_id_t); +extern void aug_save_uid(uid_t); +extern void aug_save_euid(uid_t); +extern void aug_save_gid(gid_t); +extern void aug_save_egid(gid_t); +extern void aug_save_pid(pid_t); +extern void aug_save_asid(au_asid_t); +extern void aug_save_tid(dev_t, unsigned int); +extern void aug_save_tid_ex(dev_t, u_int32_t *, u_int32_t); +extern int aug_save_me(void); +extern int aug_save_namask(void); +extern void aug_save_event(au_event_t); +extern void aug_save_sorf(int); +extern void aug_save_text(char *); +extern void aug_save_text1(char *); +extern void aug_save_text2(char *); +extern void aug_save_na(int); +extern void aug_save_user(char *); +extern void aug_save_path(char *); +extern int aug_save_policy(void); +extern void aug_save_afunc(int (*)(int)); +extern int aug_audit(void); +extern int aug_na_selected(void); +extern int aug_selected(void); +extern int aug_daemon_session(void); + +#ifndef HAVE_GETTEXT +# define gettext(a) (a) +#endif + +extern Authctxt *the_authctxt; +static AuditInfoTermID ssh_bsm_tid; + +/* Below is the low-level BSM interface code */ + +/* + * Check if the specified event is selected (enabled) for auditing. + * Returns 1 if the event is selected, 0 if not and -1 on failure. + */ +static int +selected(char *username, uid_t uid, au_event_t event, int sf) +{ + int rc, sorf; + char naflags[512]; + struct au_mask mask; + + mask.am_success = mask.am_failure = 0; + if (uid < 0) { + /* get flags for non-attributable (to a real user) events */ + rc = getacna(naflags, sizeof(naflags)); + if (rc == 0) + (void) getauditflagsbin(naflags, &mask); + } else + rc = au_user_mask(username, &mask); + + sorf = (sf == 0) ? AU_PRS_SUCCESS : AU_PRS_FAILURE; + return(au_preselect(event, &mask, sorf, AU_PRS_REREAD)); +} + +static void +bsm_audit_record(int typ, char *string, au_event_t event_no) +{ + int ad, rc, sel; + uid_t uid = -1; + gid_t gid = -1; + pid_t pid = getpid(); + AuditInfoTermID tid = ssh_bsm_tid; + + if (the_authctxt != NULL && the_authctxt->valid) { + uid = the_authctxt->pw->pw_uid; + gid = the_authctxt->pw->pw_gid; + } + + rc = (typ == 0) ? 0 : -1; + sel = selected(the_authctxt->user, uid, event_no, rc); + debug3("BSM audit: typ %d rc %d \"%s\"", typ, rc, string); + if (!sel) + return; /* audit event does not match mask, do not write */ + + debug3("BSM audit: writing audit new record"); + ad = au_open(); + + (void) au_write(ad, AUToSubjectFunc(uid, uid, gid, uid, gid, + pid, pid, &tid)); + (void) au_write(ad, au_to_text(string)); + (void) au_write(ad, AUToReturnFunc(typ, rc)); + + rc = au_close(ad, AU_TO_WRITE, event_no); + if (rc < 0) + error("BSM audit: %s failed to write \"%s\" record: %s", + __func__, string, strerror(errno)); +} + +static void +bsm_audit_session_setup(void) +{ + int rc; + struct AuditInfoStruct info; + au_mask_t mask; + + if (the_authctxt == NULL) { + error("BSM audit: session setup internal error (NULL ctxt)"); + return; + } + + if (the_authctxt->valid) + info.ai_auid = the_authctxt->pw->pw_uid; + else + info.ai_auid = -1; + info.ai_asid = getpid(); + mask.am_success = 0; + mask.am_failure = 0; + + (void) au_user_mask(the_authctxt->user, &mask); + + info.ai_mask.am_success = mask.am_success; + info.ai_mask.am_failure = mask.am_failure; + + info.ai_termid = ssh_bsm_tid; + + rc = SetAuditFunc(&info, sizeof(info)); + if (rc < 0) + error("BSM audit: %s: %s failed: %s", __func__, + SetAuditFuncText, strerror(errno)); +} + +static void +bsm_audit_bad_login(const char *what) +{ + char textbuf[BSM_TEXTBUFSZ]; + + if (the_authctxt->valid) { + (void) snprintf(textbuf, sizeof (textbuf), + gettext("invalid %s for user %s"), + what, the_authctxt->user); + bsm_audit_record(4, textbuf, AUE_openssh); + } else { + (void) snprintf(textbuf, sizeof (textbuf), + gettext("invalid user name \"%s\""), + the_authctxt->user); + bsm_audit_record(3, textbuf, AUE_openssh); + } +} + +/* Below is the sshd audit API code */ + +void +audit_connection_from(const char *host, int port) +{ + AuditInfoTermID *tid = &ssh_bsm_tid; + char buf[1024]; + + if (cannot_audit(0)) + return; + debug3("BSM audit: connection from %.100s port %d", host, port); + + /* populate our terminal id structure */ +#if defined(HAVE_GETAUDIT_ADDR) + tid->at_port = (dev_t)port; + aug_get_machine((char *)host, &(tid->at_addr[0]), &(tid->at_type)); + snprintf(buf, sizeof(buf), "%08x %08x %08x %08x", tid->at_addr[0], + tid->at_addr[1], tid->at_addr[2], tid->at_addr[3]); + debug3("BSM audit: iptype %d machine ID %s", (int)tid->at_type, buf); +#else + /* this is used on IPv4-only machines */ + tid->port = (dev_t)port; + tid->machine = inet_addr(host); + snprintf(buf, sizeof(buf), "%08x", tid->machine); + debug3("BSM audit: machine ID %s", buf); +#endif +} + +void +audit_run_command(const char *command) +{ + /* not implemented */ +} + +void +audit_session_open(const char *ttyn) +{ + /* not implemented */ +} + +void +audit_session_close(const char *ttyn) +{ + /* not implemented */ +} + +void +audit_event(ssh_audit_event_t event) +{ + char textbuf[BSM_TEXTBUFSZ]; + static int logged_in = 0; + const char *user = the_authctxt ? the_authctxt->user : "(unknown user)"; + + if (cannot_audit(0)) + return; + + switch(event) { + case SSH_AUTH_SUCCESS: + logged_in = 1; + bsm_audit_session_setup(); + snprintf(textbuf, sizeof(textbuf), + gettext("successful login %s"), user); + bsm_audit_record(0, textbuf, AUE_openssh); + break; + + case SSH_CONNECTION_CLOSE: + /* + * We can also get a close event if the user attempted auth + * but never succeeded. + */ + if (logged_in) { + snprintf(textbuf, sizeof(textbuf), + gettext("sshd logout %s"), the_authctxt->user); + bsm_audit_record(0, textbuf, AUE_logout); + } else { + debug("%s: connection closed without authentication", + __func__); + } + break; + + case SSH_NOLOGIN: + bsm_audit_record(1, + gettext("logins disabled by /etc/nologin"), AUE_openssh); + break; + + case SSH_LOGIN_EXCEED_MAXTRIES: + snprintf(textbuf, sizeof(textbuf), + gettext("too many tries for user %s"), the_authctxt->user); + bsm_audit_record(1, textbuf, AUE_openssh); + break; + + case SSH_LOGIN_ROOT_DENIED: + bsm_audit_record(2, gettext("not_console"), AUE_openssh); + break; + + case SSH_AUTH_FAIL_PASSWD: + bsm_audit_bad_login("password"); + break; + + case SSH_AUTH_FAIL_KBDINT: + bsm_audit_bad_login("interactive password entry"); + break; + + default: + debug("%s: unhandled event %d", __func__, event); + } +} +#endif /* BSM */ diff --git a/configure.ac b/configure.ac index 752b8055..6f94bbcc 100644 --- a/configure.ac +++ b/configure.ac @@ -881,6 +881,37 @@ AC_ARG_WITH(libedit, fi ] ) +AUDIT_MODULE=none +AC_ARG_WITH(audit, + [ --with-audit=module Enable EXPERIMENTAL audit support (modules=debug,bsm)], + [ + AC_MSG_CHECKING(for supported audit module) + case "$withval" in + bsm) + AC_MSG_RESULT(bsm) + AUDIT_MODULE=bsm + dnl Checks for headers, libs and functions + AC_CHECK_HEADERS(bsm/audit.h, [], + [AC_MSG_ERROR(BSM enabled and bsm/audit.h not found)]) + AC_CHECK_LIB(bsm, getaudit, [], + [AC_MSG_ERROR(BSM enabled and required library not found)]) + AC_CHECK_FUNCS(getaudit, [], + [AC_MSG_ERROR(BSM enabled and required function not found)]) + # These are optional + AC_CHECK_FUNCS(getaudit_addr gettext) + AC_DEFINE(USE_BSM_AUDIT, [], [Use BSM audit module]) + ;; + debug) + AUDIT_MODULE=debug + AC_MSG_RESULT(debug) + AC_DEFINE(SSH_AUDIT_EVENTS, [], Use audit debugging module) + ;; + *) + AC_MSG_ERROR([Unknown audit module $withval]) + ;; + esac ] +) + dnl Checks for library functions. Please keep in alphabetical order AC_CHECK_FUNCS(\ arc4random __b64_ntop b64_ntop __b64_pton b64_pton bcopy \ @@ -1846,6 +1877,8 @@ TYPE_SOCKLEN_T AC_CHECK_TYPES(sig_atomic_t,,,[#include ]) +AC_CHECK_TYPES(in_addr_t,,,[#include ]) + AC_CACHE_CHECK([for size_t], ac_cv_have_size_t, [ AC_TRY_COMPILE( [ @@ -3195,3 +3228,7 @@ if test ! -z "$NO_PEERCHECK" ; then echo "" fi +if test "$AUDIT_MODULE" = "bsm" ; then + echo "WARNING: BSM audit support is currently considered EXPERIMENTAL." + echo "See the Solaris section in README.platform for details." +fi diff --git a/defines.h b/defines.h index d83be080..b0891a33 100644 --- a/defines.h +++ b/defines.h @@ -288,6 +288,10 @@ struct sockaddr_un { }; #endif /* HAVE_SYS_UN_H */ +#ifndef HAVE_IN_ADDR_T +typedef u_int32_t in_addr_t; +#endif + #if defined(BROKEN_SYS_TERMIO_H) && !defined(_STRUCT_WINSIZE) #define _STRUCT_WINSIZE struct winsize { @@ -530,6 +534,11 @@ struct winsize { # define getpgrp() getpgrp(0) #endif +#ifdef USE_BSM_AUDIT +# define SSH_AUDIT_EVENTS +# define CUSTOM_SSH_AUDIT_EVENTS +#endif + /* OPENSSL_free() is Free() in versions before OpenSSL 0.9.6 */ #if !defined(OPENSSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x0090600f) # define OPENSSL_free(x) Free(x) -- 2.45.2