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