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