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