1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2017 Nexenta Systems, Inc. All rights reserved. 24 */ 25 26 #include <sys/types.h> 27 #include <errno.h> 28 #include <synch.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 #include <string.h> 33 #include <strings.h> 34 #include <syslog.h> 35 #include <fcntl.h> 36 #include <bsm/adt.h> 37 #include <bsm/adt_event.h> 38 #include <bsm/audit_uevents.h> 39 #include <pwd.h> 40 #include <nss_dbdefs.h> 41 #include <sys/idmap.h> 42 #include "smbd.h" 43 44 45 /* 46 * An audit session is established at user logon and terminated at user 47 * logoff. 48 * 49 * SMB audit handles are allocated when users logon (SmbSessionSetupX) 50 * and deallocted when a user logs off (SmbLogoffX). Each time an SMB 51 * audit handle is allocated it is added to a global list. 52 */ 53 typedef struct smb_audit { 54 struct smb_audit *sa_next; 55 adt_session_data_t *sa_handle; 56 uid_t sa_uid; 57 gid_t sa_gid; 58 uint32_t sa_audit_sid; 59 uint32_t sa_refcnt; 60 char *sa_domain; 61 char *sa_username; 62 } smb_audit_t; 63 64 static smb_audit_t *smbd_audit_list; 65 static mutex_t smbd_audit_lock; 66 67 /* 68 * Unique identifier for audit sessions in the audit list. 69 * Used to lookup an audit session on logoff. 70 */ 71 static uint32_t smbd_audit_sid; 72 73 static void smbd_audit_link(smb_audit_t *); 74 static smb_audit_t *smbd_audit_unlink(uint32_t); 75 76 77 /* 78 * Invoked at user logon due to SmbSessionSetupX. Authenticate the 79 * user, start an audit session and audit the event. 80 * 81 * On error, returns NULL, and status in user_info->lg_status 82 */ 83 smb_token_t * 84 smbd_user_auth_logon(smb_logon_t *user_info) 85 { 86 smb_token_t *token = NULL; 87 smb_audit_t *entry; 88 adt_session_data_t *ah = NULL; 89 adt_event_data_t *event; 90 smb_logon_t tmp_user; 91 au_tid_addr_t termid; 92 char sidbuf[SMB_SID_STRSZ]; 93 char *username; 94 char *domain; 95 uid_t uid; 96 gid_t gid; 97 char *sid; 98 int status; 99 int retval; 100 char *p; 101 char *buf = NULL; 102 103 if (user_info->lg_username == NULL || 104 user_info->lg_domain == NULL || 105 user_info->lg_workstation == NULL) { 106 user_info->lg_status = NT_STATUS_INVALID_PARAMETER; 107 return (NULL); 108 } 109 110 /* 111 * Avoid modifying the caller-provided struct because it 112 * may or may not point to allocated strings etc. 113 * Copy to tmp_user, auth, then copy the (out) lg_status 114 * member back to the caller-provided struct. 115 */ 116 tmp_user = *user_info; 117 if (tmp_user.lg_username[0] == '\0') { 118 tmp_user.lg_flags |= SMB_ATF_ANON; 119 tmp_user.lg_e_username = "anonymous"; 120 } else { 121 tmp_user.lg_e_username = tmp_user.lg_username; 122 } 123 124 /* Handle user@domain format. */ 125 if (tmp_user.lg_domain[0] == '\0' && 126 (p = strchr(tmp_user.lg_e_username, '@')) != NULL) { 127 buf = strdup(tmp_user.lg_e_username); 128 if (buf == NULL) 129 goto errout; 130 p = buf + (p - tmp_user.lg_e_username); 131 *p = '\0'; 132 tmp_user.lg_e_domain = p + 1; 133 tmp_user.lg_e_username = buf; 134 } else { 135 tmp_user.lg_e_domain = tmp_user.lg_domain; 136 } 137 138 token = smb_logon(&tmp_user); 139 user_info->lg_status = tmp_user.lg_status; 140 141 if (token == NULL) { 142 if (user_info->lg_status == 0) /* should not happen */ 143 user_info->lg_status = NT_STATUS_INTERNAL_ERROR; 144 uid = ADT_NO_ATTRIB; 145 gid = ADT_NO_ATTRIB; 146 sid = NT_NULL_SIDSTR; 147 username = tmp_user.lg_e_username; 148 domain = tmp_user.lg_e_domain; 149 status = ADT_FAILURE; 150 retval = ADT_FAIL_VALUE_AUTH; 151 } else { 152 uid = token->tkn_user.i_id; 153 gid = token->tkn_primary_grp.i_id; 154 smb_sid_tostr(token->tkn_user.i_sid, sidbuf); 155 sid = sidbuf; 156 username = token->tkn_account_name; 157 domain = token->tkn_domain_name; 158 status = ADT_SUCCESS; 159 retval = ADT_SUCCESS; 160 } 161 162 if (adt_start_session(&ah, NULL, 0)) { 163 syslog(LOG_AUTH | LOG_ALERT, "adt_start_session: %m"); 164 user_info->lg_status = NT_STATUS_AUDIT_FAILED; 165 goto errout; 166 } 167 168 if ((event = adt_alloc_event(ah, ADT_smbd_session)) == NULL) { 169 syslog(LOG_AUTH | LOG_ALERT, 170 "adt_alloc_event(ADT_smbd_session): %m"); 171 user_info->lg_status = NT_STATUS_AUDIT_FAILED; 172 goto errout; 173 } 174 175 (void) memset(&termid, 0, sizeof (au_tid_addr_t)); 176 termid.at_port = user_info->lg_local_port; 177 178 if (user_info->lg_clnt_ipaddr.a_family == AF_INET) { 179 termid.at_addr[0] = user_info->lg_clnt_ipaddr.a_ipv4; 180 termid.at_type = AU_IPv4; 181 } else { 182 bcopy(&user_info->lg_clnt_ipaddr.a_ip, termid.at_addr, 183 sizeof (in6_addr_t)); 184 termid.at_type = AU_IPv6; 185 } 186 adt_set_termid(ah, &termid); 187 188 if (adt_set_user(ah, uid, gid, uid, gid, NULL, ADT_NEW)) { 189 syslog(LOG_AUTH | LOG_ALERT, "adt_set_user: %m"); 190 adt_free_event(event); 191 user_info->lg_status = NT_STATUS_AUDIT_FAILED; 192 goto errout; 193 } 194 195 event->adt_smbd_session.domain = domain; 196 event->adt_smbd_session.username = username; 197 event->adt_smbd_session.sid = sid; 198 199 if (adt_put_event(event, status, retval)) 200 syslog(LOG_AUTH | LOG_ALERT, "adt_put_event: %m"); 201 202 adt_free_event(event); 203 204 if (token) { 205 if ((entry = malloc(sizeof (smb_audit_t))) == NULL) { 206 syslog(LOG_ERR, "smbd_user_auth_logon: %m"); 207 user_info->lg_status = 208 NT_STATUS_INSUFFICIENT_RESOURCES; 209 goto errout; 210 } 211 212 entry->sa_handle = ah; 213 entry->sa_uid = uid; 214 entry->sa_gid = gid; 215 entry->sa_username = strdup(username); 216 entry->sa_domain = strdup(domain); 217 218 smb_autohome_add(token); 219 smbd_audit_link(entry); 220 token->tkn_audit_sid = entry->sa_audit_sid; 221 222 user_info->lg_status = NT_STATUS_SUCCESS; 223 } 224 225 free(buf); 226 227 return (token); 228 229 errout: 230 free(buf); 231 (void) adt_end_session(ah); 232 smb_token_destroy(token); 233 return (NULL); 234 } 235 236 /* 237 * Logon due to a subsequent SmbSessionSetupX on an existing session. 238 * The user was authenticated during the initial session setup. 239 */ 240 void 241 smbd_user_nonauth_logon(uint32_t audit_sid) 242 { 243 smb_audit_t *entry; 244 245 (void) mutex_lock(&smbd_audit_lock); 246 entry = smbd_audit_list; 247 248 while (entry) { 249 if (entry->sa_audit_sid == audit_sid) { 250 ++entry->sa_refcnt; 251 break; 252 } 253 254 entry = entry->sa_next; 255 } 256 257 (void) mutex_unlock(&smbd_audit_lock); 258 } 259 260 /* 261 * Invoked at user logoff due to SmbLogoffX. If this is the final 262 * logoff for this user on the session, audit the event and terminate 263 * the audit session. 264 */ 265 void 266 smbd_user_auth_logoff(uint32_t audit_sid) 267 { 268 smb_audit_t *entry; 269 adt_session_data_t *ah; 270 adt_event_data_t *event; 271 struct passwd pw; 272 char buf[NSS_LINELEN_PASSWD]; 273 274 if ((entry = smbd_audit_unlink(audit_sid)) == NULL) 275 return; 276 277 if (IDMAP_ID_IS_EPHEMERAL(entry->sa_uid)) { 278 smb_autohome_remove(entry->sa_username); 279 } else { 280 if (getpwuid_r(entry->sa_uid, &pw, buf, sizeof (buf)) == NULL) 281 return; 282 283 smb_autohome_remove(pw.pw_name); 284 } 285 286 ah = entry->sa_handle; 287 288 if ((event = adt_alloc_event(ah, ADT_smbd_logoff)) == NULL) { 289 syslog(LOG_AUTH | LOG_ALERT, 290 "adt_alloc_event(ADT_smbd_logoff): %m"); 291 } else { 292 event->adt_smbd_logoff.domain = entry->sa_domain; 293 event->adt_smbd_logoff.username = entry->sa_username; 294 295 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS)) 296 syslog(LOG_AUTH | LOG_ALERT, "adt_put_event: %m"); 297 298 adt_free_event(event); 299 } 300 301 (void) adt_end_session(ah); 302 303 free(entry->sa_username); 304 free(entry->sa_domain); 305 free(entry); 306 } 307 308 /* 309 * Allocate an id and link an audit handle onto the global list. 310 */ 311 static void 312 smbd_audit_link(smb_audit_t *entry) 313 { 314 (void) mutex_lock(&smbd_audit_lock); 315 316 do { 317 ++smbd_audit_sid; 318 } while ((smbd_audit_sid == 0) || (smbd_audit_sid == (uint32_t)-1)); 319 320 entry->sa_audit_sid = smbd_audit_sid; 321 entry->sa_refcnt = 1; 322 entry->sa_next = smbd_audit_list; 323 smbd_audit_list = entry; 324 325 (void) mutex_unlock(&smbd_audit_lock); 326 } 327 328 /* 329 * Unlink an audit handle. If the reference count reaches 0, the entry 330 * is removed from the list and returned. Otherwise the entry remains 331 * on the list and a null pointer is returned. 332 */ 333 static smb_audit_t * 334 smbd_audit_unlink(uint32_t audit_sid) 335 { 336 smb_audit_t *entry; 337 smb_audit_t **ppe; 338 339 (void) mutex_lock(&smbd_audit_lock); 340 ppe = &smbd_audit_list; 341 342 while (*ppe) { 343 entry = *ppe; 344 345 if (entry->sa_audit_sid == audit_sid) { 346 if (entry->sa_refcnt == 0) 347 break; 348 349 if ((--entry->sa_refcnt) != 0) 350 break; 351 352 *ppe = entry->sa_next; 353 (void) mutex_unlock(&smbd_audit_lock); 354 return (entry); 355 } 356 357 ppe = &(*ppe)->sa_next; 358 } 359 360 (void) mutex_unlock(&smbd_audit_lock); 361 return (NULL); 362 } 363