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