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