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