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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Utility functions to support the RPC interface library.
30  */
31 
32 #include <stdio.h>
33 #include <stdarg.h>
34 #include <strings.h>
35 #include <unistd.h>
36 #include <netdb.h>
37 #include <stdlib.h>
38 
39 #include <sys/time.h>
40 #include <sys/systm.h>
41 
42 #include <smbsrv/libsmb.h>
43 #include <smbsrv/libsmbrdr.h>
44 #include <smbsrv/libsmbns.h>
45 #include <smbsrv/libmlsvc.h>
46 
47 #include <smbsrv/smbinfo.h>
48 #include <smbsrv/ntsid.h>
49 #include <smbsrv/lsalib.h>
50 #include <smbsrv/samlib.h>
51 #include <smbsrv/mlsvc_util.h>
52 #include <smbsrv/mlsvc.h>
53 
54 /* Domain join support (using MS-RPC) */
55 static boolean_t mlsvc_ntjoin_support = B_FALSE;
56 
57 extern int netr_open(char *, char *, mlsvc_handle_t *);
58 extern int netr_close(mlsvc_handle_t *);
59 extern DWORD netlogon_auth(char *, mlsvc_handle_t *, DWORD);
60 extern int mlsvc_user_getauth(char *, char *, smb_auth_info_t *);
61 
62 /*
63  * Compare the supplied domain name with the local hostname.
64  * We need to deal with both server names and fully-qualified
65  * domain names.
66  *
67  * Returns:
68  *	0	The specified domain is not the local domain,
69  *	1	The Specified domain is the local domain.
70  *	-1	Invalid parameter or unable to get the local
71  *		system information.
72  */
73 int
74 mlsvc_is_local_domain(const char *domain)
75 {
76 	char hostname[MAXHOSTNAMELEN];
77 	int rc;
78 
79 	if (smb_config_get_secmode() == SMB_SECMODE_WORKGRP)
80 		return (1);
81 
82 	if (strchr(domain, '.') != NULL)
83 		rc = smb_getfqhostname(hostname, MAXHOSTNAMELEN);
84 	else
85 		rc = smb_gethostname(hostname, MAXHOSTNAMELEN, 1);
86 
87 	if (rc != 0)
88 		return (-1);
89 
90 	if (strcasecmp(domain, hostname) == 0)
91 		return (1);
92 
93 	return (0);
94 }
95 
96 /*
97  * mlsvc_lookup_name
98  *
99  * This is just a wrapper for lsa_lookup_name.
100  *
101  * The memory for the sid is allocated using malloc so the caller should
102  * call free when it is no longer required.
103  */
104 uint32_t
105 mlsvc_lookup_name(char *account, nt_sid_t **sid, uint16_t *sid_type)
106 {
107 	smb_userinfo_t *ainfo;
108 	uint32_t status;
109 
110 	if ((ainfo = mlsvc_alloc_user_info()) == NULL)
111 		return (NT_STATUS_NO_MEMORY);
112 
113 	status = lsa_lookup_name(NULL, account, *sid_type, ainfo);
114 	if (status == NT_STATUS_SUCCESS) {
115 		*sid = ainfo->user_sid;
116 		ainfo->user_sid = NULL;
117 		*sid_type = ainfo->sid_name_use;
118 	}
119 
120 	mlsvc_free_user_info(ainfo);
121 	return (status);
122 }
123 
124 /*
125  * mlsvc_lookup_sid
126  *
127  * This is just a wrapper for lsa_lookup_sid.
128  *
129  * The allocated memory for the returned name must be freed by caller upon
130  * successful return.
131  */
132 uint32_t
133 mlsvc_lookup_sid(nt_sid_t *sid, char **name)
134 {
135 	smb_userinfo_t *ainfo;
136 	uint32_t status;
137 	int namelen;
138 
139 	if ((ainfo = mlsvc_alloc_user_info()) == NULL)
140 		return (NT_STATUS_NO_MEMORY);
141 
142 	status = lsa_lookup_sid(sid, ainfo);
143 	if (status == NT_STATUS_SUCCESS) {
144 		namelen = strlen(ainfo->domain_name) + strlen(ainfo->name) + 2;
145 		if ((*name = malloc(namelen)) == NULL) {
146 			mlsvc_free_user_info(ainfo);
147 			return (NT_STATUS_NO_MEMORY);
148 		}
149 		(void) snprintf(*name, namelen, "%s\\%s",
150 		    ainfo->domain_name, ainfo->name);
151 	}
152 
153 	mlsvc_free_user_info(ainfo);
154 	return (status);
155 }
156 
157 /*
158  * mlsvc_alloc_user_info
159  *
160  * Allocate a user_info structure and set the contents to zero. A
161  * pointer to the user_info structure is returned.
162  */
163 smb_userinfo_t *
164 mlsvc_alloc_user_info(void)
165 {
166 	smb_userinfo_t *user_info;
167 
168 	user_info = (smb_userinfo_t *)malloc(sizeof (smb_userinfo_t));
169 	if (user_info == NULL)
170 		return (NULL);
171 
172 	bzero(user_info, sizeof (smb_userinfo_t));
173 	return (user_info);
174 }
175 
176 /*
177  * mlsvc_free_user_info
178  *
179  * Free a user_info structure. This function ensures that the contents
180  * of the user_info are freed as well as the user_info itself.
181  */
182 void
183 mlsvc_free_user_info(smb_userinfo_t *user_info)
184 {
185 	if (user_info) {
186 		mlsvc_release_user_info(user_info);
187 		free(user_info);
188 	}
189 }
190 
191 /*
192  * mlsvc_release_user_info
193  *
194  * Release the contents of a user_info structure and zero out the
195  * elements but do not free the user_info structure itself. This
196  * function cleans out the structure so that it can be reused without
197  * worrying about stale contents.
198  */
199 void
200 mlsvc_release_user_info(smb_userinfo_t *user_info)
201 {
202 	int i;
203 
204 	if (user_info == NULL)
205 		return;
206 
207 	free(user_info->name);
208 	free(user_info->domain_sid);
209 	free(user_info->domain_name);
210 	free(user_info->groups);
211 
212 	if (user_info->n_other_grps) {
213 		for (i = 0; i < user_info->n_other_grps; i++)
214 			free(user_info->other_grps[i].sid);
215 
216 		free(user_info->other_grps);
217 	}
218 
219 	free(user_info->user_sid);
220 	free(user_info->pgrp_sid);
221 	bzero(user_info, sizeof (smb_userinfo_t));
222 }
223 
224 /*
225  * mlsvc_setadmin_user_info
226  *
227  * Determines if the given user is the domain Administrator or a
228  * member of Domain Admins or Administrators group and set the
229  * user_info->flags accordingly.
230  */
231 void
232 mlsvc_setadmin_user_info(smb_userinfo_t *user_info)
233 {
234 	nt_domain_t *domain;
235 	smb_group_t grp;
236 	int rc, i;
237 
238 	if ((domain = nt_domain_lookupbytype(NT_DOMAIN_PRIMARY)) == NULL)
239 		return;
240 
241 	if (!nt_sid_is_equal((nt_sid_t *)user_info->domain_sid, domain->sid))
242 		return;
243 
244 	if (user_info->rid == DOMAIN_USER_RID_ADMIN)
245 		user_info->flags |= SMB_UINFO_FLAG_DADMIN;
246 	else if (user_info->primary_group_rid == DOMAIN_GROUP_RID_ADMINS)
247 		user_info->flags |= SMB_UINFO_FLAG_DADMIN;
248 	else {
249 		for (i = 0; i < user_info->n_groups; i++)
250 			if (user_info->groups[i].rid == DOMAIN_GROUP_RID_ADMINS)
251 				user_info->flags |= SMB_UINFO_FLAG_DADMIN;
252 	}
253 
254 	rc = smb_lgrp_getbyname("Administrators", &grp);
255 	if (rc == SMB_LGRP_SUCCESS) {
256 		if (smb_lgrp_is_member(&grp, user_info->user_sid))
257 			user_info->flags |= SMB_UINFO_FLAG_LADMIN;
258 		smb_lgrp_free(&grp);
259 	}
260 }
261 
262 /*
263  * mlsvc_string_save
264  *
265  * This is a convenience function to prepare strings for an RPC call.
266  * An ms_string_t is set up with the appropriate lengths and str is
267  * set up to point to a copy of the original string on the heap. The
268  * macro MLRPC_HEAP_STRSAVE is an alias for mlrpc_heap_strsave, which
269  * extends the heap and copies the string into the new area.
270  */
271 int
272 mlsvc_string_save(ms_string_t *ms, char *str, struct mlrpc_xaction *mxa)
273 {
274 	if (str == NULL)
275 		return (0);
276 
277 	ms->length = mts_wcequiv_strlen(str);
278 	ms->allosize = ms->length + sizeof (mts_wchar_t);
279 
280 	if ((ms->str = MLRPC_HEAP_STRSAVE(mxa, str)) == NULL)
281 		return (0);
282 
283 	return (1);
284 }
285 
286 /*
287  * mlsvc_sid_save
288  *
289  * Expand the heap and copy the sid into the new area.
290  * Returns a pointer to the copy of the sid on the heap.
291  */
292 nt_sid_t *
293 mlsvc_sid_save(nt_sid_t *sid, struct mlrpc_xaction *mxa)
294 {
295 	nt_sid_t *heap_sid;
296 	unsigned size;
297 
298 	if (sid == NULL)
299 		return (NULL);
300 
301 	size = nt_sid_length(sid);
302 
303 	if ((heap_sid = (nt_sid_t *)MLRPC_HEAP_MALLOC(mxa, size)) == NULL)
304 		return (0);
305 
306 	bcopy(sid, heap_sid, size);
307 	return (heap_sid);
308 }
309 
310 /*
311  * mlsvc_is_null_handle
312  *
313  * Check a handle against a null handle. Returns 1 if the handle is
314  * null. Otherwise returns 0.
315  */
316 int
317 mlsvc_is_null_handle(mlsvc_handle_t *handle)
318 {
319 	static ms_handle_t zero_handle;
320 
321 	if (handle == NULL || handle->context == NULL)
322 		return (1);
323 
324 	if (!memcmp(&handle->handle, &zero_handle, sizeof (ms_handle_t)))
325 		return (1);
326 
327 	return (0);
328 }
329 
330 /*
331  * mlsvc_join
332  *
333  * Returns NT status codes.
334  */
335 DWORD
336 mlsvc_join(char *server, char *domain, char *plain_user, char *plain_text)
337 {
338 	smb_auth_info_t auth;
339 	smb_ntdomain_t *di;
340 	int erc;
341 	DWORD status;
342 	mlsvc_handle_t netr_handle;
343 	char machine_passwd[MLSVC_MACHINE_ACCT_PASSWD_MAX];
344 	char fqdn[MAXHOSTNAMELEN];
345 
346 	machine_passwd[0] = '\0';
347 
348 	/*
349 	 * Ensure that the domain name is uppercase.
350 	 */
351 	(void) utf8_strupr(domain);
352 
353 	/*
354 	 * There is no point continuing if the domain information is
355 	 * not available. Wait for up to 10 seconds and then give up.
356 	 */
357 	if ((di = smb_getdomaininfo(10)) == 0) {
358 		status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
359 		return (status);
360 	}
361 
362 	if (strcasecmp(domain, di->domain) != 0) {
363 		status = NT_STATUS_INVALID_PARAMETER;
364 		return (status);
365 	}
366 
367 	erc = mlsvc_logon(server, domain, plain_user);
368 
369 	if (erc == AUTH_USER_GRANT) {
370 		if (mlsvc_ntjoin_support == B_FALSE) {
371 			if (smb_resolve_fqdn(domain, fqdn, MAXHOSTNAMELEN) != 1)
372 				return (NT_STATUS_INVALID_PARAMETER);
373 
374 			if (ads_join(fqdn, plain_user, plain_text,
375 			    machine_passwd, sizeof (machine_passwd))
376 			    == ADJOIN_SUCCESS)
377 				status = NT_STATUS_SUCCESS;
378 			else
379 				status = NT_STATUS_UNSUCCESSFUL;
380 		} else {
381 			if (mlsvc_user_getauth(server, plain_user, &auth)
382 			    != 0) {
383 				status = NT_STATUS_INVALID_PARAMETER;
384 				return (status);
385 			}
386 
387 			status = sam_create_trust_account(server, domain,
388 			    &auth);
389 			if (status == NT_STATUS_SUCCESS) {
390 				(void) smb_gethostname(machine_passwd,
391 				    sizeof (machine_passwd), 0);
392 				(void) utf8_strlwr(machine_passwd);
393 			}
394 		}
395 
396 		if (status == NT_STATUS_SUCCESS) {
397 			erc = smb_config_setstr(SMB_CI_MACHINE_PASSWD,
398 			    machine_passwd);
399 			if (erc != SMBD_SMF_OK)
400 				return (NT_STATUS_UNSUCCESSFUL);
401 
402 			/*
403 			 * If we successfully create a trust account, we mark
404 			 * ourselves as a domain member in the environment so
405 			 * that we use the SAMLOGON version of the NETLOGON
406 			 * PDC location protocol.
407 			 */
408 			(void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_TRUE);
409 
410 			if (netr_open(server, domain, &netr_handle) == 0) {
411 				status = netlogon_auth(server, &netr_handle,
412 				    NETR_FLG_INIT);
413 				(void) netr_close(&netr_handle);
414 			} else {
415 				status = NT_STATUS_OPEN_FAILED;
416 			}
417 		}
418 	} else {
419 		status = NT_STATUS_LOGON_FAILURE;
420 	}
421 
422 	return (status);
423 }
424