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 /*
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2021 Tintri by DDN, Inc. All rights reserved.
25 * Copyright 2023 RackTop Systems, Inc.
26 */
27
28 /*
29 * NETR SamLogon and SamLogoff RPC client functions.
30 */
31
32 #include <stdio.h>
33 #include <strings.h>
34 #include <stdlib.h>
35 #include <time.h>
36 #include <alloca.h>
37 #include <unistd.h>
38 #include <netdb.h>
39 #include <thread.h>
40
41 #include <libmlrpc/libmlrpc.h>
42 #include <smbsrv/libsmb.h>
43 #include <smbsrv/libmlsvc.h>
44 #include <smbsrv/ndl/netlogon.ndl>
45 #include <smbsrv/netrauth.h>
46 #include <smbsrv/smbinfo.h>
47 #include <smbsrv/smb_token.h>
48 #include <mlsvc.h>
49
50 uint32_t netlogon_logon(smb_logon_t *, smb_token_t *, smb_domainex_t *);
51 static uint32_t netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *,
52 smb_logon_t *, smb_token_t *);
53 static void netr_invalidate_chain(netr_info_t *);
54 static void netr_interactive_samlogon(netr_info_t *, smb_logon_t *,
55 struct netr_logon_info1 *);
56 static void netr_network_samlogon(ndr_heap_t *, netr_info_t *,
57 smb_logon_t *, struct netr_logon_info2 *);
58 static void netr_setup_identity(ndr_heap_t *, smb_logon_t *,
59 netr_logon_id_t *);
60 static uint32_t netr_setup_domain_groups(struct netr_validation_info3 *,
61 smb_ids_t *);
62 static uint32_t netr_setup_krb5res_groups(struct krb5_validation_info *,
63 smb_ids_t *);
64 static uint32_t netr_setup_token_wingrps(struct netr_validation_info3 *,
65 smb_token_t *);
66
67 /*
68 * Shared with netr_auth.c
69 */
70 extern netr_info_t netr_global_info;
71
72 static mutex_t netlogon_mutex;
73 static cond_t netlogon_cv;
74 static boolean_t netlogon_busy = B_FALSE;
75 static boolean_t netlogon_abort = B_FALSE;
76
77 /*
78 * Helper for Kerberos authentication
79 */
80 uint32_t
smb_decode_krb5_pac(smb_token_t * token,char * data,uint_t len)81 smb_decode_krb5_pac(smb_token_t *token, char *data, uint_t len)
82 {
83 struct krb5_validation_info info;
84 ndr_buf_t *nbuf;
85 smb_sid_t *domsid;
86 uint32_t status = NT_STATUS_NO_MEMORY;
87 int rc;
88
89 bzero(&info, sizeof (info));
90
91 /* Need to keep this until we're done with &info */
92 nbuf = ndr_buf_init(&TYPEINFO(netr_interface));
93 if (nbuf == NULL)
94 goto out;
95
96 rc = ndr_buf_decode(nbuf, NDR_PTYPE_PAC,
97 NETR_OPNUM_decode_krb5_pac, data, len, &info);
98 if (rc != NDR_DRC_OK) {
99 status = RPC_NT_PROTOCOL_ERROR;
100 goto out;
101 }
102
103 /*
104 * Copy the decoded info into the token,
105 * similar to netr_setup_token()
106 */
107 domsid = (smb_sid_t *)info.info3.LogonDomainId;
108
109 token->tkn_user.i_sid = smb_sid_splice(domsid,
110 info.info3.UserId);
111 if (token->tkn_user.i_sid == NULL)
112 goto out;
113
114 token->tkn_primary_grp.i_sid = smb_sid_splice(domsid,
115 info.info3.PrimaryGroupId);
116 if (token->tkn_primary_grp.i_sid == NULL)
117 goto out;
118
119 if (info.info3.EffectiveName.str) {
120 token->tkn_account_name =
121 strdup((char *)info.info3.EffectiveName.str);
122 if (token->tkn_account_name == NULL)
123 goto out;
124 }
125
126 if (info.info3.LogonDomainName.str) {
127 token->tkn_domain_name =
128 strdup((char *)info.info3.LogonDomainName.str);
129 if (token->tkn_domain_name == NULL)
130 goto out;
131 }
132
133 status = netr_setup_domain_groups(&info.info3, &token->tkn_win_grps);
134 if (status != NT_STATUS_SUCCESS)
135 goto out;
136
137 if (info.rg_rid_cnt != 0) {
138 status = netr_setup_krb5res_groups(&info, &token->tkn_win_grps);
139 if (status != NT_STATUS_SUCCESS)
140 goto out;
141 }
142
143 status = netr_setup_token_wingrps(&info.info3, token);
144
145 out:
146 if (nbuf != NULL)
147 ndr_buf_fini(nbuf);
148
149 return (status);
150 }
151
152 /*
153 * Abort impending domain logon requests.
154 */
155 void
smb_logon_abort(void)156 smb_logon_abort(void)
157 {
158 (void) mutex_lock(&netlogon_mutex);
159 if (netlogon_busy && !netlogon_abort)
160 syslog(LOG_DEBUG, "logon abort");
161 netlogon_abort = B_TRUE;
162 (void) cond_broadcast(&netlogon_cv);
163 (void) mutex_unlock(&netlogon_mutex);
164 }
165
166 /*
167 * This is the entry point for authenticating domain users.
168 *
169 * If we are not going to attempt to authenticate the user,
170 * this function must return without updating the status.
171 *
172 * If the user is successfully authenticated, we build an
173 * access token and the status will be NT_STATUS_SUCCESS.
174 * Otherwise, the token contents are invalid.
175 *
176 * This will retry a few times for errors indicating that the
177 * current DC might have gone off-line or become too busy etc.
178 * With such errors, smb_ddiscover_bad_dc is called and then
179 * the smb_domain_getinfo call here waits for new DC info.
180 */
181 int smb_netr_logon_retries = 3;
182 void
smb_logon_domain(smb_logon_t * user_info,smb_token_t * token)183 smb_logon_domain(smb_logon_t *user_info, smb_token_t *token)
184 {
185 smb_domainex_t di;
186 uint32_t status;
187 int retries = smb_netr_logon_retries;
188
189 if (user_info->lg_secmode != SMB_SECMODE_DOMAIN)
190 return;
191
192 if (user_info->lg_domain_type == SMB_DOMAIN_LOCAL)
193 return;
194
195 while (--retries > 0) {
196
197 if (!smb_domain_getinfo(&di)) {
198 syslog(LOG_ERR, "logon DC getinfo failed");
199 status = NT_STATUS_NO_LOGON_SERVERS;
200 goto out;
201 }
202
203 (void) mutex_lock(&netlogon_mutex);
204 while (netlogon_busy && !netlogon_abort)
205 (void) cond_wait(&netlogon_cv, &netlogon_mutex);
206
207 if (netlogon_abort) {
208 (void) mutex_unlock(&netlogon_mutex);
209 status = NT_STATUS_REQUEST_ABORTED;
210 goto out;
211 }
212
213 netlogon_busy = B_TRUE;
214 (void) mutex_unlock(&netlogon_mutex);
215
216 status = netlogon_logon(user_info, token, &di);
217
218 (void) mutex_lock(&netlogon_mutex);
219 netlogon_busy = B_FALSE;
220 if (netlogon_abort)
221 status = NT_STATUS_REQUEST_ABORTED;
222 (void) cond_signal(&netlogon_cv);
223 (void) mutex_unlock(&netlogon_mutex);
224
225 switch (status) {
226 case NT_STATUS_BAD_NETWORK_PATH:
227 case NT_STATUS_BAD_NETWORK_NAME:
228 case RPC_NT_SERVER_TOO_BUSY:
229 /*
230 * May retry with a new DC, or if we're
231 * out of retries, will return...
232 */
233 status = NT_STATUS_NO_LOGON_SERVERS;
234 break;
235 default:
236 goto out;
237 }
238 }
239
240 out:
241 if (status != NT_STATUS_SUCCESS)
242 syslog(LOG_INFO, "logon[%s\\%s]: %s", user_info->lg_e_domain,
243 user_info->lg_e_username, xlate_nt_status(status));
244 user_info->lg_status = status;
245 }
246
247 static uint32_t
netr_get_handle(char * server,char * domain,mlsvc_handle_t * netr_handle)248 netr_get_handle(char *server, char *domain, mlsvc_handle_t *netr_handle)
249 {
250 uint32_t status;
251 boolean_t did_renego = B_FALSE;
252
253 reauth:
254 if ((netr_global_info.flags & NETR_FLG_VALID) == 0 ||
255 !smb_match_netlogon_seqnum()) {
256 /*
257 * This does netr_server_req_challenge() and
258 * netr_server_authenticate2(), updating the
259 * current netlogon sequence number.
260 */
261 status = netlogon_auth(server, domain, NETR_FLG_NULL);
262
263 if (status != 0) {
264 syslog(LOG_ERR, "%s: auth failed (%s)",
265 __func__, xlate_nt_status(status));
266 return (status);
267 }
268
269 netr_global_info.flags |= NETR_FLG_VALID;
270 }
271
272 /*
273 * This netr_open_secure call does the work to connect to the DC,
274 * get the IPC share, open the named pipe, RPC bind, etc.
275 */
276 status = netr_open_secure(server, domain, netr_handle);
277 if (status != 0) {
278 /*
279 * This may have failed because the DC restarted.
280 * Re-negotiate once.
281 */
282 if (!did_renego) {
283 did_renego = B_TRUE;
284 netr_invalidate_chain(&netr_global_info);
285 syslog(LOG_ERR, "%s: open failed (%s); "
286 "renegotiating...",
287 __func__, xlate_nt_status(status));
288 goto reauth;
289 }
290 syslog(LOG_ERR, "%s: open failed (%s)",
291 __func__, xlate_nt_status(status));
292 }
293
294 return (status);
295 }
296
297 /*
298 * Run a netr_server_samlogon call, dealing with the possible need to
299 * re-establish the NetLogon credential chain. If that fails, return
300 * NT_STATUS_DOMAIN_TRUST_INCONSISTENT indicating the machine account
301 * needs it's password reset (or whatever). Other errors are from the
302 * netr_server_samlogon() call including the many possibilities listed
303 * above that function.
304 */
305 uint32_t
netlogon_logon(smb_logon_t * user_info,smb_token_t * token,smb_domainex_t * di)306 netlogon_logon(smb_logon_t *user_info, smb_token_t *token, smb_domainex_t *di)
307 {
308 char server[MAXHOSTNAMELEN];
309 mlsvc_handle_t netr_handle;
310 uint32_t status;
311 boolean_t did_reauth = B_FALSE;
312
313 if (di->d_dci.dc_name[0] != '\0' &&
314 (*netr_global_info.server != '\0')) {
315 (void) snprintf(server, sizeof (server),
316 "\\\\%s", di->d_dci.dc_name);
317 if (strncasecmp(netr_global_info.server,
318 server, strlen(server)) != 0)
319 netr_invalidate_chain(&netr_global_info);
320 }
321
322 reauth:
323 status = netr_get_handle(di->d_dci.dc_name,
324 di->d_primary.di_nbname, &netr_handle);
325
326 if (status != 0) {
327 syslog(LOG_ERR, "%s: failed to get handle (%s)",
328 __func__, xlate_nt_status(status));
329 return (NT_STATUS_DOMAIN_TRUST_INCONSISTENT);
330 }
331
332 status = netr_server_samlogon(&netr_handle,
333 &netr_global_info, di->d_dci.dc_name, user_info, token);
334
335 if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) {
336 if (!did_reauth) {
337 /* Call netlogon_auth() again, just once. */
338 (void) netr_close(&netr_handle);
339 did_reauth = B_TRUE;
340 goto reauth;
341 }
342 status = NT_STATUS_DOMAIN_TRUST_INCONSISTENT;
343 }
344
345 (void) netr_close(&netr_handle);
346
347 return (status);
348 }
349
350 /*
351 * Helper for mlsvc_netlogon
352 *
353 * Call netlogon_auth with appropriate locks etc.
354 * Serialize like smb_logon_domain does for
355 * netlogon_logon / netlogon_auth
356 */
357 uint32_t
smb_netlogon_check(char * server,char * domain)358 smb_netlogon_check(char *server, char *domain)
359 {
360 mlsvc_handle_t netr_handle;
361 uint32_t status;
362
363 (void) mutex_lock(&netlogon_mutex);
364 while (netlogon_busy)
365 (void) cond_wait(&netlogon_cv, &netlogon_mutex);
366
367 netlogon_busy = B_TRUE;
368 (void) mutex_unlock(&netlogon_mutex);
369
370 /*
371 * Like netlogon_logon(), but no netr_server_samlogon call.
372 * We're just making sure we can connect to the NETLOGON server.
373 */
374 status = netr_get_handle(server, domain, &netr_handle);
375 if (status == 0)
376 (void) netr_close(&netr_handle);
377 else
378 syslog(LOG_ERR, "%s: failed to get handle (%s)",
379 __func__, xlate_nt_status(status));
380
381 (void) mutex_lock(&netlogon_mutex);
382 netlogon_busy = B_FALSE;
383 (void) cond_signal(&netlogon_cv);
384 (void) mutex_unlock(&netlogon_mutex);
385
386 return (status);
387 }
388
389 static uint32_t
netr_setup_token(struct netr_validation_info3 * info3,smb_logon_t * user_info,netr_info_t * netr_info,smb_token_t * token)390 netr_setup_token(struct netr_validation_info3 *info3, smb_logon_t *user_info,
391 netr_info_t *netr_info, smb_token_t *token)
392 {
393 char *username, *domain;
394 unsigned char rc4key[SMBAUTH_SESSION_KEY_SZ];
395 smb_sid_t *domsid;
396 uint32_t status;
397 char nbdomain[NETBIOS_NAME_SZ];
398
399 domsid = (smb_sid_t *)info3->LogonDomainId;
400
401 token->tkn_user.i_sid = smb_sid_splice(domsid, info3->UserId);
402 if (token->tkn_user.i_sid == NULL)
403 return (NT_STATUS_NO_MEMORY);
404
405 token->tkn_primary_grp.i_sid = smb_sid_splice(domsid,
406 info3->PrimaryGroupId);
407 if (token->tkn_primary_grp.i_sid == NULL)
408 return (NT_STATUS_NO_MEMORY);
409
410 username = (info3->EffectiveName.str)
411 ? (char *)info3->EffectiveName.str : user_info->lg_e_username;
412
413 if (info3->LogonDomainName.str) {
414 domain = (char *)info3->LogonDomainName.str;
415 } else if (*user_info->lg_e_domain != '\0') {
416 domain = user_info->lg_e_domain;
417 } else {
418 (void) smb_getdomainname(nbdomain, sizeof (nbdomain));
419 domain = nbdomain;
420 }
421
422 if (username)
423 token->tkn_account_name = strdup(username);
424 if (domain)
425 token->tkn_domain_name = strdup(domain);
426
427 if (token->tkn_account_name == NULL || token->tkn_domain_name == NULL)
428 return (NT_STATUS_NO_MEMORY);
429
430 status = netr_setup_domain_groups(info3, &token->tkn_win_grps);
431 if (status != NT_STATUS_SUCCESS)
432 return (status);
433
434 status = netr_setup_token_wingrps(info3, token);
435 if (status != NT_STATUS_SUCCESS)
436 return (status);
437
438 /*
439 * The UserSessionKey in NetrSamLogon RPC is obfuscated using the
440 * session key obtained in the NETLOGON credential chain.
441 * An 8 byte session key is zero extended to 16 bytes. This 16 byte
442 * key is the key to the RC4 algorithm. The RC4 byte stream is
443 * exclusively ored with the 16 byte UserSessionKey to recover
444 * the the clear form.
445 */
446 if ((token->tkn_ssnkey.val = malloc(SMBAUTH_SESSION_KEY_SZ)) == NULL)
447 return (NT_STATUS_NO_MEMORY);
448 token->tkn_ssnkey.len = SMBAUTH_SESSION_KEY_SZ;
449 bzero(rc4key, SMBAUTH_SESSION_KEY_SZ);
450 bcopy(netr_info->session_key.key, rc4key, netr_info->session_key.len);
451 bcopy(info3->UserSessionKey.data, token->tkn_ssnkey.val,
452 SMBAUTH_SESSION_KEY_SZ);
453 rand_hash((unsigned char *)token->tkn_ssnkey.val,
454 SMBAUTH_SESSION_KEY_SZ, rc4key, SMBAUTH_SESSION_KEY_SZ);
455
456 return (NT_STATUS_SUCCESS);
457 }
458
459 /*
460 * netr_server_samlogon
461 *
462 * NetrServerSamLogon RPC: interactive or network. It is assumed that
463 * we have already authenticated with the PDC. If everything works,
464 * we build a user info structure and return it, where the caller will
465 * probably build an access token.
466 *
467 * Returns an NT status. There are numerous possibilities here.
468 * For example:
469 * NT_STATUS_INVALID_INFO_CLASS
470 * NT_STATUS_INVALID_PARAMETER
471 * NT_STATUS_ACCESS_DENIED
472 * NT_STATUS_PASSWORD_MUST_CHANGE
473 * NT_STATUS_NO_SUCH_USER
474 * NT_STATUS_WRONG_PASSWORD
475 * NT_STATUS_LOGON_FAILURE
476 * NT_STATUS_ACCOUNT_RESTRICTION
477 * NT_STATUS_INVALID_LOGON_HOURS
478 * NT_STATUS_INVALID_WORKSTATION
479 * NT_STATUS_INTERNAL_ERROR
480 * NT_STATUS_PASSWORD_EXPIRED
481 * NT_STATUS_ACCOUNT_DISABLED
482 */
483 uint32_t
netr_server_samlogon(mlsvc_handle_t * netr_handle,netr_info_t * netr_info,char * server,smb_logon_t * user_info,smb_token_t * token)484 netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info,
485 char *server, smb_logon_t *user_info, smb_token_t *token)
486 {
487 struct netr_SamLogon logon_op;
488 struct netr_SamLogonEx logon_ex_op;
489 struct netr_authenticator auth;
490 struct netr_authenticator ret_auth;
491 struct netr_logon_info1 info1;
492 struct netr_logon_info2 info2;
493 struct netr_validation_info3 *info3;
494 union netr_validation_u *valid_info;
495 union netr_logon_info_u *logon_info;
496 LPTSTR servername, hostname;
497 ndr_heap_t *heap;
498 int opnum;
499 int rc, len;
500 uint32_t status, *rpc_status;
501 void *rpc_arg;
502
503 /*
504 * Should we get the server and hostname from netr_info?
505 */
506
507 len = strlen(server) + 4;
508 servername = ndr_rpc_malloc(netr_handle, len);
509 hostname = ndr_rpc_malloc(netr_handle, NETBIOS_NAME_SZ);
510 if (servername == NULL || hostname == NULL) {
511 ndr_rpc_release(netr_handle);
512 return (NT_STATUS_INTERNAL_ERROR);
513 }
514
515 (void) snprintf((char *)servername, len, "\\\\%s", server);
516 if (smb_getnetbiosname((char *)hostname, NETBIOS_NAME_SZ) != 0) {
517 ndr_rpc_release(netr_handle);
518 return (NT_STATUS_INTERNAL_ERROR);
519 }
520
521 rc = netr_setup_authenticator(netr_info, &auth, &ret_auth);
522 if (rc != SMBAUTH_SUCCESS) {
523 ndr_rpc_release(netr_handle);
524 return (NT_STATUS_INTERNAL_ERROR);
525 }
526
527 /*
528 * If we use Secure RPC, we can use SamLogonEx instead of SamLogon.
529 * SamLogonEx doesn't use NetLogon authenticators, instead relying
530 * on Secure RPC to provide security.
531 * This allows us to avoid being bitten by mitigations in
532 * the authenticator verification logic on DCs.
533 */
534 if (netr_info->use_logon_ex &&
535 (netr_info->nego_flags & NETR_NEGO_SECURE_RPC_FLAG) != 0) {
536 bzero(&logon_ex_op, sizeof (struct netr_SamLogonEx));
537 logon_ex_op.servername = servername;
538 logon_ex_op.hostname = hostname;
539 logon_ex_op.logon_info.logon_level = user_info->lg_level;
540 logon_ex_op.logon_info.switch_value = user_info->lg_level;
541 logon_ex_op.validation_level = NETR_VALIDATION_LEVEL3;
542 logon_ex_op.extra_flags = 0;
543 logon_info = &logon_ex_op.logon_info.ru;
544 valid_info = &logon_ex_op.ru;
545 rpc_status = &logon_ex_op.status;
546 rpc_arg = &logon_ex_op;
547 opnum = NETR_OPNUM_SamLogonEx;
548 } else {
549 bzero(&logon_op, sizeof (struct netr_SamLogon));
550 logon_op.servername = servername;
551 logon_op.hostname = hostname;
552 logon_op.auth = &auth;
553 logon_op.ret_auth = &ret_auth;
554 logon_op.logon_info.logon_level = user_info->lg_level;
555 logon_op.logon_info.switch_value = user_info->lg_level;
556 logon_op.validation_level = NETR_VALIDATION_LEVEL3;
557 logon_info = &logon_op.logon_info.ru;
558 valid_info = &logon_op.ru;
559 rpc_status = &logon_op.status;
560 rpc_arg = &logon_op;
561 opnum = NETR_OPNUM_SamLogon;
562 }
563 heap = ndr_rpc_get_heap(netr_handle);
564
565 switch (user_info->lg_level) {
566 case NETR_INTERACTIVE_LOGON:
567 netr_setup_identity(heap, user_info, &info1.identity);
568 netr_interactive_samlogon(netr_info, user_info, &info1);
569 logon_info->info1 = &info1;
570 break;
571
572 case NETR_NETWORK_LOGON:
573 if (user_info->lg_challenge_key.len < 8 ||
574 user_info->lg_challenge_key.val == NULL) {
575 ndr_rpc_release(netr_handle);
576 return (NT_STATUS_INVALID_PARAMETER);
577 }
578 netr_setup_identity(heap, user_info, &info2.identity);
579 netr_network_samlogon(heap, netr_info, user_info, &info2);
580 logon_info->info2 = &info2;
581 break;
582
583 default:
584 ndr_rpc_release(netr_handle);
585 return (NT_STATUS_INVALID_PARAMETER);
586 }
587
588 rc = ndr_rpc_call(netr_handle, opnum, rpc_arg);
589 if (rc != 0) {
590 netr_invalidate_chain(netr_info);
591 status = NT_STATUS_INVALID_PARAMETER;
592 } else if (*rpc_status != 0) {
593 status = NT_SC_VALUE(*rpc_status);
594
595 /*
596 * We need to validate the chain even though we have
597 * a non-zero status. If the status is ACCESS_DENIED
598 * this will trigger a new credential chain. However,
599 * a valid credential is returned with some status
600 * codes; for example, WRONG_PASSWORD.
601 *
602 * SamLogonEx doesn't use authenticators - nothing to validate.
603 */
604 if (rpc_arg == &logon_op)
605 (void) netr_validate_chain(netr_info,
606 logon_op.ret_auth);
607 } else {
608 if (rpc_arg == &logon_op) {
609 status = netr_validate_chain(netr_info,
610 logon_op.ret_auth);
611 if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) {
612 ndr_rpc_release(netr_handle);
613 return (status);
614 }
615 }
616
617 info3 = valid_info->info3;
618 status = netr_setup_token(info3, user_info, netr_info, token);
619 }
620
621 ndr_rpc_release(netr_handle);
622 return (status);
623 }
624
625 /*
626 * netr_interactive_samlogon
627 *
628 * Set things up for an interactive SamLogon. Copy the NT and LM
629 * passwords to the logon structure and hash them with the session
630 * key.
631 */
632 static void
netr_interactive_samlogon(netr_info_t * netr_info,smb_logon_t * user_info,struct netr_logon_info1 * info1)633 netr_interactive_samlogon(netr_info_t *netr_info, smb_logon_t *user_info,
634 struct netr_logon_info1 *info1)
635 {
636 BYTE key[NETR_OWF_PASSWORD_SZ];
637
638 (void) memcpy(&info1->lm_owf_password,
639 user_info->lg_lm_password.val, sizeof (netr_owf_password_t));
640
641 (void) memcpy(&info1->nt_owf_password,
642 user_info->lg_nt_password.val, sizeof (netr_owf_password_t));
643
644 (void) memset(key, 0, NETR_OWF_PASSWORD_SZ);
645 (void) memcpy(key, netr_info->session_key.key,
646 netr_info->session_key.len);
647
648 rand_hash((unsigned char *)&info1->lm_owf_password,
649 NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ);
650
651 rand_hash((unsigned char *)&info1->nt_owf_password,
652 NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ);
653 }
654
655 /*
656 * netr_network_samlogon
657 *
658 * Set things up for a network SamLogon. We provide a copy of the random
659 * challenge, that we sent to the client, to the domain controller. This
660 * is the key that the client will have used to encrypt the NT and LM
661 * passwords. Note that Windows 9x clients may not provide both passwords.
662 */
663 /*ARGSUSED*/
664 static void
netr_network_samlogon(ndr_heap_t * heap,netr_info_t * netr_info,smb_logon_t * user_info,struct netr_logon_info2 * info2)665 netr_network_samlogon(ndr_heap_t *heap, netr_info_t *netr_info,
666 smb_logon_t *user_info, struct netr_logon_info2 *info2)
667 {
668 uint32_t len;
669
670 if (user_info->lg_challenge_key.len >= 8 &&
671 user_info->lg_challenge_key.val != 0) {
672 bcopy(user_info->lg_challenge_key.val,
673 info2->lm_challenge.data, 8);
674 } else {
675 bzero(info2->lm_challenge.data, 8);
676 }
677
678 if ((len = user_info->lg_nt_password.len) != 0) {
679 ndr_heap_mkvcb(heap, user_info->lg_nt_password.val, len,
680 (ndr_vcbuf_t *)&info2->nt_response);
681 } else {
682 bzero(&info2->nt_response, sizeof (netr_vcbuf_t));
683 }
684
685 if ((len = user_info->lg_lm_password.len) != 0) {
686 ndr_heap_mkvcb(heap, user_info->lg_lm_password.val, len,
687 (ndr_vcbuf_t *)&info2->lm_response);
688 } else {
689 bzero(&info2->lm_response, sizeof (netr_vcbuf_t));
690 }
691 }
692
693 /*
694 * netr_setup_authenticator
695 *
696 * Set up the request and return authenticators. A new credential is
697 * generated from the session key, the current client credential and
698 * the current time, i.e.
699 *
700 * NewCredential = Cred(SessionKey, OldCredential, time);
701 *
702 * The timestamp, which is used as a random seed, is stored in both
703 * the request and return authenticators.
704 *
705 * If any difficulties occur using the cryptographic framework, the
706 * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is
707 * returned.
708 */
709 int
netr_setup_authenticator(netr_info_t * netr_info,struct netr_authenticator * auth,struct netr_authenticator * ret_auth)710 netr_setup_authenticator(netr_info_t *netr_info,
711 struct netr_authenticator *auth, struct netr_authenticator *ret_auth)
712 {
713 int rc;
714 bzero(auth, sizeof (struct netr_authenticator));
715
716 /*
717 * Windows DCs will reject Authenticators if none of the first
718 * 5 bytes of the ClientStoredCredential are unique.
719 * Keep retrying until we've generated one that satisfies this.
720 */
721 netr_info->timestamp = time(0) - 1;
722 do {
723 auth->timestamp = ++netr_info->timestamp;
724 rc = netr_gen_credentials(netr_info->session_key.key,
725 &netr_info->client_credential,
726 netr_info->timestamp,
727 (netr_cred_t *)&auth->credential, B_TRUE);
728 if (rc != SMBAUTH_SUCCESS && rc != SMBAUTH_RETRY)
729 return (SMBAUTH_FAILURE);
730 } while (rc == SMBAUTH_RETRY);
731
732 if (ret_auth) {
733 bzero(ret_auth, sizeof (struct netr_authenticator));
734 ret_auth->timestamp = netr_info->timestamp;
735 }
736
737 return (SMBAUTH_SUCCESS);
738 }
739
740 /*
741 * Validate the returned credentials and update the credential chain.
742 * The server returns an updated client credential rather than a new
743 * server credential. The server uses (timestamp + 1) when generating
744 * the credential.
745 *
746 * Generate the new seed for the credential chain. The new seed is
747 * formed by adding (timestamp + 1) to the current client credential.
748 * The only quirk is the uint32_t style addition.
749 *
750 * Returns NT_STATUS_INSUFFICIENT_LOGON_INFO if auth->credential is a
751 * NULL pointer. The Authenticator field of the SamLogon response packet
752 * sent by the Samba 3 PDC always return NULL pointer if the received
753 * SamLogon request is not immediately followed by the ServerReqChallenge
754 * and ServerAuthenticate2 requests.
755 *
756 * Returns NT_STATUS_SUCCESS if the server returned a valid credential.
757 * Otherwise we retirm NT_STATUS_UNSUCCESSFUL.
758 */
759 uint32_t
netr_validate_chain(netr_info_t * netr_info,struct netr_authenticator * auth)760 netr_validate_chain(netr_info_t *netr_info, struct netr_authenticator *auth)
761 {
762 netr_cred_t cred;
763 uint32_t result = NT_STATUS_SUCCESS;
764 uint32_t *dwp;
765
766 ++netr_info->timestamp;
767
768 if (netr_gen_credentials(netr_info->session_key.key,
769 &netr_info->client_credential,
770 netr_info->timestamp, &cred, B_FALSE) != SMBAUTH_SUCCESS)
771 return (NT_STATUS_INTERNAL_ERROR);
772
773 if (auth == NULL) {
774 /*
775 * If the validation fails, destroy the credential chain.
776 * This should trigger a new authentication chain.
777 */
778 netr_invalidate_chain(netr_info);
779 return (NT_STATUS_INSUFFICIENT_LOGON_INFO);
780 }
781
782 result = memcmp(&cred, &auth->credential, sizeof (netr_cred_t));
783 if (result != 0) {
784 /*
785 * If the validation fails, destroy the credential chain.
786 * This should trigger a new authentication chain.
787 */
788 netr_invalidate_chain(netr_info);
789 result = NT_STATUS_UNSUCCESSFUL;
790 } else {
791 /*
792 * Otherwise generate the next step in the chain.
793 */
794 /*LINTED E_BAD_PTR_CAST_ALIGN*/
795 dwp = (uint32_t *)&netr_info->client_credential;
796 dwp[0] += netr_info->timestamp;
797
798 netr_info->flags |= NETR_FLG_VALID;
799 }
800
801 return (result);
802 }
803
804 /*
805 * netr_invalidate_chain
806 *
807 * Mark the credential chain as invalid so that it will be recreated
808 * on the next attempt.
809 */
810 static void
netr_invalidate_chain(netr_info_t * netr_info)811 netr_invalidate_chain(netr_info_t *netr_info)
812 {
813 if ((netr_info->flags & NETR_FLG_VALID) == 0)
814 return;
815
816 netr_info->flags &= ~NETR_FLG_VALID;
817 explicit_bzero(&netr_info->session_key,
818 sizeof (netr_info->session_key));
819 explicit_bzero(&netr_info->rpc_seal_key,
820 sizeof (netr_info->rpc_seal_key));
821 explicit_bzero(&netr_info->client_credential,
822 sizeof (netr_info->client_credential));
823 explicit_bzero(&netr_info->server_credential,
824 sizeof (netr_info->server_credential));
825 }
826
827 /*
828 * netr_setup_identity
829 *
830 * Set up the client identity information. All of this information is
831 * specifically related to the client user and workstation attempting
832 * to access this system. It may not be in our primary domain.
833 *
834 * I don't know what logon_id is, it seems to be a unique identifier.
835 * Increment it before each use.
836 */
837 static void
netr_setup_identity(ndr_heap_t * heap,smb_logon_t * user_info,netr_logon_id_t * identity)838 netr_setup_identity(ndr_heap_t *heap, smb_logon_t *user_info,
839 netr_logon_id_t *identity)
840 {
841 static mutex_t logon_id_mutex;
842 static uint32_t logon_id;
843
844 (void) mutex_lock(&logon_id_mutex);
845
846 if (logon_id == 0)
847 logon_id = 0xDCD0;
848
849 ++logon_id;
850 user_info->lg_logon_id = logon_id;
851
852 (void) mutex_unlock(&logon_id_mutex);
853
854 /*
855 * [MS-APDS] 3.1.5.2 "NTLM Network Logon" says to set
856 * ParameterControl to the 'E' + 'K' bits. Those are:
857 * (1 << 5) | (1 << 11), a.k.a
858 */
859 identity->parameter_control =
860 MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
861 MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
862 identity->logon_id.LowPart = logon_id;
863 identity->logon_id.HighPart = 0;
864
865 ndr_heap_mkvcs(heap, user_info->lg_domain,
866 (ndr_vcstr_t *)&identity->domain_name);
867
868 ndr_heap_mkvcs(heap, user_info->lg_username,
869 (ndr_vcstr_t *)&identity->username);
870
871 /*
872 * Some systems prefix the client workstation name with \\.
873 * It doesn't seem to make any difference whether it's there
874 * or not.
875 */
876 ndr_heap_mkvcs(heap, user_info->lg_workstation,
877 (ndr_vcstr_t *)&identity->workstation);
878 }
879
880 /*
881 * Add local and well-known group membership to the given
882 * token. Called after domain groups have been added.
883 */
884 static uint32_t
netr_setup_token_wingrps(struct netr_validation_info3 * info3 __unused,smb_token_t * token)885 netr_setup_token_wingrps(struct netr_validation_info3 *info3 __unused,
886 smb_token_t *token)
887 {
888 uint32_t status;
889
890 status = smb_sam_usr_groups(token->tkn_user.i_sid,
891 &token->tkn_win_grps);
892 if (status != NT_STATUS_SUCCESS)
893 return (status);
894
895 status = smb_wka_token_groups(token->tkn_flags, &token->tkn_win_grps);
896
897 return (status);
898 }
899
900 /*
901 * Converts groups information in the returned structure by domain controller
902 * (info3) to an internal representation (gids)
903 */
904 static uint32_t
netr_setup_domain_groups(struct netr_validation_info3 * info3,smb_ids_t * gids)905 netr_setup_domain_groups(struct netr_validation_info3 *info3, smb_ids_t *gids)
906 {
907 smb_sid_t *domain_sid;
908 smb_id_t *ids;
909 int i, total_cnt;
910
911 if ((i = info3->GroupCount) == 0)
912 i++;
913 i += info3->SidCount;
914
915 total_cnt = gids->i_cnt + i;
916
917 gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t));
918 if (gids->i_ids == NULL)
919 return (NT_STATUS_NO_MEMORY);
920
921 domain_sid = (smb_sid_t *)info3->LogonDomainId;
922
923 ids = gids->i_ids + gids->i_cnt;
924 for (i = 0; i < info3->GroupCount; i++, gids->i_cnt++, ids++) {
925 ids->i_sid = smb_sid_splice(domain_sid, info3->GroupIds[i].rid);
926 if (ids->i_sid == NULL)
927 return (NT_STATUS_NO_MEMORY);
928
929 ids->i_attrs = info3->GroupIds[i].attributes;
930 }
931
932 if (info3->GroupCount == 0) {
933 /*
934 * if there's no global group should add the primary group.
935 */
936 ids->i_sid = smb_sid_splice(domain_sid, info3->PrimaryGroupId);
937 if (ids->i_sid == NULL)
938 return (NT_STATUS_NO_MEMORY);
939
940 ids->i_attrs = 0x7;
941 gids->i_cnt++;
942 ids++;
943 }
944
945 /* Add the extra SIDs */
946 for (i = 0; i < info3->SidCount; i++, gids->i_cnt++, ids++) {
947 ids->i_sid = smb_sid_dup((smb_sid_t *)info3->ExtraSids[i].sid);
948 if (ids->i_sid == NULL)
949 return (NT_STATUS_NO_MEMORY);
950
951 ids->i_attrs = info3->ExtraSids[i].attributes;
952 }
953
954 return (NT_STATUS_SUCCESS);
955 }
956
957 /*
958 * Converts additional "resource" groups (from krb5_validation_info)
959 * into the internal representation (gids), appending to the list
960 * already put in place by netr_setup_domain_groups().
961 */
netr_setup_krb5res_groups(struct krb5_validation_info * info,smb_ids_t * gids)962 static uint32_t netr_setup_krb5res_groups(struct krb5_validation_info *info,
963 smb_ids_t *gids)
964 {
965 smb_sid_t *domain_sid;
966 smb_id_t *ids;
967 int i, total_cnt;
968
969 total_cnt = gids->i_cnt + info->rg_rid_cnt;
970
971 gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t));
972 if (gids->i_ids == NULL)
973 return (NT_STATUS_NO_MEMORY);
974
975 domain_sid = (smb_sid_t *)info->rg_dom_sid;
976
977 ids = gids->i_ids + gids->i_cnt;
978 for (i = 0; i < info->rg_rid_cnt; i++, gids->i_cnt++, ids++) {
979 ids->i_sid = smb_sid_splice(domain_sid, info->rg_rids[i].rid);
980 if (ids->i_sid == NULL)
981 return (NT_STATUS_NO_MEMORY);
982 ids->i_attrs = info->rg_rids[i].attributes;
983 }
984
985 return (0);
986 }
987