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 *
smbd_user_auth_logon(smb_logon_t * user_info)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
smbd_user_nonauth_logon(uint32_t audit_sid)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
smbd_user_auth_logoff(uint32_t audit_sid)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
smbd_audit_link(smb_audit_t * entry)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 *
smbd_audit_unlink(uint32_t audit_sid)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