00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "NetVar.h"
00023 #include "SMB.h"
00024 #include "smb_pac.h"
00025 #include "Val.h"
00026
00027 enum SMB_Command {
00028 #define SMB_COMMAND(name, value) name = value,
00029 #include "SMB_COM.def"
00030 #undef SMB_COMMAND
00031 };
00032
00033 const char* SMB_command_name[256];
00034 StringVal* SMB_command_str[256];
00035
00036 static void init_SMB_command_name()
00037 {
00038 static int initialized = 0;
00039 if ( initialized )
00040 return;
00041
00042 initialized = 1;
00043
00044 for ( int i = 0; i < 256; ++i )
00045 {
00046 SMB_command_name[i] = "<unknown>";
00047 SMB_command_str[i] = 0;
00048 }
00049
00050 #define SMB_COMMAND(name, value) SMB_command_name[value] = #name;
00051 #include "SMB_COM.def"
00052 }
00053
00054 StringVal* get_SMB_command_str(int cmd)
00055 {
00056 if ( ! SMB_command_str[cmd] )
00057 SMB_command_str[cmd] = new StringVal(SMB_command_name[cmd]);
00058
00059 return SMB_command_str[cmd];
00060 }
00061
00062
00063 static int lookup_IPC_name(BroString* name)
00064 {
00065 static const char* IPC_pipe_names[] = {
00066 "\\locator", "\\epmapper", "\\samr", "\\lsarpc", 0
00067 };
00068
00069 for ( int i = 0; IPC_pipe_names[i]; ++i )
00070 {
00071 if ( size_t(name->Len()) == strlen(IPC_pipe_names[i]) &&
00072 strncmp((const char*) name->Bytes(),
00073 IPC_pipe_names[i], name->Len()) == 0 )
00074 return i + 1;
00075 }
00076
00077 return IPC_NONE;
00078 }
00079
00080 SMB_Session::SMB_Session(Connection* c)
00081 {
00082 conn = c;
00083 dce_rpc_session = 0;
00084 init_SMB_command_name();
00085
00086
00087
00088
00089 is_IPC = true;
00090 IPC_pipe = IPC_NONE;
00091
00092 transaction_name = 0;
00093 transaction_subcmd = 0;
00094
00095 andx(0) = andx(1) = 0;
00096 }
00097
00098 SMB_Session::~SMB_Session()
00099 {
00100 Unref(transaction_name);
00101 delete dce_rpc_session;
00102 }
00103
00104 void SMB_Session::Deliver(int is_orig, int len, const u_char* data)
00105 {
00106 if ( len == 0 )
00107 return;
00108
00109 try
00110 {
00111 const u_char *data_start = data;
00112 const u_char *data_end = data + len;
00113 BinPAC::SMB_header hdr;
00114 int hdr_len = hdr.parse(data, data_end, 1);
00115 data += hdr_len;
00116 int next_command = hdr.command();
00117 while ( data < data_end )
00118 {
00119 SMB_Body body(data, data_end);
00120 andx(is_orig) = 0;
00121 ParseMessage(is_orig, next_command, hdr, body);
00122 int next = AndxOffset(is_orig, next_command);
00123 if ( next <= 0 )
00124 break;
00125
00126 if ( data_start + next < data + body.length() )
00127 {
00128 Weird(fmt("ANDX buffer overlapping: next = %d, buffer_end = %d", next, data + body.length() - data_start));
00129 break;
00130 }
00131 data = data_start + next;
00132 }
00133 }
00134 catch ( const BinPAC::Exception& e )
00135 {
00136 conn->Weird(e.msg().c_str());
00137 }
00138 }
00139
00140 void SMB_Session::ParseMessage(int is_orig, int cmd,
00141 BinPAC::SMB_header const &hdr, SMB_Body const &body)
00142 {
00143 if ( smb_message )
00144 {
00145 val_list* vl = new val_list;
00146 StringVal* cmd_str = get_SMB_command_str(cmd);
00147 Ref(cmd_str);
00148
00149 vl->append(conn->BuildConnVal());
00150 vl->append(new Val(is_orig, TYPE_BOOL));
00151 vl->append(cmd_str);
00152 vl->append(new Val(body.length(), TYPE_COUNT));
00153
00154 conn->ConnectionEvent(smb_message, vl);
00155 }
00156
00157 if ( is_orig )
00158 req_cmd = cmd;
00159
00160 int ret = 0;
00161 switch ( cmd ) {
00162 case SMB_COM_TREE_CONNECT_ANDX:
00163 if ( is_orig )
00164 ret = ParseTreeConnectAndx(hdr, body);
00165 else
00166 ret = ParseAndx(is_orig, hdr, body);
00167 break;
00168
00169 case SMB_COM_NT_CREATE_ANDX:
00170 if ( is_orig )
00171 ret = ParseNtCreateAndx(hdr, body);
00172 else
00173 ret = ParseAndx(is_orig, hdr, body);
00174 break;
00175
00176 case SMB_COM_TRANSACTION:
00177 case SMB_COM_TRANSACTION2:
00178 case SMB_COM_TRANSACTION_SECONDARY:
00179 case SMB_COM_TRANSACTION2_SECONDARY:
00180 ret = ParseTransaction(is_orig, cmd, hdr, body);
00181 break;
00182
00183 case SMB_COM_READ_ANDX:
00184 if ( is_orig )
00185 ret = ParseReadAndx(hdr, body);
00186 else
00187 ret = ParseReadAndxResponse(hdr, body);
00188 break;
00189
00190 case SMB_COM_WRITE_ANDX:
00191 if ( is_orig )
00192 ret = ParseWriteAndx(hdr, body);
00193 else
00194 ret = ParseWriteAndxResponse(hdr, body);
00195 break;
00196
00197 case SMB_COM_NEGOTIATE:
00198 case SMB_COM_CLOSE:
00199 case SMB_COM_TREE_DISCONNECT:
00200 break;
00201
00202 case SMB_COM_LOGOFF_ANDX:
00203 case SMB_COM_SESSION_SETUP_ANDX:
00204 ret = ParseAndx(is_orig, hdr, body);
00205 break;
00206
00207 default:
00208 Weird(fmt("unknown_SMB_command(0x%x)", cmd));
00209 break;
00210 }
00211
00212 if ( ret == -1 )
00213 Weird("SMB_parsing_error");
00214 }
00215
00216 int SMB_Session::ParseAndx(int is_orig,
00217 BinPAC::SMB_header const &hdr, SMB_Body const &body)
00218 {
00219 BinPAC::SMB_generic_andx msg;
00220 msg.parse(body.data(), body.data() + body.length(), 1);
00221 if ( msg.word_count() > 0 )
00222 andx(is_orig) = msg.andx();
00223 return 0;
00224 }
00225
00226 int SMB_Session::ParseTreeConnectAndx(BinPAC::SMB_header const &hdr, SMB_Body const &body)
00227 {
00228 BinPAC::SMB_tree_connect_andx req(hdr.unicode());
00229
00230 req.parse(body.data(), body.data() + body.length(), 1);
00231 andx(1) = req.andx();
00232
00233 BroString* path = ExtractString(req.path());
00234 BroString* service = ExtractString(req.service());
00235
00236
00237 BroString* norm_path = new BroString(path->Bytes(), path->Len(), 1);
00238 norm_path->ToUpper();
00239
00240 if ( strstr_n(norm_path->Len(), norm_path->Bytes(), 5,
00241 (const u_char*) "\\IPC$") != -1 )
00242 is_IPC = true;
00243 else
00244 is_IPC = false;
00245
00246 delete norm_path;
00247
00248 if ( smb_com_tree_connect_andx )
00249 {
00250 val_list* vl = new val_list;
00251 vl->append(conn->BuildConnVal());
00252 vl->append(new StringVal(path));
00253 vl->append(new StringVal(service));
00254 conn->ConnectionEvent(smb_com_tree_connect_andx, vl);
00255 }
00256 else
00257 {
00258 delete path;
00259 delete service;
00260 }
00261
00262 return 0;
00263 }
00264
00265 int SMB_Session::ParseNtCreateAndx(BinPAC::SMB_header const &hdr, SMB_Body const &body)
00266 {
00267 BinPAC::SMB_nt_create_andx req(hdr.unicode());
00268
00269 req.parse(body.data(), body.data() + body.length(), 1);
00270 andx(1) = req.andx();
00271
00272 BroString* name = ExtractString(req.name());
00273
00274 IPC_pipe = (enum IPC_named_pipe) lookup_IPC_name(name);
00275
00276 if ( smb_com_nt_create_andx )
00277 {
00278 val_list* vl = new val_list;
00279 vl->append(conn->BuildConnVal());
00280 vl->append(new StringVal(name));
00281 conn->ConnectionEvent(smb_com_nt_create_andx, vl);
00282 }
00283 else
00284 delete name;
00285
00286 return 0;
00287 }
00288
00289 int SMB_Session::ParseReadAndx(BinPAC::SMB_header const &hdr, SMB_Body const &body)
00290 {
00291 BinPAC::SMB_read_andx req;
00292 req.parse(body.data(), body.data() + body.length(), 1);
00293 andx(1) = req.andx();
00294 return 0;
00295 }
00296
00297 int SMB_Session::ParseReadAndxResponse(BinPAC::SMB_header const &hdr, SMB_Body const &body)
00298 {
00299 BinPAC::SMB_read_andx_response resp;
00300
00301 resp.parse(body.data(), body.data() + body.length(), 1);
00302 andx(0) = resp.andx();
00303
00304 int data_count = resp.data_length();
00305 const u_char* data = resp.data().begin();
00306
00307 if ( smb_com_read_andx )
00308 {
00309 val_list* vl = new val_list;
00310 vl->append(conn->BuildConnVal());
00311 vl->append(new StringVal(data_count, (const char*) data));
00312 conn->ConnectionEvent(smb_com_read_andx, vl);
00313 }
00314
00315 CheckRPC(0, data_count, data);
00316 return 0;
00317 }
00318
00319 int SMB_Session::ParseWriteAndx(BinPAC::SMB_header const &hdr, SMB_Body const &body)
00320 {
00321 BinPAC::SMB_write_andx req;
00322 req.parse(body.data(), body.data() + body.length(), 1);
00323 andx(1) = req.andx();
00324
00325 int data_count = req.data_length();
00326 const u_char* data = req.data().begin();
00327
00328 if ( smb_com_write_andx )
00329 {
00330 val_list* vl = new val_list;
00331 vl->append(conn->BuildConnVal());
00332 vl->append(new StringVal(data_count, (const char*) data));
00333 conn->ConnectionEvent(smb_com_write_andx, vl);
00334 }
00335
00336 CheckRPC(1, data_count, data);
00337 return 0;
00338 }
00339
00340 int SMB_Session::ParseWriteAndxResponse(BinPAC::SMB_header const &hdr, SMB_Body const &body)
00341 {
00342 BinPAC::SMB_write_andx_response resp;
00343 resp.parse(body.data(), body.data() + body.length(), 1);
00344 andx(0) = resp.andx();
00345 return 0;
00346 }
00347
00348 void SMB_Session::TransactionEvent(EventHandlerPtr f, int is_orig,
00349 int data_count, const u_char* data)
00350 {
00351 if ( f )
00352 {
00353 val_list* vl = new val_list;
00354
00355 vl->append(conn->BuildConnVal());
00356 vl->append(new Val(is_orig, TYPE_BOOL));
00357 vl->append(new Val(transaction_subcmd, TYPE_COUNT));
00358 if ( transaction_name )
00359 {
00360 Ref(transaction_name);
00361 vl->append(transaction_name);
00362 }
00363 else
00364 vl->append(new StringVal(""));
00365 vl->append(new StringVal(data_count, (const char*) data));
00366 conn->ConnectionEvent(f, vl);
00367 }
00368 }
00369
00370 int SMB_Session::ParseTransaction(int is_orig, int cmd,
00371 BinPAC::SMB_header const &hdr, SMB_Body const &body)
00372 {
00373 EventHandlerPtr f = 0;
00374 if ( cmd == SMB_COM_TRANSACTION || cmd == SMB_COM_TRANSACTION_SECONDARY )
00375 f = smb_com_transaction;
00376 else if ( cmd == SMB_COM_TRANSACTION2 || cmd == SMB_COM_TRANSACTION2_SECONDARY )
00377 f = smb_com_transaction2;
00378 else
00379 internal_error("command mismatch for ParseTransaction");
00380
00381 int ret;
00382 if ( is_orig )
00383 {
00384 if ( cmd == SMB_COM_TRANSACTION ||
00385 cmd == SMB_COM_TRANSACTION2 )
00386 ret = ParseTransactionRequest(cmd, hdr, body, f);
00387 else if ( cmd == SMB_COM_TRANSACTION_SECONDARY ||
00388 cmd == SMB_COM_TRANSACTION2_SECONDARY )
00389 ret = ParseTransactionSecondaryRequest(cmd, hdr, body, f);
00390 else
00391 ret = 0;
00392 }
00393 else
00394 ret = ParseTransactionResponse(cmd, hdr, body, f);
00395
00396 return ret;
00397 }
00398
00399 int SMB_Session::ParseTransactionRequest(int cmd,
00400 BinPAC::SMB_header const &hdr,
00401 SMB_Body const &body,
00402 EventHandlerPtr f)
00403 {
00404 BinPAC::SMB_transaction trans(
00405 cmd == SMB_COM_TRANSACTION ? 1 : 2,
00406 hdr.unicode());
00407 trans.parse(body.data(), body.data() + body.length(), 1);
00408
00409 if ( transaction_name )
00410 {
00411 Unref(transaction_name);
00412 transaction_name = 0;
00413 }
00414
00415 if ( cmd == SMB_COM_TRANSACTION )
00416 transaction_name = new StringVal(
00417 ExtractString(trans.name()));
00418
00419
00420
00421 if ( trans.setup_count() > 0 )
00422 transaction_subcmd = trans.setup()[0];
00423 else
00424 Weird("transaction_subcmd_missing");
00425
00426 TransactionEvent(f, true, trans.data_count(), trans.data().begin());
00427 return ProcessTransactionData(true, cmd, hdr,
00428 trans.param_count(), trans.parameters().begin(),
00429 trans.data_count(), trans.data().begin());
00430 }
00431
00432 int SMB_Session::ParseTransactionSecondaryRequest(int cmd,
00433 BinPAC::SMB_header const &hdr,
00434 SMB_Body const &body,
00435 EventHandlerPtr f)
00436 {
00437 BinPAC::SMB_transaction_secondary trans(hdr.unicode());
00438 trans.parse(body.data(), body.data() + body.length(), 1);
00439
00440 TransactionEvent(f, true, trans.data_count(), trans.data().begin());
00441 return ProcessTransactionData(true, cmd, hdr,
00442 trans.param_count(), trans.parameters().begin(),
00443 trans.data_count(), trans.data().begin());
00444 }
00445
00446 int SMB_Session::ParseTransactionResponse(int cmd,
00447 BinPAC::SMB_header const &hdr,
00448 SMB_Body const &body,
00449 EventHandlerPtr f)
00450 {
00451 if ( body.word_count() == 0 )
00452 {
00453 TransactionEvent(f, false, 0, 0);
00454 return 0;
00455 }
00456
00457 BinPAC::SMB_transaction_response trans(hdr.unicode());
00458 trans.parse(body.data(), body.data() + body.length(), 1);
00459
00460 TransactionEvent(f, false, trans.data_count(), trans.data().begin());
00461 return ProcessTransactionData(false, cmd, hdr,
00462 trans.param_count(), trans.parameters().begin(),
00463 trans.data_count(), trans.data().begin());
00464 }
00465
00466 int SMB_Session::ProcessTransactionData(int is_orig, int cmd,
00467 BinPAC::SMB_header const &hdr,
00468 int param_count, const u_char* parameters,
00469 int data_count, const u_char* data)
00470 {
00471 if ( cmd == SMB_COM_TRANSACTION )
00472 {
00473 switch ( transaction_subcmd ) {
00474 case 0x26:
00475
00476 break;
00477 default:
00478 if ( is_orig )
00479 Weird(fmt("unknown smb_transaction subcmd: 0x%x", transaction_subcmd));
00480 break;
00481 }
00482 }
00483
00484 int ret = 0;
00485 if ( cmd == SMB_COM_TRANSACTION2 )
00486 {
00487 switch ( transaction_subcmd ) {
00488 case 0x5:
00489
00490
00491 break;
00492 case 0x7:
00493
00494
00495 break;
00496 case 0x10:
00497 if ( is_orig )
00498 ret = ParseGetDFSReferral(hdr,
00499 param_count, parameters);
00500 break;
00501
00502 default:
00503 if ( is_orig )
00504 Weird(fmt("Unknown smb_transaction2 subcmd: 0x%x", transaction_subcmd));
00505 break;
00506 }
00507 }
00508
00509 if ( data_count > 0 )
00510 {
00511 if ( CheckRPC(is_orig, data_count, data) )
00512 {
00513 if ( cmd != SMB_COM_TRANSACTION ||
00514 transaction_subcmd == 0x26 )
00515 Weird(fmt("RPC through unknown command: 0x%x/0x%x",
00516 cmd, transaction_subcmd));
00517 }
00518 }
00519
00520 return ret;
00521 }
00522
00523 int SMB_Session::ParseGetDFSReferral(BinPAC::SMB_header const &hdr,
00524 int param_count, const u_char* param)
00525 {
00526 BinPAC::SMB_get_dfs_referral req(hdr.unicode());
00527 req.parse(param, param + param_count, 1);
00528
00529 if ( smb_get_dfs_referral )
00530 {
00531 val_list* vl = new val_list;
00532 vl->append(conn->BuildConnVal());
00533 vl->append(new Val(req.max_referral_level(), TYPE_COUNT));
00534 vl->append(new StringVal(ExtractString(req.file_name())));
00535 conn->ConnectionEvent(smb_get_dfs_referral, vl);
00536 }
00537
00538 return 0;
00539 }
00540
00541 int SMB_Session::AndxOffset(int is_orig, int& next_command) const
00542 {
00543 if ( ! andx(is_orig) )
00544 return -1;
00545
00546 next_command = andx(is_orig)->command();
00547 if ( next_command != 0xff )
00548 return andx(is_orig)->offset();
00549 else
00550 return -1;
00551 }
00552
00553 void SMB_Session::Weird(const char* msg)
00554 {
00555 conn->Weird(msg);
00556 }
00557
00558
00559
00560
00561
00562 BroString* SMB_Session::ExtractString(BinPAC::SMB_string const* s)
00563 {
00564 return s->unicode() ? ExtractString(s->u()) : ExtractString(s->a());
00565 }
00566
00567 BroString* SMB_Session::ExtractString(BinPAC::SMB_ascii_string const* s)
00568 {
00569 return new BroString(s->val().begin(), s->val().size(), 1);
00570 }
00571
00572 BroString* SMB_Session::ExtractString(BinPAC::SMB_unicode_string const* s)
00573 {
00574 u_char* b = new u_char[s->s().size() + 1];
00575 int i;
00576 for ( i = 0; i < s->s().size(); ++i )
00577 {
00578 uint16 x = s->s()[i];
00579 if ( x & 0xff00 )
00580 Weird(fmt("unicode string confusion: 0x%04x", x));
00581 b[i] = u_char(x & 0xff);
00582 }
00583 b[i] = 0;
00584 return new BroString(1, b, s->s().size());
00585 }
00586
00587 bool SMB_Session::LooksLikeRPC(int len, const u_char* msg)
00588 {
00589
00590
00591
00592
00593 try
00594 {
00595 BinPAC::DCE_RPC_Header h;
00596 h.parse(msg, msg + len, 0);
00597 if ( h.rpc_vers() == 5 && h.rpc_vers_minor() == 0 )
00598 {
00599 if ( h.frag_length() == len )
00600 return true;
00601 else
00602 {
00603 DEBUG_MSG("length mismatch: %d != %d\n",
00604 h.frag_length(), len);
00605 return false;
00606 }
00607 }
00608 }
00609 catch ( const BinPAC::Exception& )
00610 {
00611
00612 }
00613
00614 return false;
00615 }
00616
00617 bool SMB_Session::CheckRPC(int is_orig, int data_count, const u_char *data)
00618 {
00619 if ( LooksLikeRPC(data_count, data) )
00620 {
00621 if ( ! dce_rpc_session )
00622 dce_rpc_session = new DCE_RPC_Session(conn);
00623
00624 dce_rpc_session->DeliverPDU(is_orig, data_count, data);
00625 return true;
00626 }
00627 return false;
00628 }
00629
00630 TCP_Contents_SMB::TCP_Contents_SMB(TCP_Endpoint* arg_endp, SMB_Session* s)
00631 : TCP_Contents(arg_endp, STOP_ON_GAP, PUNT_ON_PARTIAL)
00632 {
00633 smb_session = s;
00634 msg_buf = 0;
00635 msg_len = 0;
00636 buf_len = 0;
00637 buf_n = 0;
00638 }
00639
00640 void TCP_Contents_SMB::InitMsgBuf()
00641 {
00642 delete [] msg_buf;
00643 msg_buf = new u_char[msg_len];
00644 buf_len = msg_len;
00645 buf_n = 0;
00646 }
00647
00648 TCP_Contents_SMB::~TCP_Contents_SMB()
00649 {
00650 delete [] msg_buf;
00651 }
00652
00653 void TCP_Contents_SMB::DeliverSMB(int len, const u_char* data)
00654 {
00655
00656 if ( strncmp((const char*) data, "\xffSMB", 4) )
00657 {
00658 Conn()->Weird(fmt("SMB-over-TCP header error: %02x%02x%02x%02x, \\x%02x%c%c%c",
00659 dshdr[0], dshdr[1], dshdr[2], dshdr[3],
00660 data[0], data[1], data[2], data[3]));
00661 SetSkipDeliveries(1);
00662 }
00663 else
00664 smb_session->Deliver(IsOrig(), len, data);
00665
00666 buf_n = 0;
00667 msg_len = 0;
00668 }
00669
00670 void TCP_Contents_SMB::Deliver(int , int len, const u_char* data)
00671 {
00672 while ( len > 0 )
00673 {
00674 if ( ! msg_len )
00675 {
00676
00677 while ( buf_n < 4 && len > 0 )
00678 {
00679 dshdr[buf_n] = *data;
00680 ++buf_n; ++data; --len;
00681 }
00682
00683 if ( buf_n < 4 )
00684 return;
00685
00686 buf_n = 0;
00687 for ( int i = 1; i < 4; ++i )
00688 msg_len = ( msg_len << 8 ) + dshdr[i];
00689
00690 if ( dshdr[0] != 0 || msg_len <= 4 )
00691 {
00692 Conn()->Weird("SMB message length error");
00693 SetSkipDeliveries(1);
00694 return;
00695 }
00696 }
00697
00698 if ( buf_n == 0 && msg_len <= len )
00699 {
00700
00701
00702 int mlen = msg_len;
00703 DeliverSMB(msg_len, data);
00704 len -= mlen;
00705 data += mlen;
00706 }
00707
00708 else
00709 {
00710 if ( buf_len < msg_len )
00711 InitMsgBuf();
00712
00713 while ( buf_n < msg_len && len > 0 )
00714 {
00715 msg_buf[buf_n] = *data;
00716 ++buf_n;
00717 ++data;
00718 --len;
00719 }
00720
00721 if ( buf_n < msg_len )
00722 return;
00723
00724 DeliverSMB(msg_len, msg_buf);
00725 }
00726 }
00727 }
00728
00729 SMB_TCP_Conn::SMB_TCP_Conn(NetSessions* s, HashKey* k, double t,
00730 const ConnID* id, const struct tcphdr* tp)
00731 :TCP_Connection(s, k, t, id, tp)
00732 {
00733 o_smb = r_smb = 0;
00734 smb_session = new SMB_Session(this);
00735 }
00736
00737 SMB_TCP_Conn::~SMB_TCP_Conn()
00738 {
00739 delete smb_session;
00740 }
00741
00742 void SMB_TCP_Conn::BuildEndpoints()
00743 {
00744 o_smb = new TCP_Contents_SMB(orig, smb_session);
00745 r_smb = new TCP_Contents_SMB(resp, smb_session);
00746
00747 orig->AddContentsProcessor(o_smb);
00748 resp->AddContentsProcessor(r_smb);
00749 }