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