xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/samlib.c (revision da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0)
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 2007 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  * This module provides the high level interface to the SAM RPC
30  * functions.
31  */
32 
33 #include <unistd.h>
34 #include <netdb.h>
35 #include <alloca.h>
36 
37 #include <smbsrv/libsmb.h>
38 #include <smbsrv/libmlsvc.h>
39 #include <smbsrv/ntstatus.h>
40 #include <smbsrv/ntaccess.h>
41 #include <smbsrv/ntsid.h>
42 #include <smbsrv/lsalib.h>
43 #include <smbsrv/samlib.h>
44 
45 /*
46  * Valid values for the OEM OWF password encryption.
47  */
48 #define	SAM_PASSWORD_516	516
49 #define	SAM_KEYLEN		16
50 
51 extern DWORD samr_set_user_info(mlsvc_handle_t *, smb_auth_info_t *);
52 static int get_user_group_info(mlsvc_handle_t *, smb_userinfo_t *);
53 
54 /*
55  * sam_lookup_user_info
56  *
57  * Lookup user information in the SAM database on the specified server
58  * (domain controller). The LSA interface is used to obtain the user
59  * RID, the domain name and the domain SID (user privileges are TBD).
60  * Then the various SAM layers are opened using the domain SID and the
61  * user RID to obtain the users's group membership information.
62  *
63  * The information is returned in the user_info structure. The caller
64  * is responsible for allocating and releasing this structure. If the
65  * lookup is successful, sid_name_use will be set to SidTypeUser.
66  *
67  * On success 0 is returned. Otherwise a -ve error code.
68  */
69 int
70 sam_lookup_user_info(char *server, char *domain_name,
71     char *account_name, char *password, smb_userinfo_t *user_info)
72 {
73 	mlsvc_handle_t samr_handle;
74 	mlsvc_handle_t domain_handle;
75 	mlsvc_handle_t user_handle;
76 	struct samr_sid *sid;
77 	int rc;
78 	DWORD access_mask;
79 	DWORD status;
80 
81 	if (lsa_lookup_name(server, domain_name, account_name, user_info) != 0)
82 		return (-1);
83 
84 	if (user_info->sid_name_use != SidTypeUser ||
85 	    user_info->rid == 0 || user_info->domain_sid == 0) {
86 		return (-1);
87 	}
88 
89 	rc = samr_open(MLSVC_IPC_USER, server, domain_name, account_name,
90 	    password, SAM_LOOKUP_INFORMATION, &samr_handle);
91 	if (rc != 0)
92 		return (-1);
93 #if 0
94 	rc = samr_lookup_domain(&samr_handle, domain_name, user_info);
95 	if (rc != 0)
96 		return (-1);
97 #endif
98 	sid = (struct samr_sid *)user_info->domain_sid;
99 
100 	status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION,
101 	    sid, &domain_handle);
102 	if (status == 0) {
103 #if 0
104 		(void) samr_lookup_domain_names(&domain_handle, account_name,
105 		    user_info);
106 #endif
107 		access_mask = STANDARD_RIGHTS_EXECUTE | SAM_ACCESS_USER_READ;
108 
109 		rc = samr_open_user(&domain_handle, access_mask,
110 		    user_info->rid, &user_handle);
111 
112 		if (rc == 0) {
113 			(void) get_user_group_info(&user_handle, user_info);
114 			(void) samr_close_handle(&user_handle);
115 		}
116 
117 		(void) samr_close_handle(&domain_handle);
118 	}
119 
120 	(void) samr_close_handle(&samr_handle);
121 	return (rc);
122 }
123 
124 /*
125  * get_user_group_info
126  *
127  * This is a private function to obtain the primary group and group
128  * memberships for the user specified by the user_handle. This function
129  * should only be called from sam_lookup_user_info.
130  *
131  * On success 0 is returned. Otherwise -1 is returned.
132  */
133 static int
134 get_user_group_info(mlsvc_handle_t *user_handle, smb_userinfo_t *user_info)
135 {
136 	union samr_user_info sui;
137 	int rc;
138 
139 	rc = samr_query_user_info(user_handle, SAMR_QUERY_USER_GROUPRID, &sui);
140 	if (rc != 0)
141 		return (-1);
142 
143 	rc = samr_query_user_groups(user_handle, user_info);
144 	if (rc != 0)
145 		return (-1);
146 
147 	user_info->primary_group_rid = sui.info9.group_rid;
148 	return (0);
149 }
150 
151 /*
152  * sam_create_trust_account
153  *
154  * Create a trust account for this system.
155  *
156  *	SAMR_AF_WORKSTATION_TRUST_ACCOUNT: servers and workstations.
157  *	SAMR_AF_SERVER_TRUST_ACCOUNT: domain controllers.
158  *
159  * Returns NT status codes.
160  */
161 DWORD
162 sam_create_trust_account(char *server, char *domain, smb_auth_info_t *auth)
163 {
164 	smb_userinfo_t *user_info;
165 	char account_name[MAXHOSTNAMELEN];
166 	DWORD status;
167 
168 	if (smb_gethostname(account_name, MAXHOSTNAMELEN - 2, 1) != 0)
169 		return (NT_STATUS_NO_MEMORY);
170 
171 	(void) strlcat(account_name, "$", MAXHOSTNAMELEN);
172 
173 	if ((user_info = mlsvc_alloc_user_info()) == 0)
174 		return (NT_STATUS_NO_MEMORY);
175 
176 	/*
177 	 * The trust account value here should match
178 	 * the value that will be used when the user
179 	 * information is set on this account.
180 	 */
181 	status = sam_create_account(server, domain, account_name,
182 	    auth, SAMR_AF_WORKSTATION_TRUST_ACCOUNT, user_info);
183 
184 	mlsvc_free_user_info(user_info);
185 	return (status);
186 }
187 
188 
189 /*
190  * sam_create_account
191  *
192  * Create the specified domain account in the SAM database on the
193  * domain controller.
194  *
195  * Account flags:
196  *		SAMR_AF_NORMAL_ACCOUNT
197  *		SAMR_AF_WORKSTATION_TRUST_ACCOUNT
198  *		SAMR_AF_SERVER_TRUST_ACCOUNT
199  *
200  * Returns NT status codes.
201  */
202 DWORD
203 sam_create_account(char *server, char *domain_name, char *account_name,
204     smb_auth_info_t *auth, DWORD account_flags, smb_userinfo_t *user_info)
205 {
206 	mlsvc_handle_t samr_handle;
207 	mlsvc_handle_t domain_handle;
208 	mlsvc_handle_t user_handle;
209 	union samr_user_info sui;
210 	struct samr_sid *sid;
211 	DWORD rid;
212 	DWORD status;
213 	int rc;
214 
215 	rc = samr_open(MLSVC_IPC_ADMIN, server, domain_name, 0, 0,
216 	    SAM_CONNECT_CREATE_ACCOUNT, &samr_handle);
217 
218 	if (rc != 0) {
219 		status = NT_STATUS_OPEN_FAILED;
220 		smb_tracef("SamCreateAccount[%s\\%s]: %s",
221 		    domain_name, account_name, xlate_nt_status(status));
222 		return (status);
223 	}
224 
225 	if (samr_handle.context->server_os == NATIVE_OS_WIN2000) {
226 		nt_domain_t *ntdp;
227 
228 		if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) {
229 			(void) lsa_query_account_domain_info();
230 			if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) {
231 				(void) samr_close_handle(&samr_handle);
232 				status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
233 				smb_tracef("SamCreateAccount[%s\\%s]: %s",
234 				    domain_name, account_name,
235 				    xlate_nt_status(status));
236 				return (status);
237 			}
238 		}
239 
240 		sid = (struct samr_sid *)ntdp->sid;
241 	} else {
242 		if (samr_lookup_domain(&samr_handle,
243 		    domain_name, user_info) != 0) {
244 			(void) samr_close_handle(&samr_handle);
245 			smb_tracef("SamCreateAccount[%s]: lookup failed",
246 			    account_name);
247 
248 			return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
249 		}
250 
251 		sid = (struct samr_sid *)user_info->domain_sid;
252 	}
253 
254 	status = samr_open_domain(&samr_handle,
255 	    SAM_DOMAIN_CREATE_ACCOUNT, sid, &domain_handle);
256 
257 	if (status == NT_STATUS_SUCCESS) {
258 		status = samr_create_user(&domain_handle, account_name,
259 		    account_flags, &rid, &user_handle);
260 
261 		if (status == NT_STATUS_SUCCESS) {
262 			(void) samr_query_user_info(&user_handle,
263 			    SAMR_QUERY_USER_UNKNOWN16, &sui);
264 
265 			(void) samr_get_user_pwinfo(&user_handle);
266 			(void) samr_set_user_info(&user_handle, auth);
267 			(void) samr_close_handle(&user_handle);
268 		} else if (status == NT_STATUS_USER_EXISTS) {
269 			mlsvc_release_user_info(user_info);
270 
271 			rc = lsa_lookup_name(server, domain_name, account_name,
272 			    user_info);
273 			if (rc == 0)
274 				rid = user_info->rid;
275 			status = 0;
276 		} else {
277 			smb_tracef("SamCreateAccount[%s]: %s",
278 			    account_name, xlate_nt_status(status));
279 		}
280 
281 		(void) samr_close_handle(&domain_handle);
282 	} else {
283 		smb_tracef("SamCreateAccount[%s]: open domain failed",
284 		    account_name);
285 		status = (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
286 	}
287 
288 	(void) samr_close_handle(&samr_handle);
289 	return (status);
290 }
291 
292 
293 /*
294  * sam_remove_trust_account
295  *
296  * Attempt to remove the workstation trust account for this system.
297  * Administrator access is required to perform this operation.
298  *
299  * Returns NT status codes.
300  */
301 DWORD
302 sam_remove_trust_account(char *server, char *domain)
303 {
304 	char account_name[MAXHOSTNAMELEN];
305 
306 	if (smb_gethostname(account_name, MAXHOSTNAMELEN - 2, 1) != 0)
307 		return (NT_STATUS_NO_MEMORY);
308 
309 	(void) strcat(account_name, "$");
310 
311 	return (sam_delete_account(server, domain, account_name));
312 }
313 
314 
315 /*
316  * sam_delete_account
317  *
318  * Attempt to remove an account from the SAM database on the specified
319  * server.
320  *
321  * Returns NT status codes.
322  */
323 DWORD
324 sam_delete_account(char *server, char *domain_name, char *account_name)
325 {
326 	mlsvc_handle_t samr_handle;
327 	mlsvc_handle_t domain_handle;
328 	mlsvc_handle_t user_handle;
329 	smb_userinfo_t *user_info;
330 	struct samr_sid *sid;
331 	DWORD rid;
332 	DWORD access_mask;
333 	DWORD status;
334 	int rc;
335 
336 	if ((user_info = mlsvc_alloc_user_info()) == 0)
337 		return (NT_STATUS_NO_MEMORY);
338 
339 	rc = samr_open(MLSVC_IPC_ADMIN, server, domain_name, 0, 0,
340 	    SAM_LOOKUP_INFORMATION, &samr_handle);
341 
342 	if (rc != 0) {
343 		mlsvc_free_user_info(user_info);
344 		return (NT_STATUS_OPEN_FAILED);
345 	}
346 
347 	if (samr_handle.context->server_os == NATIVE_OS_WIN2000) {
348 		nt_domain_t *ntdp;
349 
350 		if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) {
351 			(void) lsa_query_account_domain_info();
352 			if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) {
353 
354 				(void) samr_close_handle(&samr_handle);
355 				return (NT_STATUS_NO_SUCH_DOMAIN);
356 			}
357 		}
358 
359 		sid = (struct samr_sid *)ntdp->sid;
360 	} else {
361 		if (samr_lookup_domain(
362 		    &samr_handle, domain_name, user_info) != 0) {
363 			(void) samr_close_handle(&samr_handle);
364 			mlsvc_free_user_info(user_info);
365 			return (NT_STATUS_NO_SUCH_DOMAIN);
366 		}
367 
368 		sid = (struct samr_sid *)user_info->domain_sid;
369 	}
370 
371 	status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION,
372 	    sid, &domain_handle);
373 	if (status == 0) {
374 		mlsvc_release_user_info(user_info);
375 		status = samr_lookup_domain_names(&domain_handle,
376 		    account_name, user_info);
377 
378 		if (status == 0) {
379 			rid = user_info->rid;
380 			access_mask = STANDARD_RIGHTS_EXECUTE | DELETE;
381 
382 			rc = samr_open_user(&domain_handle, access_mask,
383 			    rid, &user_handle);
384 			if (rc == 0) {
385 				if (samr_delete_user(&user_handle) != 0)
386 					(void) samr_close_handle(&user_handle);
387 			}
388 		}
389 
390 		(void) samr_close_handle(&domain_handle);
391 	}
392 
393 	(void) samr_close_handle(&samr_handle);
394 	mlsvc_free_user_info(user_info);
395 	return (status);
396 }
397 
398 /*
399  * sam_lookup_name
400  *
401  * Lookup an account name in the SAM database on the specified domain
402  * controller. Provides the account RID on success.
403  *
404  * Returns NT status codes.
405  */
406 DWORD
407 sam_lookup_name(char *server, char *domain_name, char *account_name,
408     DWORD *rid_ret)
409 {
410 	mlsvc_handle_t samr_handle;
411 	mlsvc_handle_t domain_handle;
412 	smb_userinfo_t *user_info;
413 	struct samr_sid *domain_sid;
414 	int rc;
415 	DWORD status;
416 
417 	*rid_ret = 0;
418 
419 	if ((user_info = mlsvc_alloc_user_info()) == 0)
420 		return (NT_STATUS_NO_MEMORY);
421 
422 	rc = samr_open(MLSVC_IPC_ANON, server, domain_name, 0, 0,
423 	    SAM_LOOKUP_INFORMATION, &samr_handle);
424 
425 	if (rc != 0) {
426 		mlsvc_free_user_info(user_info);
427 		return (NT_STATUS_OPEN_FAILED);
428 	}
429 
430 	rc = samr_lookup_domain(&samr_handle, domain_name, user_info);
431 	if (rc != 0) {
432 		(void) samr_close_handle(&samr_handle);
433 		mlsvc_free_user_info(user_info);
434 		return (NT_STATUS_NO_SUCH_DOMAIN);
435 	}
436 
437 	domain_sid = (struct samr_sid *)user_info->domain_sid;
438 
439 	status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION,
440 	    domain_sid, &domain_handle);
441 	if (status == 0) {
442 		mlsvc_release_user_info(user_info);
443 
444 		status = samr_lookup_domain_names(&domain_handle,
445 		    account_name, user_info);
446 		if (status == 0)
447 			*rid_ret = user_info->rid;
448 
449 		(void) samr_close_handle(&domain_handle);
450 	}
451 
452 	(void) samr_close_handle(&samr_handle);
453 	mlsvc_free_user_info(user_info);
454 	return (status);
455 }
456 
457 
458 /*
459  * sam_get_local_domains
460  *
461  * Query a remote server to get the list of local domains that it
462  * supports.
463  *
464  * Returns NT status codes.
465  */
466 DWORD
467 sam_get_local_domains(char *server, char *domain_name)
468 {
469 	mlsvc_handle_t samr_handle;
470 	DWORD status;
471 	int rc;
472 
473 	rc = samr_open(MLSVC_IPC_ANON, server, domain_name, 0, 0,
474 	    SAM_ENUM_LOCAL_DOMAIN, &samr_handle);
475 	if (rc != 0)
476 		return (NT_STATUS_OPEN_FAILED);
477 
478 	status = samr_enum_local_domains(&samr_handle);
479 	(void) samr_close_handle(&samr_handle);
480 	return (status);
481 }
482 
483 /*
484  * sam_oem_password
485  *
486  * Generate an OEM password.
487  */
488 int sam_oem_password(oem_password_t *oem_password, unsigned char *new_password,
489     unsigned char *old_password)
490 {
491 	mts_wchar_t *unicode_password;
492 	int length;
493 
494 #ifdef PBSHORTCUT
495 	assert(sizeof (oem_password_t) == SAM_PASSWORD_516);
496 #endif /* PBSHORTCUT */
497 
498 	length = strlen((char const *)new_password);
499 	unicode_password = alloca((length + 1) * sizeof (mts_wchar_t));
500 
501 	length = smb_auth_qnd_unicode((unsigned short *)unicode_password,
502 	    (char *)new_password, length);
503 	oem_password->length = length;
504 
505 	(void) memcpy(&oem_password->data[512 - length],
506 	    unicode_password, length);
507 
508 	rand_hash((unsigned char *)oem_password, sizeof (oem_password_t),
509 	    old_password, SAM_KEYLEN);
510 
511 	return (0);
512 }
513