xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c (revision 613b28719c10e84c1202c1045df44d77767de21d)
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->session_key);
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 (!smb_sid_cmp((smb_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 smb_sid_t *
293 mlsvc_sid_save(smb_sid_t *sid, struct mlrpc_xaction *mxa)
294 {
295 	smb_sid_t *heap_sid;
296 	unsigned size;
297 
298 	if (sid == NULL)
299 		return (NULL);
300 
301 	size = smb_sid_len(sid);
302 
303 	if ((heap_sid = (smb_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 DWORD
331 mlsvc_netlogon(char *server, char *domain)
332 {
333 	mlsvc_handle_t netr_handle;
334 	DWORD status;
335 
336 	if (netr_open(server, domain, &netr_handle) == 0) {
337 		status = netlogon_auth(server, &netr_handle,
338 		    NETR_FLG_INIT);
339 		(void) netr_close(&netr_handle);
340 	} else {
341 		status = NT_STATUS_OPEN_FAILED;
342 	}
343 
344 	return (status);
345 }
346 
347 /*
348  * mlsvc_join
349  *
350  * Returns NT status codes.
351  */
352 DWORD
353 mlsvc_join(char *server, char *domain, char *plain_user, char *plain_text)
354 {
355 	smb_auth_info_t auth;
356 	smb_ntdomain_t *di;
357 	int erc;
358 	DWORD status;
359 	char machine_passwd[MLSVC_MACHINE_ACCT_PASSWD_MAX];
360 	char fqdn[MAXHOSTNAMELEN];
361 
362 	machine_passwd[0] = '\0';
363 
364 	/*
365 	 * Ensure that the domain name is uppercase.
366 	 */
367 	(void) utf8_strupr(domain);
368 
369 	/*
370 	 * There is no point continuing if the domain information is
371 	 * not available. Wait for up to 10 seconds and then give up.
372 	 */
373 	if ((di = smb_getdomaininfo(10)) == 0) {
374 		status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
375 		return (status);
376 	}
377 
378 	if (strcasecmp(domain, di->domain) != 0) {
379 		status = NT_STATUS_INVALID_PARAMETER;
380 		return (status);
381 	}
382 
383 	erc = mlsvc_logon(server, domain, plain_user);
384 
385 	if (erc == AUTH_USER_GRANT) {
386 		if (mlsvc_ntjoin_support == B_FALSE) {
387 			if (smb_resolve_fqdn(domain, fqdn, MAXHOSTNAMELEN) != 1)
388 				return (NT_STATUS_INVALID_PARAMETER);
389 
390 			if (smb_ads_join(fqdn, plain_user, plain_text,
391 			    machine_passwd, sizeof (machine_passwd))
392 			    == SMB_ADJOIN_SUCCESS)
393 				status = NT_STATUS_SUCCESS;
394 			else
395 				status = NT_STATUS_UNSUCCESSFUL;
396 		} else {
397 			if (mlsvc_user_getauth(server, plain_user, &auth)
398 			    != 0) {
399 				status = NT_STATUS_INVALID_PARAMETER;
400 				return (status);
401 			}
402 
403 			status = sam_create_trust_account(server, domain,
404 			    &auth);
405 			if (status == NT_STATUS_SUCCESS) {
406 				(void) smb_gethostname(machine_passwd,
407 				    sizeof (machine_passwd), 0);
408 				(void) utf8_strlwr(machine_passwd);
409 			}
410 		}
411 
412 		if (status == NT_STATUS_SUCCESS) {
413 			erc = smb_setdomainprops(NULL, server,
414 			    machine_passwd);
415 			if (erc != 0)
416 				return (NT_STATUS_UNSUCCESSFUL);
417 
418 			status = mlsvc_netlogon(server, domain);
419 		}
420 	} else {
421 		status = NT_STATUS_LOGON_FAILURE;
422 	}
423 
424 	return (status);
425 }
426