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