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 */ 25 26/* 27 * This file provides some common functionality for SMB Redirector 28 * module. 29 */ 30 31#include <unistd.h> 32#include <string.h> 33#include <strings.h> 34#include <smbrdr.h> 35 36smb_log_hdl_t smbrdr_log_hdl; 37 38static DWORD smbrdr_handle_setup(smbrdr_handle_t *, unsigned char, 39 struct sdb_session *, struct sdb_logon *, struct sdb_netuse *); 40static int smbrdr_hdr_setup(smbrdr_handle_t *); 41static DWORD smbrdr_hdr_process(smbrdr_handle_t *, smb_hdr_t *); 42static int smbrdr_sign(smb_sign_ctx_t *, smb_msgbuf_t *); 43static int smbrdr_sign_chk(smb_sign_ctx_t *, smb_msgbuf_t *, unsigned char *); 44 45void smbrdr_lock_transport() { nb_lock(); } 46void smbrdr_unlock_transport() { nb_unlock(); } 47 48#pragma init(_smbrdr_init) 49 50void 51_smbrdr_init(void) 52{ 53 smbrdr_log_hdl = smb_log_create(SMBRDR_LOG_MAXCNT, SMBRDR_LOG_NAME); 54} 55 56/* 57 * smbrdr_request_init 58 * 59 * Setup a handle with given information and then 60 * setup a SMB header structure. 61 * 62 * Returns: 63 * 64 * NT_STATUS_NO_MEMORY no memory for creating request 65 * NT_STATUS_INTERNAL_ERROR header encode failed or crypto failed 66 * NT_STATUS_SUCCESS successful 67 */ 68DWORD 69smbrdr_request_init(smbrdr_handle_t *srh, 70 unsigned char cmd, 71 struct sdb_session *session, 72 struct sdb_logon *logon, 73 struct sdb_netuse *netuse) 74{ 75 DWORD status; 76 77 status = smbrdr_handle_setup(srh, cmd, session, logon, netuse); 78 if (status != NT_STATUS_SUCCESS) 79 return (status); 80 81 if (smbrdr_hdr_setup(srh) < SMB_HEADER_LEN) { 82 smbrdr_handle_free(srh); 83 return (NT_STATUS_INTERNAL_ERROR); 84 } 85 86 return (NT_STATUS_SUCCESS); 87} 88 89/* 90 * smbrdr_send 91 * 92 * Send the SMB packet pointed by the given handle over 93 * network. 94 * 95 * Returns: 96 * 97 * NT_STATUS_INTERNAL_ERROR crypto framework failure 98 * NT_STATUS_UNEXPECTED_NETWORK_ERROR send failed 99 * NT_STATUS_SUCCESS successful 100 */ 101DWORD 102smbrdr_send(smbrdr_handle_t *srh) 103{ 104 int rc; 105 106 if (smbrdr_sign(&srh->srh_session->sign_ctx, &srh->srh_mbuf) != 107 SMBAUTH_SUCCESS) { 108 smb_log(smbrdr_log_hdl, LOG_ERR, 109 "smbrdr_send[%d]: signing failed", srh->srh_cmd); 110 return (NT_STATUS_INTERNAL_ERROR); 111 } 112 113 rc = nb_send(srh->srh_session->sock, srh->srh_buf, 114 smb_msgbuf_used(&srh->srh_mbuf)); 115 116 if (rc < 0) { 117 /* 118 * Make the sequence number of the next SMB request even 119 * to avoid DC from failing the next SMB request with 120 * ACCESS_DENIED. 121 */ 122 smb_mac_dec_seqnum(&srh->srh_session->sign_ctx); 123 smb_log(smbrdr_log_hdl, LOG_ERR, 124 "smbrdr_send[%d]: send failed (%d)", srh->srh_cmd, rc); 125 return (NT_STATUS_UNEXPECTED_NETWORK_ERROR); 126 } 127 128 return (NT_STATUS_SUCCESS); 129} 130 131/* 132 * smbrdr_rcv 133 * 134 * Receive a SMB response and decode the packet header. 135 * 136 * "Implementing CIFS" book, SMB requests always have an even sequence 137 * number and replies always have an odd. 138 * 139 * With the original code, if the SMB Redirector skip the counter increment 140 * in the event of any failure during SmbSessionSetupAndX, it causes the 141 * domain controller to fail the next SMB request(odd sequence number) 142 * with ACCESS_DENIED. 143 * 144 * Smbrdr module should use the same sequence number (i.e. ssc_seqnum of the 145 * SMB Sign context) for generating the MAC signature for all incoming 146 * responses per SmbTransact request. Otherwise, the validation will fail. 147 * It is now fixed by decrementing the sequence number prior to validating 148 * the subsequent responses for a single request. 149 * 150 * Returns: 151 * 152 * status code returned by smbrdr_hdr_process() 153 * NT_STATUS_UNEXPECTED_NETWORK_ERROR receive failed 154 * NT_STATUS_SUCCESS successful 155 */ 156DWORD 157smbrdr_rcv(smbrdr_handle_t *srh, int is_first_rsp) 158{ 159 smb_hdr_t smb_hdr; 160 DWORD status; 161 int rc; 162 smb_sign_ctx_t *sign_ctx = &srh->srh_session->sign_ctx; 163 164 rc = nb_rcv(srh->srh_session->sock, srh->srh_buf, SMBRDR_REQ_BUFSZ, 0); 165 if (rc < 0) { 166 smb_mac_inc_seqnum(sign_ctx); 167 smb_log(smbrdr_log_hdl, LOG_ERR, 168 "smbrdr_rcv[%d]: receive failed (%d)", srh->srh_cmd, rc); 169 return (NT_STATUS_UNEXPECTED_NETWORK_ERROR); 170 } 171 172 smb_msgbuf_init(&srh->srh_mbuf, srh->srh_buf, rc, srh->srh_mbflags); 173 174 status = smbrdr_hdr_process(srh, &smb_hdr); 175 if (status != NT_STATUS_SUCCESS) { 176 smb_mac_inc_seqnum(sign_ctx); 177 smb_log(smbrdr_log_hdl, LOG_ERR, 178 "smbrdr_rcv[%d]: failed (%s)", srh->srh_cmd, 179 xlate_nt_status(status)); 180 return (status); 181 } 182 183 if (!is_first_rsp) 184 smb_mac_dec_seqnum(sign_ctx); 185 186 if (!smbrdr_sign_chk(sign_ctx, 187 &srh->srh_mbuf, smb_hdr.extra.extra.security_sig)) { 188 smb_log(smbrdr_log_hdl, LOG_ERR, 189 "smbrdr_rcv[%d]: bad signature", srh->srh_cmd); 190 return (NT_STATUS_INVALID_NETWORK_RESPONSE); 191 } 192 193 return (NT_STATUS_SUCCESS); 194} 195 196/* 197 * smbrdr_exchange 198 * 199 * Send the SMB packet pointed by the given handle over 200 * network. Receive the response and decode the packet header. 201 * 202 * From "Implementing CIFS" book, SMB requests always have an even sequence 203 * number and replies always have an odd. 204 * 205 * With the original code, if the SMB Redirector skips the counter increment 206 * in the event of any failure during SmbSessionSetupAndX, it causes the 207 * domain controller to fail the next SMB request(odd sequence number) 208 * with ACCESS_DENIED. 209 * 210 * Returns: 211 * 212 * status code returned by smbrdr_hdr_process() 213 * NT_STATUS_INTERNAL_ERROR crypto framework failure 214 * NT_STATUS_UNEXPECTED_NETWORK_ERROR send/receive failed 215 * NT_STATUS_SUCCESS successful 216 */ 217DWORD 218smbrdr_exchange(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr, long timeout) 219{ 220 smb_sign_ctx_t *sign_ctx; 221 smb_msgbuf_t *mb; 222 DWORD status; 223 int rc; 224 225 smbrdr_lock_transport(); 226 227 mb = &srh->srh_mbuf; 228 sign_ctx = &srh->srh_session->sign_ctx; 229 230 if (smbrdr_sign(sign_ctx, mb) != SMBAUTH_SUCCESS) { 231 smb_log(smbrdr_log_hdl, LOG_ERR, 232 "smbrdr_exchange[%d]: signing failed", srh->srh_cmd); 233 smbrdr_unlock_transport(); 234 return (NT_STATUS_INTERNAL_ERROR); 235 } 236 237 rc = nb_exchange(srh->srh_session->sock, 238 srh->srh_buf, smb_msgbuf_used(mb), 239 srh->srh_buf, SMBRDR_REQ_BUFSZ, timeout); 240 241 if (rc < 0) { 242 smb_log(smbrdr_log_hdl, LOG_ERR, 243 "smbrdr_exchange[%d]: failed (%d)", srh->srh_cmd, rc); 244 245 if (srh->srh_cmd != SMB_COM_ECHO) { 246 /* 247 * Since SMB echo is used to check the session 248 * status then don't destroy the session if it's 249 * SMB echo. 250 */ 251 srh->srh_session->state = SDB_SSTATE_STALE; 252 } 253 smb_mac_inc_seqnum(sign_ctx); 254 smbrdr_unlock_transport(); 255 return (NT_STATUS_UNEXPECTED_NETWORK_ERROR); 256 } 257 258 /* initialize for processing response */ 259 smb_msgbuf_init(mb, srh->srh_buf, rc, srh->srh_mbflags); 260 261 status = smbrdr_hdr_process(srh, smb_hdr); 262 if (status != NT_STATUS_SUCCESS) { 263 smb_log(smbrdr_log_hdl, LOG_ERR, 264 "smbrdr_exchange[%d]: failed (%s)", srh->srh_cmd, 265 xlate_nt_status(status)); 266 smb_mac_inc_seqnum(sign_ctx); 267 smbrdr_unlock_transport(); 268 return (status); 269 } 270 271 /* Signature validation */ 272 if (!smbrdr_sign_chk(sign_ctx, mb, smb_hdr->extra.extra.security_sig)) { 273 smb_log(smbrdr_log_hdl, LOG_ERR, 274 "smbrdr_exchange[%d]: bad signature", srh->srh_cmd); 275 smbrdr_unlock_transport(); 276 return (NT_STATUS_INVALID_NETWORK_RESPONSE); 277 } 278 279 smbrdr_unlock_transport(); 280 return (NT_STATUS_SUCCESS); 281} 282 283/* 284 * smbrdr_handle_free 285 * 286 * Frees the memories allocated for the given handle. 287 */ 288void 289smbrdr_handle_free(smbrdr_handle_t *srh) 290{ 291 if (srh) { 292 smb_msgbuf_term(&srh->srh_mbuf); 293 free(srh->srh_buf); 294 } 295} 296 297 298/* 299 * smbrdr_sign_init 300 * 301 * This function is called from SessionSetup and initialize the 302 * signing context for the session if the connected user isn't 303 * anonymous. This has to call before smbrdr_request_init() 304 * because it modifies smb_flags2. 305 * 306 * The following description is taken out from the "Implementing CIFS" 307 * book(pg. 304): 308 * 309 * "Once the MAC signing has been initialized within a session, all 310 * messages are numbered using the same counters and signed using 311 * the same Session Key. This is true even if additional SESSION 312 * SETUP ANDX exchanges occur." 313 * 314 * The original SMB packet signing implementation calculates a MAC 315 * key each time the SMB Redirector sends the SmbSessionSetupAndx 316 * request for any non-anonymous/non-guest user which is not desired 317 * whenever there is a change in the user session key. 318 * 319 * If NTLMv2 authentication is used, the MAC key generated for each 320 * SessionSetup is unique. Since the domain controller expects the 321 * signature of all incoming requests are signed by the same MAC key 322 * (i.e. the one that generated for the first non-anonymous SessionSetup), 323 * access denied is returned for any subsequent SmbSessionSetupAndX 324 * request. 325 */ 326int 327smbrdr_sign_init(struct sdb_session *session, struct sdb_logon *logon) 328{ 329 smb_sign_ctx_t *sign_ctx; 330 int rc = 0; 331 332 sign_ctx = &session->sign_ctx; 333 334 if ((sign_ctx->ssc_flags & SMB_SCF_REQUIRED) && 335 !(sign_ctx->ssc_flags & SMB_SCF_STARTED) && 336 (logon->type != SDB_LOGON_ANONYMOUS)) { 337 if (smb_mac_init(sign_ctx, &logon->auth) != SMBAUTH_SUCCESS) 338 return (-1); 339 340 sign_ctx->ssc_flags |= 341 (SMB_SCF_STARTED | SMB_SCF_KEY_ISSET_THIS_LOGON); 342 session->smb_flags2 |= SMB_FLAGS2_SMB_SECURITY_SIGNATURE; 343 rc = 1; 344 } 345 346 return (rc); 347} 348 349/* 350 * smbrdr_sign_fini 351 * 352 * Invalidate the MAC key if the first non-anonymous/non-guest user logon 353 * fail. 354 */ 355void 356smbrdr_sign_fini(struct sdb_session *session) 357{ 358 smb_sign_ctx_t *sign_ctx = &session->sign_ctx; 359 360 if (sign_ctx->ssc_flags & SMB_SCF_KEY_ISSET_THIS_LOGON) { 361 sign_ctx->ssc_flags &= ~SMB_SCF_STARTED; 362 sign_ctx->ssc_flags &= ~SMB_SCF_KEY_ISSET_THIS_LOGON; 363 sign_ctx->ssc_seqnum = 0; 364 } 365} 366 367/* 368 * smbrdr_sign_unset_key 369 * 370 * The SMB_SCF_KEY_ISSET_THIS_LOGON should be unset upon the successful 371 * SmbSessionSetupAndX request for the first non-anonymous/non-guest 372 * logon. 373 */ 374void 375smbrdr_sign_unset_key(struct sdb_session *session) 376{ 377 smb_sign_ctx_t *sign_ctx = &session->sign_ctx; 378 379 sign_ctx->ssc_flags &= ~SMB_SCF_KEY_ISSET_THIS_LOGON; 380} 381 382/* 383 * smbrdr_handle_setup 384 * 385 * Allocates a buffer for sending/receiving a SMB request. 386 * Initialize a smb_msgbuf structure with the allocated buffer. 387 * Setup given handle (srh) with the specified information. 388 * 389 * Returns: 390 * 391 * NT_STATUS_NO_MEMORY not enough memory 392 * NT_STATUS_SUCCESS successful 393 */ 394static DWORD 395smbrdr_handle_setup(smbrdr_handle_t *srh, 396 unsigned char cmd, 397 struct sdb_session *session, 398 struct sdb_logon *logon, 399 struct sdb_netuse *netuse) 400{ 401 srh->srh_buf = (unsigned char *)malloc(SMBRDR_REQ_BUFSZ); 402 if (srh->srh_buf == NULL) 403 return (NT_STATUS_NO_MEMORY); 404 405 bzero(srh->srh_buf, SMBRDR_REQ_BUFSZ); 406 407 srh->srh_mbflags = (session->remote_caps & CAP_UNICODE) 408 ? SMB_MSGBUF_UNICODE : 0; 409 410 smb_msgbuf_init(&srh->srh_mbuf, srh->srh_buf, 411 SMBRDR_REQ_BUFSZ, srh->srh_mbflags); 412 413 srh->srh_cmd = cmd; 414 srh->srh_session = session; 415 srh->srh_user = logon; 416 srh->srh_tree = netuse; 417 418 return (NT_STATUS_SUCCESS); 419} 420 421/* 422 * smbrdr_hdr_setup 423 * 424 * Build an SMB header based on the information in the given handle. 425 * The SMB header is described in section 3.2 of the CIFS spec. 426 * As this is a canned function, no error checking is performed here. 427 * The return value from smb_msgbuf_encode is simply returned to the caller. 428 */ 429static int 430smbrdr_hdr_setup(smbrdr_handle_t *srh) 431{ 432 static unsigned short my_pid = 0; 433 434 if (!my_pid) 435 my_pid = getpid(); 436 437 return (smb_msgbuf_encode(&srh->srh_mbuf, "Mb4.bw12.wwww", 438 srh->srh_cmd, 439 srh->srh_session->smb_flags, 440 srh->srh_session->smb_flags2, 441 (srh->srh_tree) ? srh->srh_tree->tid : 0, 442 my_pid, 443 (srh->srh_user) ? srh->srh_user->uid : 0, 444 0 /* mid */)); 445} 446 447/* 448 * Canned SMB header decode. 449 */ 450static int 451smb_decode_nt_hdr(smb_msgbuf_t *mb, smb_hdr_t *hdr) 452{ 453 return (smb_msgbuf_decode(mb, SMB_HEADER_NT_FMT, 454 &hdr->command, 455 &hdr->status.ntstatus, 456 &hdr->flags, 457 &hdr->flags2, 458 &hdr->pid_high, 459 SMB_SIG_SIZE, 460 &hdr->extra.extra.security_sig, 461 &hdr->tid, 462 &hdr->pid, 463 &hdr->uid, 464 &hdr->mid)); 465} 466 467/* 468 * smbrdr_hdr_process 469 * 470 * Assuming 'srh->srh_mbuf' contains a response from a Windows client, 471 * decodes the 32 bytes SMB header. 472 * 473 * Buffer overflow typically means that the server has more data than 474 * it could fit in the response buffer. The client can use subsequent 475 * SmbReadX requests to obtain the remaining data (KB 193839). 476 * 477 * Returns: 478 * 479 * NT_STATUS_INVALID_NETWORK_RESPONSE error decoding the header 480 * NT_STATUS_REPLY_MESSAGE_MISMATCH response doesn't match the request 481 * NT_STATUS_SUCCESS successful 482 * smb_hdr->status.ntstatus error returned by server 483 */ 484static DWORD 485smbrdr_hdr_process(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr) 486{ 487 int rc; 488 489 rc = smb_decode_nt_hdr(&srh->srh_mbuf, smb_hdr); 490 if (rc < SMB_HEADER_LEN) { 491 smb_log(smbrdr_log_hdl, LOG_DEBUG, 492 "smbrdr_hdr_process[%d]: invalid header (%d)", 493 srh->srh_cmd, rc); 494 return (NT_STATUS_INVALID_NETWORK_RESPONSE); 495 } 496 497 switch (NT_SC_VALUE(smb_hdr->status.ntstatus)) { 498 case NT_STATUS_SUCCESS: 499 case NT_STATUS_BUFFER_OVERFLOW: 500 break; 501 502 default: 503 smb_log(smbrdr_log_hdl, LOG_DEBUG, 504 "smbrdr_hdr_process[%d]: request failed (%s)", 505 srh->srh_cmd, xlate_nt_status(smb_hdr->status.ntstatus)); 506 return (smb_hdr->status.ntstatus); 507 } 508 509 if (smb_hdr->command != srh->srh_cmd) { 510 smb_log(smbrdr_log_hdl, LOG_DEBUG, 511 "smbrdr_hdr_process[%d]: reply mismatch (%d)", 512 srh->srh_cmd, smb_hdr->command); 513 return (NT_STATUS_REPLY_MESSAGE_MISMATCH); 514 } 515 516 return (NT_STATUS_SUCCESS); 517} 518 519/* 520 * smbrdr_sign 521 * 522 * Signs the given outgoing packet according to the 523 * specified signing context. 524 * 525 * The client and server each maintain an integer counter 526 * which they initialize to zero. Both counters are 527 * incremented for every SMB message - that's once for a 528 * request and once for a reply. As a result, requests sent 529 * by SMB Redirector always have an even sequence number 530 * and replies from the Windows server always have an odd 531 * number. 532 * 533 * Based on the observed Windows 2003 behavior, any SMB 534 * request will fail with NT_STATUS_ACCESS_DENIED if its 535 * sequence number is not even. 536 * 537 * The function can fail if there is trouble with the cryptographic 538 * framework and if that happens SMBAUTH_FAILURE is returned. In the 539 * normal case SMBAUTH_SUCCESS is returned. 540 */ 541static int 542smbrdr_sign(smb_sign_ctx_t *sign_ctx, smb_msgbuf_t *mb) 543{ 544 if (sign_ctx->ssc_flags & SMB_SCF_STARTED) { 545 if (sign_ctx->ssc_seqnum % 2) { 546 smb_log(smbrdr_log_hdl, LOG_DEBUG, 547 "smbrdr_sign: invalid sequence (%d)", 548 sign_ctx->ssc_seqnum); 549 } 550 if (smb_mac_sign(sign_ctx, smb_msgbuf_base(mb), 551 smb_msgbuf_used(mb)) != SMBAUTH_SUCCESS) 552 return (SMBAUTH_FAILURE); 553 sign_ctx->ssc_seqnum++; 554 } 555 return (SMBAUTH_SUCCESS); 556} 557 558 559/* 560 * smbrdr_sign_chk 561 * 562 * Validates SMB MAC signature in the in-coming message. 563 * Return 1 if the signature are match; otherwise, return 0; 564 * 565 * When packet signing is enabled, the sequence number kept in the 566 * sign_ctx structure will be incremented when a SMB request is 567 * sent and upon the receipt of the first SmbTransact response 568 * if SMB fragmentation occurs. 569 */ 570static int 571smbrdr_sign_chk(smb_sign_ctx_t *sign_ctx, smb_msgbuf_t *mb, 572 unsigned char *signature) 573{ 574 int sign_ok = 1; 575 576 if (sign_ctx->ssc_flags & SMB_SCF_STARTED) { 577 (void) memcpy(sign_ctx->ssc_sign, signature, SMB_SIG_SIZE); 578 sign_ok = smb_mac_chk(sign_ctx, smb_msgbuf_base(mb), 579 smb_msgbuf_size(mb)); 580 sign_ctx->ssc_seqnum++; 581 } 582 583 return (sign_ok); 584} 585