Print this page
NEX-18708 Domain logons may get STATUS_ACCESS_DENIED
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-14666 Need to provide SMB 2.1 Client
NEX-17187 panic in smbfs_acl_store
NEX-17231 smbfs create xattr files finds wrong file
NEX-17224 smbfs lookup EINVAL should be ENOENT
NEX-17260 SMB1 client fails to list directory after NEX-14666
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
and: (cleanup)
1575 untangle libmlrpc from SMB server
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Toomas Soome <tsoome@me.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
NEX-4083 Upstream changes from illumos 5917 and 5995
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-2667 Wrong error when join domain with wrong password
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
SUP-621 ... join AD domain, ... ACCESS_DENIED when trying to open \lsarpc
NEX-2225 Unable to join NexentaStor to 2008 AD
NEX-2286 smbadm join error messages are uninformative
NEX-1852 re-enable Kerberos-style AD join (try 2)
NEX-1638 Updated DC Locator
Includes work by: matt.barden@nexenta.com, kevin.crowe@nexenta.com
SFR-56 Identity Management for UNIX (IDMU) authentication support
re #13190 rb4312 idmapd error -9961 (No AD servers)
re #12435 rb3958 r10 is added 2 times to panic info
re #12393 rb3935 Kerberos and smbd disagree about who is our AD server
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c
+++ new/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
|
↓ open down ↓ |
12 lines elided |
↑ open up ↑ |
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 - * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
23 + * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
24 24 */
25 25
26 26 /*
27 27 * Utility functions to support the RPC interface library.
28 28 */
29 29
30 30 #include <stdio.h>
31 31 #include <stdarg.h>
32 32 #include <strings.h>
33 33 #include <unistd.h>
34 34 #include <netdb.h>
35 35 #include <stdlib.h>
36 36 #include <sys/time.h>
37 37 #include <sys/systm.h>
38 38 #include <note.h>
39 39 #include <syslog.h>
40 40
41 41 #include <smbsrv/libsmb.h>
42 42 #include <smbsrv/libsmbns.h>
43 43 #include <smbsrv/libmlsvc.h>
44 -#include <smbsrv/ntaccess.h>
44 +#include <smb/ntaccess.h>
45 45 #include <smbsrv/smbinfo.h>
46 46 #include <smbsrv/netrauth.h>
47 47 #include <libsmbrdr.h>
48 48 #include <lsalib.h>
49 49 #include <samlib.h>
50 50 #include <mlsvc.h>
51 51
52 52 static DWORD
53 53 mlsvc_join_rpc(smb_domainex_t *dxi,
54 54 char *admin_user, char *admin_pw,
55 55 char *machine_name, char *machine_pw);
56 56 static DWORD
57 57 mlsvc_join_noauth(smb_domainex_t *dxi,
58 58 char *machine_name, char *machine_pw);
59 59
60 -
60 +/*
61 + * This is called by smbd_dc_update just after we've learned about a
62 + * new domain controller. Make sure we can authenticate with this DC.
63 + */
61 64 DWORD
62 65 mlsvc_netlogon(char *server, char *domain)
63 66 {
64 - mlsvc_handle_t netr_handle;
65 67 DWORD status;
66 68
67 - status = netr_open(server, domain, &netr_handle);
68 - if (status != 0) {
69 - syslog(LOG_NOTICE, "Failed to connect to %s "
70 - "for domain %s (%s)", server, domain,
71 - xlate_nt_status(status));
72 - return (status);
73 - }
74 -
75 - status = netlogon_auth(server, &netr_handle, NETR_FLG_INIT);
69 + status = smb_netlogon_check(server, domain);
76 70 if (status != NT_STATUS_SUCCESS) {
77 71 syslog(LOG_NOTICE, "Failed to establish NETLOGON "
78 72 "credential chain with DC: %s (%s)", server,
79 73 xlate_nt_status(status));
80 74 syslog(LOG_NOTICE, "The machine account information on the "
81 75 "domain controller does not match the local storage.");
82 76 syslog(LOG_NOTICE, "To correct this, use 'smbadm join'");
83 77 }
84 - (void) netr_close(&netr_handle);
85 78
86 79 return (status);
87 80 }
88 81
89 82 /*
90 83 * Join the specified domain. The method varies depending on whether
91 84 * we're using "secure join" (using an administrative account to join)
92 85 * or "unsecure join" (using a pre-created machine account). In the
93 86 * latter case, the machine account is created "by hand" before this
94 87 * machine attempts to join, and we just change the password from the
95 88 * (weak) default password for a new machine account to a random one.
96 89 *
97 90 * Returns NT status codes.
98 91 */
99 92 void
100 93 mlsvc_join(smb_joininfo_t *info, smb_joinres_t *res)
101 94 {
102 95 static unsigned char zero_hash[SMBAUTH_HASH_SZ];
103 96 char machine_name[SMB_SAMACCT_MAXLEN];
104 97 char machine_pw[NETR_MACHINE_ACCT_PASSWD_MAX];
105 98 unsigned char passwd_hash[SMBAUTH_HASH_SZ];
106 99 smb_domainex_t dxi;
107 100 smb_domain_t *di = &dxi.d_primary;
108 101 DWORD status;
109 102 int rc;
110 103
111 104 bzero(&dxi, sizeof (dxi));
112 105
113 106 /*
114 107 * Domain join support: AD (Kerberos+LDAP) or MS-RPC?
115 108 */
116 109 boolean_t ads_enabled = smb_config_get_ads_enable();
117 110
118 111 if (smb_getsamaccount(machine_name, sizeof (machine_name)) != 0) {
119 112 res->status = NT_STATUS_INVALID_COMPUTER_NAME;
120 113 return;
121 114 }
122 115
123 116 (void) smb_gen_random_passwd(machine_pw, sizeof (machine_pw));
124 117
125 118 /*
126 119 * Ensure that any previous membership of this domain has
127 120 * been cleared from the environment before we start. This
128 121 * will ensure that we don't attempt a NETLOGON_SAMLOGON
129 122 * when attempting to find the PDC.
130 123 */
131 124 (void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_FALSE);
132 125
133 126 if (info->domain_username[0] != '\0') {
134 127 (void) smb_auth_ntlm_hash(info->domain_passwd, passwd_hash);
135 128 smb_ipc_set(info->domain_username, passwd_hash);
136 129 } else {
137 130 smb_ipc_set(MLSVC_ANON_USER, zero_hash);
138 131 }
139 132
140 133 /*
141 134 * Tentatively set the idmap domain to the one we're joining,
142 135 * so that the DC locator in idmap knows what to look for.
143 136 * Ditto the SMB server domain.
144 137 */
145 138 if (smb_config_set_idmap_domain(info->domain_name) != 0)
146 139 syslog(LOG_NOTICE, "Failed to set idmap domain name");
147 140 if (smb_config_refresh_idmap() != 0)
148 141 syslog(LOG_NOTICE, "Failed to refresh idmap service");
149 142
150 143 /* Clear DNS local (ADS) lookup cache. */
151 144 smb_ads_refresh(B_FALSE);
152 145
153 146 /*
154 147 * Locate a DC for this domain. Intentionally bypass the
155 148 * ddiscover service here because we're still joining.
156 149 * This also allows better reporting of any failures.
157 150 */
158 151 status = smb_ads_lookup_msdcs(info->domain_name, &dxi.d_dci);
159 152 if (status != NT_STATUS_SUCCESS) {
160 153 syslog(LOG_ERR,
161 154 "smbd: failed to locate AD server for domain %s (%s)",
162 155 info->domain_name, xlate_nt_status(status));
163 156 goto out;
164 157 }
165 158
166 159 /*
167 160 * Found a DC. Report what we found along with the return status
168 161 * so that admin will know which AD server we were talking to.
169 162 */
170 163 (void) strlcpy(res->dc_name, dxi.d_dci.dc_name, MAXHOSTNAMELEN);
171 164 syslog(LOG_INFO, "smbd: found AD server %s", dxi.d_dci.dc_name);
172 165
173 166 /*
174 167 * Domain discovery needs to authenticate with the AD server.
175 168 * Disconnect any existing connection with the domain controller
176 169 * to make sure we won't use any prior authentication context
177 170 * our redirector might have.
178 171 */
179 172 mlsvc_disconnect(dxi.d_dci.dc_name);
180 173
181 174 /*
182 175 * Get the domain policy info (domain SID etc).
183 176 * Here too, bypass the smb_ddiscover_service.
184 177 */
185 178 status = smb_ddiscover_main(info->domain_name, &dxi);
186 179 if (status != NT_STATUS_SUCCESS) {
187 180 syslog(LOG_ERR,
188 181 "smbd: failed getting domain info for %s (%s)",
189 182 info->domain_name, xlate_nt_status(status));
190 183 goto out;
191 184 }
192 185 /*
193 186 * After a successful smbd_ddiscover_main() call
194 187 * we should call smb_domain_save() to update the
195 188 * data shown by smbadm list. Do that at the end,
196 189 * only if all goes well with joining the domain.
197 190 */
198 191
199 192 /*
200 193 * Create or update our machine account on the DC.
201 194 * A non-null user means we do "secure join".
202 195 */
203 196 if (info->domain_username[0] != '\0') {
204 197 /*
205 198 * If enabled, try to join using AD Services.
206 199 */
207 200 status = NT_STATUS_UNSUCCESSFUL;
208 201 if (ads_enabled) {
209 202 res->join_err = smb_ads_join(di->di_fqname,
210 203 info->domain_username, info->domain_passwd,
211 204 machine_pw);
212 205 if (res->join_err == SMB_ADS_SUCCESS) {
213 206 status = NT_STATUS_SUCCESS;
214 207 }
215 208 } else {
216 209 syslog(LOG_DEBUG, "use_ads=false (do RPC join)");
217 210
218 211 /*
219 212 * If ADS was disabled, join using RPC.
220 213 */
221 214 status = mlsvc_join_rpc(&dxi,
222 215 info->domain_username,
223 216 info->domain_passwd,
224 217 machine_name, machine_pw);
225 218 }
226 219
227 220 } else {
228 221 /*
229 222 * Doing "Unsecure join" (pre-created account)
230 223 */
231 224 status = mlsvc_join_noauth(&dxi, machine_name, machine_pw);
232 225 }
233 226
234 227 if (status != NT_STATUS_SUCCESS)
235 228 goto out;
236 229
237 230 /*
238 231 * Make sure we can authenticate using the
239 232 * (new, or updated) machine account.
240 233 */
241 234 (void) smb_auth_ntlm_hash(machine_pw, passwd_hash);
242 235 smb_ipc_set(machine_name, passwd_hash);
243 236 rc = smbrdr_logon(dxi.d_dci.dc_name, di->di_nbname, machine_name);
244 237 if (rc != 0) {
245 238 syslog(LOG_NOTICE, "Authenticate with "
246 239 "new/updated machine account: %s",
247 240 strerror(rc));
248 241 res->join_err = SMB_ADJOIN_ERR_AUTH_NETLOGON;
249 242 status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
250 243 goto out;
251 244 }
252 245
253 246 /*
254 247 * Store the new machine account password, and
255 248 * SMB_CI_DOMAIN_MEMB etc.
256 249 */
257 250 rc = smb_setdomainprops(NULL, dxi.d_dci.dc_name, machine_pw);
258 251 if (rc != 0) {
259 252 syslog(LOG_NOTICE,
260 253 "Failed to save machine account password");
261 254 res->join_err = SMB_ADJOIN_ERR_STORE_PROPS;
262 255 status = NT_STATUS_INTERNAL_DB_ERROR;
263 256 goto out;
264 257 }
265 258
266 259 /*
267 260 * Update idmap config?
268 261 * Already set the domain_name above.
269 262 */
270 263
271 264 /*
272 265 * Save the SMB server config. Sets: SMB_CI_DOMAIN_*
273 266 * Should unify SMB vs idmap configs.
274 267 */
275 268 smb_config_setdomaininfo(di->di_nbname, di->di_fqname,
276 269 di->di_sid,
277 270 di->di_u.di_dns.ddi_forest,
278 271 di->di_u.di_dns.ddi_guid);
279 272 smb_ipc_commit();
280 273 smb_domain_save();
281 274
282 275 status = 0;
283 276
284 277 out:
285 278
286 279 if (status != 0) {
287 280 /*
288 281 * Undo the tentative domain settings.
289 282 */
290 283 (void) smb_config_set_idmap_domain("");
291 284 (void) smb_config_refresh_idmap();
292 285 smb_ipc_rollback();
293 286 }
294 287
295 288 /* Avoid leaving cleartext passwords around. */
296 289 bzero(machine_pw, sizeof (machine_pw));
297 290 bzero(passwd_hash, sizeof (passwd_hash));
298 291
299 292 res->status = status;
300 293 }
301 294
302 295 static DWORD
303 296 mlsvc_join_rpc(smb_domainex_t *dxi,
304 297 char *admin_user, char *admin_pw,
305 298 char *machine_name, char *machine_pw)
306 299 {
307 300 mlsvc_handle_t samr_handle;
308 301 mlsvc_handle_t domain_handle;
309 302 mlsvc_handle_t user_handle;
310 303 smb_account_t ainfo;
311 304 char *server = dxi->d_dci.dc_name;
312 305 smb_domain_t *di = &dxi->d_primary;
313 306 DWORD account_flags;
314 307 DWORD rid;
315 308 DWORD status;
316 309 int rc;
317 310
318 311 /* Caller did smb_ipc_set() so we don't need the pw for now. */
319 312 _NOTE(ARGUNUSED(admin_pw));
320 313
321 314 rc = samr_open(server, di->di_nbname, admin_user,
322 315 MAXIMUM_ALLOWED, &samr_handle);
323 316 if (rc != 0) {
324 317 syslog(LOG_NOTICE, "sam_connect to server %s failed", server);
325 318 return (RPC_NT_SERVER_UNAVAILABLE);
326 319 }
327 320 /* have samr_handle */
328 321
329 322 status = samr_open_domain(&samr_handle, MAXIMUM_ALLOWED,
330 323 (struct samr_sid *)di->di_binsid, &domain_handle);
331 324 if (status != NT_STATUS_SUCCESS)
332 325 goto out_samr_handle;
333 326 /* have domain_handle */
334 327
335 328 account_flags = SAMR_AF_WORKSTATION_TRUST_ACCOUNT;
336 329 status = samr_create_user(&domain_handle, machine_name,
337 330 account_flags, &rid, &user_handle);
338 331 if (status == NT_STATUS_USER_EXISTS) {
339 332 status = samr_lookup_domain_names(&domain_handle,
340 333 machine_name, &ainfo);
341 334 if (status != NT_STATUS_SUCCESS)
342 335 goto out_domain_handle;
343 336 status = samr_open_user(&domain_handle, MAXIMUM_ALLOWED,
344 337 ainfo.a_rid, &user_handle);
345 338 }
346 339 if (status != NT_STATUS_SUCCESS) {
347 340 syslog(LOG_NOTICE,
348 341 "smbd: failed to open machine account (%s)",
349 342 xlate_nt_status(status));
350 343 goto out_domain_handle;
351 344 }
352 345
353 346 /*
354 347 * The account exists, and we have user_handle open
355 348 * on that account. Set the password and flags.
356 349 */
357 350
358 351 status = netr_set_user_password(&user_handle, machine_pw);
359 352 if (status != NT_STATUS_SUCCESS) {
360 353 syslog(LOG_NOTICE,
361 354 "smbd: failed to set machine account password (%s)",
362 355 xlate_nt_status(status));
363 356 goto out_user_handle;
364 357 }
365 358
366 359 account_flags |= SAMR_AF_DONT_EXPIRE_PASSWD;
367 360 status = netr_set_user_control(&user_handle, account_flags);
368 361 if (status != NT_STATUS_SUCCESS) {
369 362 syslog(LOG_NOTICE,
370 363 "Set machine account control flags: %s",
371 364 xlate_nt_status(status));
372 365 goto out_user_handle;
373 366 }
374 367
375 368 out_user_handle:
376 369 (void) samr_close_handle(&user_handle);
377 370 out_domain_handle:
378 371 (void) samr_close_handle(&domain_handle);
379 372 out_samr_handle:
380 373 (void) samr_close_handle(&samr_handle);
381 374
382 375 return (status);
383 376 }
384 377
385 378 /*
386 379 * Doing "Unsecure join" (using a pre-created machine account).
387 380 * All we need to do is change the password from the default
388 381 * to a random string.
389 382 *
390 383 * Note: this is a work in progres. Nexenta issue 11960
391 384 * (allow joining an AD domain using a pre-created computer account)
392 385 * It turns out that to change the machine account password,
393 386 * we need to use a different RPC call, performed over the
394 387 * NetLogon secure channel. (See netr_server_password_set2)
395 388 */
396 389 static DWORD
397 390 mlsvc_join_noauth(smb_domainex_t *dxi,
398 391 char *machine_name, char *machine_pw)
399 392 {
400 393 char old_pw[SMB_SAMACCT_MAXLEN];
401 394 DWORD status;
402 395
403 396 /*
404 397 * Compose the current (default) password for the
405 398 * pre-created machine account, which is just the
406 399 * account name in lower case, truncated to 14
407 400 * characters.
408 401 */
409 402 if (smb_gethostname(old_pw, sizeof (old_pw), SMB_CASE_LOWER) != 0)
410 403 return (NT_STATUS_INTERNAL_ERROR);
411 404 old_pw[14] = '\0';
412 405
413 406 status = netr_change_password(dxi->d_dci.dc_name, machine_name,
414 407 old_pw, machine_pw);
415 408 if (status != NT_STATUS_SUCCESS) {
416 409 syslog(LOG_NOTICE,
|
↓ open down ↓ |
322 lines elided |
↑ open up ↑ |
417 410 "Change machine account password: %s",
418 411 xlate_nt_status(status));
419 412 }
420 413 return (status);
421 414 }
422 415
423 416 void
424 417 mlsvc_disconnect(const char *server)
425 418 {
426 419 smbrdr_disconnect(server);
420 +}
421 +
422 +/*
423 + * A few more helper functions for RPC services.
424 + */
425 +
426 +/*
427 + * Check whether or not the specified user has administrator privileges,
428 + * i.e. is a member of Domain Admins or Administrators.
429 + * Returns true if the user is an administrator, otherwise returns false.
430 + */
431 +boolean_t
432 +ndr_is_admin(ndr_xa_t *xa)
433 +{
434 + smb_netuserinfo_t *ctx = xa->pipe->np_user;
435 +
436 + return (ctx->ui_flags & SMB_ATF_ADMIN);
437 +}
438 +
439 +/*
440 + * Check whether or not the specified user has power-user privileges,
441 + * i.e. is a member of Domain Admins, Administrators or Power Users.
442 + * This is typically required for operations such as managing shares.
443 + * Returns true if the user is a power user, otherwise returns false.
444 + */
445 +boolean_t
446 +ndr_is_poweruser(ndr_xa_t *xa)
447 +{
448 + smb_netuserinfo_t *ctx = xa->pipe->np_user;
449 +
450 + return ((ctx->ui_flags & SMB_ATF_ADMIN) ||
451 + (ctx->ui_flags & SMB_ATF_POWERUSER));
452 +}
453 +
454 +int32_t
455 +ndr_native_os(ndr_xa_t *xa)
456 +{
457 + smb_netuserinfo_t *ctx = xa->pipe->np_user;
458 +
459 + return (ctx->ui_native_os);
427 460 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX