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 * NETR challenge/response client functions. 30 * 31 * NT_STATUS_INVALID_PARAMETER 32 * NT_STATUS_NO_TRUST_SAM_ACCOUNT 33 * NT_STATUS_ACCESS_DENIED 34 */ 35 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <strings.h> 39 #include <unistd.h> 40 #include <ctype.h> 41 42 #include <smbsrv/libsmb.h> 43 #include <smbsrv/libsmbrdr.h> 44 #include <smbsrv/libsmbns.h> 45 #include <smbsrv/mlsvc_util.h> 46 #include <smbsrv/ndl/netlogon.ndl> 47 #include <smbsrv/ntstatus.h> 48 #include <smbsrv/smbinfo.h> 49 #include <smbsrv/mlsvc.h> 50 #include <smbsrv/netrauth.h> 51 52 int netr_setup_authenticator(netr_info_t *, struct netr_authenticator *, 53 struct netr_authenticator *); 54 DWORD netr_validate_chain(netr_info_t *, struct netr_authenticator *); 55 56 static int netr_server_req_challenge(mlsvc_handle_t *, netr_info_t *); 57 static int netr_server_authenticate2(mlsvc_handle_t *, netr_info_t *); 58 static int netr_gen_password(BYTE *, BYTE *, BYTE *); 59 60 /* 61 * Shared with netr_logon.c 62 */ 63 netr_info_t netr_global_info; 64 65 /* 66 * netlogon_auth 67 * 68 * This is the core of the NETLOGON authentication protocol. 69 * Do the challenge response authentication. 70 * 71 * Prior to calling this function, an anonymous session to the NETLOGON 72 * pipe on a domain controller(server) should have already been opened. 73 * 74 * Upon a successful NETLOGON credential chain establishment, the 75 * netlogon sequence number will be set to match the kpasswd sequence 76 * number. 77 * 78 */ 79 DWORD 80 netlogon_auth(char *server, mlsvc_handle_t *netr_handle, DWORD flags) 81 { 82 netr_info_t *netr_info; 83 int rc; 84 DWORD leout_rc[2]; 85 86 netr_info = &netr_global_info; 87 bzero(netr_info, sizeof (netr_info_t)); 88 89 netr_info->flags |= flags; 90 91 rc = smb_getnetbiosname(netr_info->hostname, MLSVC_DOMAIN_NAME_MAX); 92 if (rc != 0) 93 return (NT_STATUS_UNSUCCESSFUL); 94 95 (void) snprintf(netr_info->server, sizeof (netr_info->server), 96 "\\\\%s", server); 97 98 LE_OUT32(&leout_rc[0], random()); 99 LE_OUT32(&leout_rc[1], random()); 100 (void) memcpy(&netr_info->client_challenge, leout_rc, 101 sizeof (struct netr_credential)); 102 103 if ((rc = netr_server_req_challenge(netr_handle, netr_info)) == 0) { 104 rc = netr_server_authenticate2(netr_handle, netr_info); 105 if (rc == 0) { 106 smb_update_netlogon_seqnum(); 107 netr_info->flags |= NETR_FLG_VALID; 108 109 } 110 } 111 112 /* 113 * The NETLOGON credential chain establishment has unset 114 * both ServerPrincipalName and dNSHostName attributes of the 115 * workstation trust account. Those attributes will be updated 116 * here to avoid any Kerberos authentication errors from happening. 117 * 118 * Later, when NT4 domain controller is supported, we need to 119 * find a way to disable the following code. 120 */ 121 if (ads_update_attrs() == -1) 122 syslog(LOG_DEBUG, "netlogon_auth: ServerPrincipalName" 123 " and dNSHostName attributes might have been unset."); 124 125 return ((rc) ? NT_STATUS_UNSUCCESSFUL : NT_STATUS_SUCCESS); 126 } 127 128 /* 129 * netr_open 130 * 131 * Open an anonymous session to the NETLOGON pipe on a domain 132 * controller and bind to the NETR RPC interface. We store the 133 * remote server's native OS type - we may need it due to 134 * differences between versions of Windows. 135 */ 136 int 137 netr_open(char *server, char *domain, mlsvc_handle_t *netr_handle) 138 { 139 int fid; 140 int remote_os = 0; 141 int remote_lm = 0; 142 int server_pdc; 143 char *user = smbrdr_ipc_get_user(); 144 145 if (mlsvc_logon(server, domain, user) != 0) 146 return (-1); 147 148 fid = mlsvc_open_pipe(server, domain, user, "\\NETLOGON"); 149 if (fid < 0) 150 return (-1); 151 152 if (mlsvc_rpc_bind(netr_handle, fid, "NETR") < 0) { 153 (void) mlsvc_close_pipe(fid); 154 return (-1); 155 } 156 157 (void) mlsvc_session_native_values(fid, &remote_os, &remote_lm, 158 &server_pdc); 159 netr_handle->context->server_os = remote_os; 160 netr_handle->context->server_pdc = server_pdc; 161 return (0); 162 } 163 164 /* 165 * netr_close 166 * 167 * Close a NETLOGON pipe and free the RPC context. 168 */ 169 int 170 netr_close(mlsvc_handle_t *netr_handle) 171 { 172 (void) mlsvc_close_pipe(netr_handle->context->fid); 173 free(netr_handle->context); 174 return (0); 175 } 176 177 /* 178 * netr_server_req_challenge 179 */ 180 static int 181 netr_server_req_challenge(mlsvc_handle_t *netr_handle, netr_info_t *netr_info) 182 { 183 struct netr_ServerReqChallenge arg; 184 mlrpc_heapref_t heap; 185 int opnum; 186 int rc; 187 188 bzero(&arg, sizeof (struct netr_ServerReqChallenge)); 189 opnum = NETR_OPNUM_ServerReqChallenge; 190 191 arg.servername = (unsigned char *)netr_info->server; 192 arg.hostname = (unsigned char *)netr_info->hostname; 193 194 (void) memcpy(&arg.client_challenge, &netr_info->client_challenge, 195 sizeof (struct netr_credential)); 196 197 (void) mlsvc_rpc_init(&heap); 198 rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap); 199 if (rc == 0) { 200 if (arg.status != 0) { 201 mlsvc_rpc_report_status(opnum, arg.status); 202 rc = -1; 203 } else { 204 (void) memcpy(&netr_info->server_challenge, 205 &arg.server_challenge, 206 sizeof (struct netr_credential)); 207 } 208 } 209 210 mlsvc_rpc_free(netr_handle->context, &heap); 211 return (rc); 212 } 213 214 /* 215 * netr_server_authenticate2 216 */ 217 static int 218 netr_server_authenticate2(mlsvc_handle_t *netr_handle, netr_info_t *netr_info) 219 { 220 struct netr_ServerAuthenticate2 arg; 221 mlrpc_heapref_t heap; 222 int opnum; 223 int rc; 224 char account_name[MLSVC_DOMAIN_NAME_MAX * 2]; 225 226 bzero(&arg, sizeof (struct netr_ServerAuthenticate2)); 227 opnum = NETR_OPNUM_ServerAuthenticate2; 228 229 (void) snprintf(account_name, sizeof (account_name), "%s$", 230 netr_info->hostname); 231 232 arg.servername = (unsigned char *)netr_info->server; 233 arg.account_name = (unsigned char *)account_name; 234 arg.account_type = NETR_WKSTA_TRUST_ACCOUNT_TYPE; 235 arg.hostname = (unsigned char *)netr_info->hostname; 236 arg.negotiate_flags = NETR_NEGOTIATE_FLAGS; 237 238 smb_tracef("server=[%s] account_name=[%s] hostname=[%s]\n", 239 netr_info->server, account_name, netr_info->hostname); 240 241 if (netr_gen_session_key(netr_info) != SMBAUTH_SUCCESS) 242 return (-1); 243 244 if (netr_gen_credentials(netr_info->session_key, 245 &netr_info->client_challenge, 246 0, 247 &netr_info->client_credential) != SMBAUTH_SUCCESS) { 248 return (-1); 249 } 250 251 if (netr_gen_credentials(netr_info->session_key, 252 &netr_info->server_challenge, 253 0, 254 &netr_info->server_credential) != SMBAUTH_SUCCESS) { 255 return (-1); 256 } 257 258 (void) memcpy(&arg.client_credential, &netr_info->client_credential, 259 sizeof (struct netr_credential)); 260 261 (void) mlsvc_rpc_init(&heap); 262 263 rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap); 264 if (rc == 0) { 265 if (arg.status != 0) { 266 mlsvc_rpc_report_status(opnum, arg.status); 267 rc = -1; 268 } else { 269 rc = memcmp(&netr_info->server_credential, 270 &arg.server_credential, 271 sizeof (struct netr_credential)); 272 } 273 } 274 275 mlsvc_rpc_free(netr_handle->context, &heap); 276 return (rc); 277 } 278 279 /* 280 * netr_gen_session_key 281 * 282 * Generate a session key from the client and server challenges. The 283 * algorithm is a two stage hash. For the first hash, the input is 284 * the combination of the client and server challenges, the key is 285 * the first 8 bytes of the password. The initial password is formed 286 * using the NT password hash on the local hostname in lower case. 287 * The result is stored in a temporary buffer. 288 * 289 * input: challenge 290 * key: passwd lower 8 bytes 291 * output: intermediate result 292 * 293 * For the second hash, the input is the result of the first hash and 294 * the key is the last 8 bytes of the password. 295 * 296 * input: result of first hash 297 * key: passwd upper 8 bytes 298 * output: session_key 299 * 300 * The final output should be the session key. 301 * 302 * FYI: smb_auth_DES(output, key, input) 303 * 304 * If any difficulties occur using the cryptographic framework, the 305 * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is 306 * returned. 307 */ 308 int 309 netr_gen_session_key(netr_info_t *netr_info) 310 { 311 unsigned char md4hash[32]; 312 unsigned char buffer[8]; 313 DWORD data[2]; 314 DWORD *client_challenge; 315 DWORD *server_challenge; 316 int rc; 317 DWORD le_data[2]; 318 319 client_challenge = (DWORD *)(uintptr_t)&netr_info->client_challenge; 320 server_challenge = (DWORD *)(uintptr_t)&netr_info->server_challenge; 321 bzero(md4hash, 32); 322 323 /* 324 * We should check (netr_info->flags & NETR_FLG_INIT) and use 325 * the appropriate password but it isn't working yet. So we 326 * always use the default one for now. 327 */ 328 bzero(netr_info->password, sizeof (netr_info->password)); 329 rc = smb_config_getstr(SMB_CI_MACHINE_PASSWD, 330 (char *)netr_info->password, sizeof (netr_info->password)); 331 332 if ((rc != SMBD_SMF_OK) || *netr_info->password == '\0') { 333 return (-1); 334 } 335 336 rc = smb_auth_ntlm_hash((char *)netr_info->password, md4hash); 337 338 if (rc != SMBAUTH_SUCCESS) 339 return (SMBAUTH_FAILURE); 340 341 data[0] = LE_IN32(&client_challenge[0]) + LE_IN32(&server_challenge[0]); 342 data[1] = LE_IN32(&client_challenge[1]) + LE_IN32(&server_challenge[1]); 343 LE_OUT32(&le_data[0], data[0]); 344 LE_OUT32(&le_data[1], data[1]); 345 346 rc = smb_auth_DES(buffer, 8, md4hash, 8, (unsigned char *)le_data, 8); 347 if (rc != SMBAUTH_SUCCESS) 348 return (rc); 349 350 rc = smb_auth_DES(netr_info->session_key, 8, &md4hash[9], 8, buffer, 8); 351 return (rc); 352 } 353 354 /* 355 * netr_gen_credentials 356 * 357 * Generate a set of credentials from a challenge and a session key. 358 * The algorithm is a two stage hash. For the first hash, the 359 * timestamp is added to the challenge and the result is stored in a 360 * temporary buffer: 361 * 362 * input: challenge (including timestamp) 363 * key: session_key 364 * output: intermediate result 365 * 366 * For the second hash, the input is the result of the first hash and 367 * a strange partial key is used: 368 * 369 * input: result of first hash 370 * key: funny partial key 371 * output: credentiails 372 * 373 * The final output should be an encrypted set of credentials. 374 * 375 * FYI: smb_auth_DES(output, key, input) 376 * 377 * If any difficulties occur using the cryptographic framework, the 378 * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is 379 * returned. 380 */ 381 int 382 netr_gen_credentials(BYTE *session_key, netr_cred_t *challenge, 383 DWORD timestamp, netr_cred_t *out_cred) 384 { 385 unsigned char buffer[8]; 386 unsigned char partial_key[8]; 387 DWORD data[2]; 388 DWORD le_data[2]; 389 DWORD *p; 390 int rc; 391 392 p = (DWORD *)(uintptr_t)challenge; 393 data[0] = LE_IN32(&p[0]) + timestamp; 394 data[1] = LE_IN32(&p[1]); 395 396 LE_OUT32(&le_data[0], data[0]); 397 LE_OUT32(&le_data[1], data[1]); 398 399 if (smb_auth_DES(buffer, 8, session_key, 8, 400 (unsigned char *)le_data, 8) != SMBAUTH_SUCCESS) 401 return (SMBAUTH_FAILURE); 402 403 bzero(partial_key, 8); 404 partial_key[0] = session_key[7]; 405 406 rc = smb_auth_DES((unsigned char *)out_cred, 8, partial_key, 8, 407 buffer, 8); 408 return (rc); 409 } 410 411 /* 412 * netr_server_password_set 413 * 414 * Attempt to change the trust account password for this system. 415 * 416 * Note that this call may legitimately fail if the registry on the 417 * domain controller has been setup to deny attempts to change the 418 * trust account password. In this case we should just continue to 419 * use the original password. 420 * 421 * Possible status values: 422 * NT_STATUS_ACCESS_DENIED 423 */ 424 int 425 netr_server_password_set(mlsvc_handle_t *netr_handle, netr_info_t *netr_info) 426 { 427 struct netr_PasswordSet arg; 428 mlrpc_heapref_t heap; 429 int opnum; 430 int rc; 431 BYTE new_password[NETR_OWF_PASSWORD_SZ]; 432 char account_name[MLSVC_DOMAIN_NAME_MAX * 2]; 433 434 bzero(&arg, sizeof (struct netr_PasswordSet)); 435 opnum = NETR_OPNUM_ServerPasswordSet; 436 437 (void) snprintf(account_name, sizeof (account_name), "%s$", 438 netr_info->hostname); 439 440 arg.servername = (unsigned char *)netr_info->server; 441 arg.account_name = (unsigned char *)account_name; 442 arg.account_type = NETR_WKSTA_TRUST_ACCOUNT_TYPE; 443 arg.hostname = (unsigned char *)netr_info->hostname; 444 445 /* 446 * Set up the client side authenticator. 447 */ 448 if (netr_setup_authenticator(netr_info, &arg.auth, 0) != 449 SMBAUTH_SUCCESS) { 450 return (-1); 451 } 452 453 /* 454 * Generate a new password from the old password. 455 */ 456 if (netr_gen_password(netr_info->session_key, 457 netr_info->password, new_password) == SMBAUTH_FAILURE) { 458 return (-1); 459 } 460 461 (void) memcpy(&arg.uas_new_password, &new_password, 462 NETR_OWF_PASSWORD_SZ); 463 464 (void) mlsvc_rpc_init(&heap); 465 rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap); 466 if ((rc != 0) || (arg.status != 0)) { 467 mlsvc_rpc_report_status(opnum, arg.status); 468 mlsvc_rpc_free(netr_handle->context, &heap); 469 return (-1); 470 } 471 472 /* 473 * Check the returned credentials. The server returns the new 474 * client credential rather than the new server credentiali, 475 * as documented elsewhere. 476 * 477 * Generate the new seed for the credential chain. Increment 478 * the timestamp and add it to the client challenge. Then we 479 * need to copy the challenge to the credential field in 480 * preparation for the next cycle. 481 */ 482 if (netr_validate_chain(netr_info, &arg.auth) == 0) { 483 /* 484 * Save the new password. 485 */ 486 (void) memcpy(netr_info->password, new_password, 487 NETR_OWF_PASSWORD_SZ); 488 } 489 490 mlsvc_rpc_free(netr_handle->context, &heap); 491 return (0); 492 } 493 494 /* 495 * netr_gen_password 496 * 497 * Generate a new pasword from the old password and the session key. 498 * The algorithm is a two stage hash. The session key is used in the 499 * first hash but only part of the session key is used in the second 500 * hash. 501 * 502 * If any difficulties occur using the cryptographic framework, the 503 * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is 504 * returned. 505 */ 506 static int 507 netr_gen_password(BYTE *session_key, BYTE *old_password, BYTE *new_password) 508 { 509 unsigned char partial_key[8]; 510 int rv; 511 512 rv = smb_auth_DES(new_password, 8, session_key, 8, old_password, 8); 513 if (rv != SMBAUTH_SUCCESS) 514 return (rv); 515 516 bzero(partial_key, 8); 517 partial_key[0] = session_key[7]; 518 519 rv = smb_auth_DES(&new_password[8], 8, partial_key, 8, 520 &old_password[8], 8); 521 return (rv); 522 } 523