Home | History | Annotate | Download | only in openssh
      1 /* $Id: audit-bsm.c,v 1.7 2011/01/17 10:15:29 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 #include "ssh.h"
     49 #include "log.h"
     50 #include "key.h"
     51 #include "hostfile.h"
     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
     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
     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 
     80 #ifndef cannot_audit
     81 extern int	cannot_audit(int);
     82 #endif
     83 extern void	aug_init(void);
     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 
    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 
    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(struct logininfo *li)
    309 {
    310 	/* not implemented */
    311 }
    312 
    313 void
    314 audit_session_close(struct logininfo *li)
    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 */
    381