text: # This is an original Core Protocol command.
  #
  # This command is used to initiate an SMB connection between the
  # client and the server. An SMB_COM_NEGOTIATE exchange MUST be
  # completed before any other SMB messages are sent to the server.
  #
  # There MUST be only one SMB_COM_NEGOTIATE exchange per SMB
  # connection. Subsequent SMB_COM_NEGOTIATE requests received by the
  # server MUST be rejected with error responses. The server MUST NOT
  # take any other action.
  
  refine connection SMB_Conn += {
  
  	function proc_smb1_negotiate_request(header: SMB_Header, val: SMB1_negotiate_request): bool
  		%{
  		if ( smb1_negotiate_request )
  			{
  			auto dialects = zeek::make_intrusive<zeek::VectorVal>(zeek::id::string_vec);
  
  			for ( unsigned int i = 0; i < ${val.dialects}->size(); ++i )
  				{
  				auto dia = smb_string2stringval((*${val.dialects})[i]->name());
  				dialects->Assign(i, std::move(dia));
  				}
  
  			zeek::BifEvent::enqueue_smb1_negotiate_request(zeek_analyzer(), zeek_analyzer()->Conn(),
  			                                         SMBHeaderVal(header),
  			                                         std::move(dialects));
  			}
  
  		return true;
  		%}
  
  	function proc_smb1_negotiate_response(header: SMB_Header, val: SMB1_negotiate_response): bool
  		%{
  		if ( smb1_negotiate_response )
  			{
  			auto response = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateResponse);
  
  			switch ( ${val.word_count} )
  				{
  				case 0x01:
  					{
  					auto core = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateResponseCore);
  					core->Assign(0, ${val.dialect_index});
  
  					response->Assign(0, std::move(core));
  					}
  					break;
  
  				case 0x0d:
  					{
  					auto security = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateResponseSecurity);
  					security->Assign(0, ${val.lanman.security_user_level});
  					security->Assign(1, ${val.lanman.security_challenge_response});
  
  					auto raw = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateRawMode);
  					raw->Assign(0, ${val.lanman.raw_read_supported});
  					raw->Assign(1, ${val.lanman.raw_write_supported});
  
  					auto lanman = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateResponseLANMAN);
  					lanman->Assign(0, ${val.word_count});
  					lanman->Assign(1, ${val.dialect_index});
  					lanman->Assign(2, std::move(security));
  					lanman->Assign(3, ${val.lanman.max_buffer_size});
  					lanman->Assign(4, ${val.lanman.max_mpx_count});
  
  					lanman->Assign(5, ${val.lanman.max_number_vcs});
  					lanman->Assign(6, std::move(raw));
  					lanman->Assign(7, ${val.lanman.session_key});
  					lanman->Assign(8, time_from_lanman(${val.lanman.server_time}, ${val.lanman.server_date}, ${val.lanman.server_tz}));
  					lanman->Assign(9, to_stringval(${val.lanman.encryption_key}));
  
  					lanman->Assign(10, smb_string2stringval(${val.lanman.primary_domain}));
  
  					response->Assign(1, std::move(lanman));
  					}
  					break;
  
  				case 0x11:
  					{
  					auto security = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateResponseSecurity);
  					security->Assign(0, ${val.ntlm.security_user_level});
  					security->Assign(1, ${val.ntlm.security_challenge_response});
  					security->Assign(2, ${val.ntlm.security_signatures_enabled});
  					security->Assign(3, ${val.ntlm.security_signatures_required});
  
  					auto capabilities = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateCapabilities);
  					capabilities->Assign(0, ${val.ntlm.capabilities_raw_mode});
  					capabilities->Assign(1, ${val.ntlm.capabilities_mpx_mode});
  					capabilities->Assign(2, ${val.ntlm.capabilities_unicode});
  					capabilities->Assign(3, ${val.ntlm.capabilities_large_files});
  					capabilities->Assign(4, ${val.ntlm.capabilities_nt_smbs});
  
  					capabilities->Assign(5, ${val.ntlm.capabilities_rpc_remote_apis});
  					capabilities->Assign(6, ${val.ntlm.capabilities_status32});
  					capabilities->Assign(7, ${val.ntlm.capabilities_level_2_oplocks});
  					capabilities->Assign(8, ${val.ntlm.capabilities_lock_and_read});
  					capabilities->Assign(9, ${val.ntlm.capabilities_nt_find});
  
  					capabilities->Assign(10, ${val.ntlm.capabilities_dfs});
  					capabilities->Assign(11, ${val.ntlm.capabilities_infolevel_passthru});
  					capabilities->Assign(12, ${val.ntlm.capabilities_large_readx});
  					capabilities->Assign(13, ${val.ntlm.capabilities_large_writex});
  					capabilities->Assign(14, ${val.ntlm.capabilities_unix});
  
  					capabilities->Assign(15, ${val.ntlm.capabilities_bulk_transfer});
  					capabilities->Assign(16, ${val.ntlm.capabilities_compressed_data});
  					capabilities->Assign(17, ${val.ntlm.capabilities_extended_security});
  
  					auto ntlm = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateResponseNTLM);
  					ntlm->Assign(0, ${val.word_count});
  					ntlm->Assign(1, ${val.dialect_index});
  					ntlm->Assign(2, std::move(security));
  					ntlm->Assign(3, ${val.ntlm.max_buffer_size});
  					ntlm->Assign(4, ${val.ntlm.max_mpx_count});
  
  					ntlm->Assign(5, ${val.ntlm.max_number_vcs});
  					ntlm->Assign(6, ${val.ntlm.max_raw_size});
  					ntlm->Assign(7, ${val.ntlm.session_key});
  					ntlm->Assign(8, std::move(capabilities));
  					ntlm->Assign(9, filetime2zeektime(${val.ntlm.server_time}));
  
  					if ( ${val.ntlm.capabilities_extended_security} == false )
  						{
  						ntlm->Assign(10, to_stringval(${val.ntlm.encryption_key}));
  						ntlm->Assign(11, smb_string2stringval(${val.ntlm.domain_name}));
  						}
  					else
  						{
  						ntlm->Assign(12, to_stringval(${val.ntlm.server_guid}));
  						}
  
  					response->Assign(2, std::move(ntlm));
  					}
  					break;
  				}
  			zeek::BifEvent::enqueue_smb1_negotiate_response(zeek_analyzer(),
  			                                          zeek_analyzer()->Conn(),
  			                                          SMBHeaderVal(header),
  			                                          std::move(response));
  			}
  
  		return true;
  		%}
  };
  
  type SMB_dialect = record {
  	buffer_format  : uint8; # must be 0x2 for dialect
  	name           : SMB_string(0,0);
  };
  
  type SMB1_negotiate_request(header: SMB_Header) = record {
  	word_count: uint8;	# must be 0
  	byte_count: uint16;
  	dialects:   SMB_dialect[] &length=byte_count;
  } &let {
  	proc : bool = $context.connection.proc_smb1_negotiate_request(header, this);
  };
  
  type SMB1_negotiate_response(header: SMB_Header) = record {
  	word_count:    uint8;
  	dialect_index: uint16;
  	response:      case word_count of {
  		0x01	-> core   : SMB1_negotiate_core_response;
  		0x0d	-> lanman : SMB1_negotiate_lanman_response(header);
  		0x11	-> ntlm   : SMB1_negotiate_ntlm_response(header);
  	};
  } &let {
  	proc: bool = $context.connection.proc_smb1_negotiate_response(header, this);
  };
  
  type SMB1_negotiate_core_response = record {
  	byte_count: uint16;
  };
  
  type SMB1_negotiate_lanman_response(header: SMB_Header) = record {
  	security_flags        : uint16; # expanded in &let
  	max_buffer_size       : uint16;
  	max_mpx_count         : uint16;
  	max_number_vcs        : uint16;
  	raw_mode              : uint16; # expanded in &let
  	session_key           : uint32;
  	server_time           : SMB_time;
  	server_date           : SMB_date;
  	server_tz             : uint16;
  	encryption_key_length : uint16;
  	reserved              : uint16; # must be zero
  	byte_count            : uint16; # count of data bytes
  	encryption_key        : bytestring &length=encryption_key_length;
  	primary_domain        : SMB_string(header.unicode, offsetof(primary_domain));
  } &let {
  	security_user_level         : bool = ( security_flags & 0x1 ) > 0;
  	security_challenge_response : bool = ( security_flags & 0x2 ) > 0;
  	raw_read_supported          : bool = ( raw_mode & 0x1 ) > 0;
  	raw_write_supported         : bool = ( raw_mode & 0x2 ) > 0;
  };
  
  type SMB1_negotiate_ntlm_response(header: SMB_Header) = record {
  	security_flags        : uint8;  # Expanded in &let
  	max_mpx_count         : uint16;
  	max_number_vcs        : uint16;
  	max_buffer_size       : uint32;
  	max_raw_size          : uint32;
  	session_key           : uint32;
  	capabilities          : uint32; # Expanded in &let
  	server_time           : uint64;
  	server_tz             : uint16;
  	encryption_key_length : uint8;
  	byte_count            : uint16;
  	encryption_key_present: case capabilities_extended_security of {
  		false	-> encryption_key : bytestring &length=encryption_key_length;
  		true	-> no_key         : empty;
  	} &requires(capabilities_extended_security);
  	domain_name_present: case capabilities_extended_security of {
  		false	-> domain_name : SMB_string(header.unicode, offsetof(domain_name_present));
  		true	-> no_name     : empty;
  	} &requires(capabilities_extended_security);
  	server_guid_present: case capabilities_extended_security of {
  		true	-> server_guid : bytestring &length=16;
  		false	-> no_guid     : empty;
  	} &requires(capabilities_extended_security);
  	security_blob_present: case capabilities_extended_security of {
  		true	-> security_blob : bytestring &length=(byte_count-16);
  		false	-> no_blob       : empty;
  	} &requires(capabilities_extended_security);
  } &let {
  	security_user_level             : bool = (security_flags & 0x1) > 0;
  	security_challenge_response     : bool = (security_flags & 0x2) > 0;
  	security_signatures_enabled     : bool = (security_flags & 0x4) > 0;
  	security_signatures_required    : bool = (security_flags & 0x8) > 0;
  	capabilities_raw_mode           : bool = (capabilities & 0x1) > 0;
  	capabilities_mpx_mode           : bool = (capabilities & 0x2) > 0;
  	capabilities_unicode            : bool = (capabilities & 0x4) > 0;
  	capabilities_large_files        : bool = (capabilities & 0x8) > 0;
  	capabilities_nt_smbs            : bool = (capabilities & 0x10) > 0;
  	capabilities_rpc_remote_apis    : bool = (capabilities & 0x20) > 0;
  	capabilities_status32           : bool = (capabilities & 0x40) > 0;
  	capabilities_level_2_oplocks    : bool = (capabilities & 0x80) > 0;
  	capabilities_lock_and_read      : bool = (capabilities & 0x100) > 0;
  	capabilities_nt_find            : bool = (capabilities & 0x200) > 0;
  	capabilities_dfs                : bool = (capabilities & 0x1000) > 0;
  	capabilities_infolevel_passthru : bool = (capabilities & 0x2000) > 0;
  	capabilities_large_readx        : bool = (capabilities & 0x4000) > 0;
  	capabilities_large_writex       : bool = (capabilities & 0x8000) > 0;
  	capabilities_unix               : bool = (capabilities & 0x00800000) > 0;
  	capabilities_reserved           : bool = (capabilities & 0x02000000) > 0;
  	capabilities_bulk_transfer      : bool = (capabilities & 0x20000000) > 0;
  	capabilities_compressed_data    : bool = (capabilities & 0x40000000) > 0;
  	capabilities_extended_security  : bool = (capabilities & 0x80000000) > 0;
  
  	gssapi_proc : bool = $context.connection.forward_gssapi(security_blob, false) &if(capabilities_extended_security);
  };
  