/*************************************************************************** * EchoHeader.cc -- The EchoHeader Class represents packets of the Nping * * Echo Protocol. It contains the appropriate methods to set/get all * * header fields. In general these methods do error checking and perform * * byte order conversions. * * * ***********************IMPORTANT NMAP LICENSE TERMS************************ * * * The Nmap Security Scanner is (C) 1996-2013 Insecure.Com LLC. Nmap is * * also a registered trademark of Insecure.Com LLC. This program is free * * software; you may redistribute and/or modify it under the terms of the * * GNU General Public License as published by the Free Software * * Foundation; Version 2 ("GPL"), BUT ONLY WITH ALL OF THE CLARIFICATIONS * * AND EXCEPTIONS DESCRIBED HEREIN. This guarantees your right to use, * * modify, and redistribute this software under certain conditions. If * * you wish to embed Nmap technology into proprietary software, we sell * * alternative licenses (contact sales@insecure.com). Dozens of software * * vendors already license Nmap technology such as host discovery, port * * scanning, OS detection, version detection, and the Nmap Scripting * * Engine. * * * * Note that the GPL places important restrictions on "derivative works", * * yet it does not provide a detailed definition of that term. To avoid * * misunderstandings, we interpret that term as broadly as copyright law * * allows. For example, we consider an application to constitute a * * derivative work for the purpose of this license if it does any of the * * following with any software or content covered by this license * * ("Covered Software"): * * * * o Integrates source code from Covered Software. * * * * o Reads or includes copyrighted data files, such as Nmap's nmap-os-db * * or nmap-service-probes. * * * * o Is designed specifically to execute Covered Software and parse the * * results (as opposed to typical shell or execution-menu apps, which will * * execute anything you tell them to). * * * * o Includes Covered Software in a proprietary executable installer. The * * installers produced by InstallShield are an example of this. Including * * Nmap with other software in compressed or archival form does not * * trigger this provision, provided appropriate open source decompression * * or de-archiving software is widely available for no charge. For the * * purposes of this license, an installer is considered to include Covered * * Software even if it actually retrieves a copy of Covered Software from * * another source during runtime (such as by downloading it from the * * Internet). * * * * o Links (statically or dynamically) to a library which does any of the * * above. * * * * o Executes a helper program, module, or script to do any of the above. * * * * This list is not exclusive, but is meant to clarify our interpretation * * of derived works with some common examples. Other people may interpret * * the plain GPL differently, so we consider this a special exception to * * the GPL that we apply to Covered Software. Works which meet any of * * these conditions must conform to all of the terms of this license, * * particularly including the GPL Section 3 requirements of providing * * source code and allowing free redistribution of the work as a whole. * * * * As another special exception to the GPL terms, Insecure.Com LLC grants * * permission to link the code of this program with any version of the * * OpenSSL library which is distributed under a license identical to that * * listed in the included docs/licenses/OpenSSL.txt file, and distribute * * linked combinations including the two. * * * * Any redistribution of Covered Software, including any derived works, * * must obey and carry forward all of the terms of this license, including * * obeying all GPL rules and restrictions. For example, source code of * * the whole work must be provided and free redistribution must be * * allowed. All GPL references to "this License", are to be treated as * * including the special and conditions of the license text as well. * * * * Because this license imposes special exceptions to the GPL, Covered * * Work may not be combined (even as part of a larger work) with plain GPL * * software. The terms, conditions, and exceptions of this license must * * be included as well. This license is incompatible with some other open * * source licenses as well. In some cases we can relicense portions of * * Nmap or grant special permissions to use it in other open source * * software. Please contact fyodor@nmap.org with any such requests. * * Similarly, we don't incorporate incompatible open source software into * * Covered Software without special permission from the copyright holders. * * * * If you have any questions about the licensing restrictions on using * * Nmap in other works, are happy to help. As mentioned above, we also * * offer alternative license to integrate Nmap into proprietary * * applications and appliances. These contracts have been sold to dozens * * of software vendors, and generally include a perpetual license as well * * as providing for priority support and updates. They also fund the * * continued development of Nmap. Please email sales@insecure.com for * * further information. * * * * If you received these files with a written license agreement or * * contract stating terms other than the terms above, then that * * alternative license agreement takes precedence over these comments. * * * * Source is provided to this software because we believe users have a * * right to know exactly what a program is going to do before they run it. * * This also allows you to audit the software for security holes (none * * have been found so far). * * * * Source code also allows you to port Nmap to new platforms, fix bugs, * * and add new features. You are highly encouraged to send your changes * * to the dev@nmap.org mailing list for possible incorporation into the * * main distribution. By sending these changes to Fyodor or one of the * * Insecure.Org development mailing lists, or checking them into the Nmap * * source code repository, it is understood (unless you specify otherwise) * * that you are offering the Nmap Project (Insecure.Com LLC) the * * unlimited, non-exclusive right to reuse, modify, and relicense the * * code. Nmap will always be available Open Source, but this is important * * because the inability to relicense code has caused devastating problems * * for other Free Software projects (such as KDE and NASM). We also * * occasionally relicense the code to third parties as discussed above. * * If you wish to specify special license conditions of your * * contributions, just say so when you send them. * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Nmap * * license file for more details (it's in a COPYING file included with * * Nmap, and also available from https://svn.nmap.org/nmap/COPYING * * * ***************************************************************************/ #include "EchoHeader.h" #include "nping.h" #include "output.h" #include #include #include "Crypto.h" EchoHeader::EchoHeader(){ this->reset(); } /* End of EchoHeader constructor */ EchoHeader::~EchoHeader(){ } /* End of EchoHeader destructor */ /** Sets every attribute to its default value. */ void EchoHeader::reset() { memset(&this->h, 0, sizeof(echohdr_t) ); this->data_hsserv=(nep_hs_serv_data_t *)this->h.data; this->data_hsclnt=(nep_hs_clnt_data_t *)this->h.data; this->data_hsfinal=(nep_hs_final_data_t *)this->h.data; this->data_pspec=(nep_packet_spec_data_t *)this->h.data; this->data_ready=(nep_ready_data_t *)this->h.data; this->data_echo=(nep_echo_data_t *)this->h.data; this->data_error=(nep_error_data_t *)this->h.data; this->fs_off=(u8 *)this->data_pspec->packetspec; this->fs_bytes=0; this->echo_mac=(u8 *)this->data_echo->payload_and_mac; this->echo_bytes=0; /* Some safe initilizations */ this->setVersion(ECHO_CURRENT_PROTO_VER); this->setTotalLength(STD_NEP_HEADER_LEN + MAC_LENGTH); this->length=STD_NEP_HEADER_LEN + MAC_LENGTH; /* Sets length in PacketElement superclass */ } /* End of reset() */ /** @warning This method is essential for the superclass getBinaryBuffer() * method to work. Do NOT change a thing unless you know what you're doing. */ u8 * EchoHeader::getBufferPointer(){ return (u8*)(&h); } /* End of getBufferPointer() */ /** Stores supplied packet in the internal buffer so the information * can be accessed using the standard get & set methods. */ int EchoHeader::storeRecvData(const u8 *buf, size_t len){ if(buf==NULL || len>(STD_NEP_HEADER_LEN+MAX_DATA_LEN)){ return OP_FAILURE; }else{ this->reset(); /* Re-init the object, just in case the caller had used it already */ this->length=len; memcpy(&(this->h), buf, len); } return OP_SUCCESS; } /* End of storeRecvData() */ /* Returns a protocol identifier. This is used by packet parsing funtions * that return linked lists of PacketElement objects, to determine the protocol * the object represents. */ int EchoHeader::protocol_id() const { return HEADER_TYPE_NEP; } /* End of protocol_id() */ /** Sets Version. * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ int EchoHeader::setVersion(u8 val){ this->h.echo_ver=val; return OP_SUCCESS; } /* End of setVersion() */ /** Returns value of attribute h.echo_ver */ u8 EchoHeader::getVersion(){ return this->h.echo_ver; } /* End of getVersion() */ /** Sets MessageType. * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ int EchoHeader::setMessageType(u8 val){ this->h.echo_mtype=val; return OP_SUCCESS; } /* End of setMessageType() */ /** Returns value of attribute h.echo_mtype */ u8 EchoHeader::getMessageType(){ return this->h.echo_mtype; } /* End of getsetMessageType() */ /** Sets Total Length. * @return OP_SUCCESS on success and OP_FAILURE in case of error. * @warning the length is expressed in 32bit words. */ int EchoHeader::setTotalLength(u16 val){ this->h.echo_tlen=htons(val); this->length=val*4; /* Also, set superclass length attribute */ return OP_SUCCESS; } /* End of setTotalLength() */ /** Sets Total Length. * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ int EchoHeader::setTotalLength(){ switch( this->getMessageType() ){ case TYPE_NEP_HANDSHAKE_SERVER: this->setTotalLength(NEP_HANDSHAKE_SERVER_LEN/4); break; case TYPE_NEP_HANDSHAKE_CLIENT: this->setTotalLength(NEP_HANDSHAKE_CLIENT_LEN/4); break; case TYPE_NEP_HANDSHAKE_FINAL: this->setTotalLength(NEP_HANDSHAKE_FINAL_LEN/4); break; case TYPE_NEP_PACKET_SPEC: this->setTotalLength(NEP_PACKETSPEC_LEN/4); break; case TYPE_NEP_READY: this->setTotalLength(NEP_READY_LEN/4); break; case TYPE_NEP_ECHO: this->setTotalLength( (STD_NEP_HEADER_LEN + 4 + MAC_LENGTH + this->echo_bytes)/4 ); break; case TYPE_NEP_ERROR: this->setTotalLength(NEP_ERROR_LEN/4); break; default: return OP_FAILURE; break; } return OP_SUCCESS; } /* End of setTotalLength() */ /** Returns value of attribute h.echo_tlen * @warning Returned length is expressed in 32bit words. To get a byte count * it must be multiplied by four */ u16 EchoHeader::getTotalLength(){ return ntohs(this->h.echo_tlen); } /* End of getTotalLength() */ /** Sets SequenceNumber. * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ int EchoHeader::setSequenceNumber(u32 val){ this->h.echo_seq=htonl(val); return OP_SUCCESS; } /* End of setSequenceNumber() */ /** Returns value of attribute h.echo_seq */ u32 EchoHeader::getSequenceNumber(){ return ntohl(this->h.echo_seq); } /* End of getSequenceNumber() */ /** Sets Timestamp. * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ int EchoHeader::setTimestamp(u32 val){ this->h.echo_ts=htonl(val); return OP_SUCCESS; } /* End of setTimestamp() */ /** Sets Timestamp. * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ int EchoHeader::setTimestamp(){ u32 t=(u32)time(NULL); /* TODO: Make sure this does not cause problems */ this->h.echo_ts=htonl(t); return OP_SUCCESS; } /* End of setTimestamp() */ /** Returns value of attribute h.echo_ts*/ u32 EchoHeader::getTimestamp(){ return ntohl(this->h.echo_ts); } /* End of getTimestamp() */ /** Sets Reserved. * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ int EchoHeader::setReserved(u32 val){ this->h.echo_res=htonl(val); return OP_SUCCESS; } /* End of setReserved() */ /** Returns value of attribute h.echo_res */ u32 EchoHeader::getReserved(){ return this->h.echo_res; } /* End of getReserved() */ int EchoHeader::setMessageAuthenticationCode(u8 *key, size_t keylen){ u8 *macpnt=NULL; u8 *from=(u8 *)&(this->h); size_t bytes=0; /* Determine where the MAC field is and the length of the data that needs * to be authenticated, based on message type. */ switch( this->getMessageType() ){ case TYPE_NEP_HANDSHAKE_SERVER: macpnt=this->data_hsserv->mac; bytes=NEP_HANDSHAKE_SERVER_LEN-MAC_LENGTH; break; case TYPE_NEP_HANDSHAKE_CLIENT: macpnt=this->data_hsclnt->mac; bytes=NEP_HANDSHAKE_CLIENT_LEN-MAC_LENGTH; break; case TYPE_NEP_HANDSHAKE_FINAL: macpnt=this->data_hsfinal->mac; bytes=NEP_HANDSHAKE_FINAL_LEN-MAC_LENGTH; break; case TYPE_NEP_PACKET_SPEC: macpnt=this->data_pspec->mac; bytes=NEP_PACKETSPEC_LEN-MAC_LENGTH; break; case TYPE_NEP_READY: macpnt=this->data_ready->mac; bytes=NEP_READY_LEN-MAC_LENGTH; break; case TYPE_NEP_ECHO: macpnt=this->echo_mac; bytes=STD_NEP_HEADER_LEN + 4 + this->echo_bytes; break; case TYPE_NEP_ERROR: macpnt=this->data_error->mac; bytes=NEP_ERROR_LEN-MAC_LENGTH; break; default: return OP_FAILURE; break; } /* Compute the code */ Crypto::hmac_sha256(from, bytes, macpnt, key, keylen); return OP_SUCCESS; } /* End of setMessageAuthenticationCode() */ u8 *EchoHeader::getMessageAuthenticationCode(){ switch( this->getMessageType() ){ case TYPE_NEP_HANDSHAKE_SERVER: return this->data_hsserv->mac; break; case TYPE_NEP_HANDSHAKE_CLIENT: return this->data_hsclnt->mac; break; case TYPE_NEP_HANDSHAKE_FINAL: return this->data_hsfinal->mac; break; case TYPE_NEP_PACKET_SPEC: return this->data_pspec->mac; break; case TYPE_NEP_READY: return this->data_ready->mac; break; case TYPE_NEP_ECHO: this->updateEchoInternals(); return this->echo_mac; break; case TYPE_NEP_ERROR: return this->data_error->mac; break; default: return NULL; break; } return NULL; } /* End of getMessageAuthenticationCode() */ int EchoHeader::verifyMessageAuthenticationCode(u8 *key, size_t keylen){ u8 mac_backup[MAC_LENGTH]; u8 *aux; /* Make a copy of the current MAC */ if( (aux=this->getMessageAuthenticationCode())==NULL ) return OP_FAILURE; memcpy(mac_backup, aux, MAC_LENGTH); /* Recompute the MAC */ memset(aux, 0, MAC_LENGTH); this->setMessageAuthenticationCode(key, keylen); /* Try to match both MACs*/ if( (aux=this->getMessageAuthenticationCode())==NULL ) return OP_FAILURE; if( memcmp(mac_backup, aux, MAC_LENGTH)==0 ){ return OP_SUCCESS; }else{ /* Restore original MAC */ memcpy(aux, mac_backup, MAC_LENGTH); return OP_FAILURE; } } /* End of verifyMessageAuthenticationCode() */ /******************************************************************************/ /* NEP_HANDSHAKE methods */ /******************************************************************************/ int EchoHeader::setServerNonce(u8 *nonce){ assert(nonce); switch( this->getMessageType() ){ case TYPE_NEP_HANDSHAKE_SERVER: memcpy(this->data_hsserv->server_nonce, nonce, NONCE_LEN); break; case TYPE_NEP_HANDSHAKE_CLIENT: memcpy(this->data_hsclnt->server_nonce, nonce, NONCE_LEN); break; default: return OP_FAILURE; break; } return OP_SUCCESS; } /* End of getServerNonce() */ u8 *EchoHeader::getServerNonce(){ switch( this->getMessageType() ){ case TYPE_NEP_HANDSHAKE_SERVER: return this->data_hsserv->server_nonce; break; case TYPE_NEP_HANDSHAKE_CLIENT: return this->data_hsclnt->server_nonce; break; default: return NULL; break; } } /* End of getServerNonce() */ int EchoHeader::setClientNonce(u8 *nonce){ assert(nonce); switch( this->getMessageType() ){ case TYPE_NEP_HANDSHAKE_CLIENT: memcpy(this->data_hsclnt->client_nonce, nonce, NONCE_LEN); break; case TYPE_NEP_HANDSHAKE_FINAL: memcpy(this->data_hsfinal->client_nonce , nonce, NONCE_LEN); break; default: return OP_FAILURE; break; } return OP_SUCCESS; } /* End of getClientNonce() */ u8 *EchoHeader::getClientNonce(){ switch( this->getMessageType() ){ case TYPE_NEP_HANDSHAKE_CLIENT: return this->data_hsclnt->client_nonce; break; case TYPE_NEP_HANDSHAKE_FINAL: return this->data_hsfinal->client_nonce; break; default: return NULL; break; } } /* End of getClientNonce() */ int EchoHeader::setPartnerAddress(struct in_addr val){ switch( this->getMessageType() ){ case TYPE_NEP_HANDSHAKE_CLIENT: memset(this->data_hsclnt->partner_ip, 0, 16); memcpy(this->data_hsclnt->partner_ip , &val, sizeof(struct in_addr)); break; case TYPE_NEP_HANDSHAKE_FINAL: memset(this->data_hsfinal->partner_ip, 0, 16); memcpy(this->data_hsfinal->partner_ip , &val, sizeof(struct in_addr)); break; default: return OP_FAILURE; break; } this->setIPVersion(0x04); return OP_SUCCESS; } /* End of setPartnerAddress() */ int EchoHeader::setPartnerAddress(struct in6_addr val){ switch( this->getMessageType() ){ case TYPE_NEP_HANDSHAKE_CLIENT: memset(this->data_hsclnt->partner_ip, 0, 16); memcpy(this->data_hsclnt->partner_ip , &val, sizeof(struct in6_addr)); break; case TYPE_NEP_HANDSHAKE_FINAL: memset(this->data_hsfinal->partner_ip, 0, 16); memcpy(this->data_hsfinal->partner_ip , &val, sizeof(struct in6_addr)); break; default: return OP_FAILURE; break; } this->setIPVersion(0x06); return OP_SUCCESS; } /* End of setPartnerAddress() */ int EchoHeader::getPartnerAddress(struct in_addr *dst){ switch( this->getMessageType() ){ case TYPE_NEP_HANDSHAKE_CLIENT: memcpy(dst, this->data_hsclnt->partner_ip,sizeof(struct in_addr)); break; case TYPE_NEP_HANDSHAKE_FINAL: memcpy(dst, this->data_hsfinal->partner_ip,sizeof(struct in_addr)); break; default: return OP_FAILURE; break; } return OP_SUCCESS; } /* End of getPartnerAddress() */ int EchoHeader::getPartnerAddress(struct in6_addr *dst){ switch( this->getMessageType() ){ case TYPE_NEP_HANDSHAKE_CLIENT: memcpy(dst, this->data_hsclnt->partner_ip,sizeof(struct in6_addr)); break; case TYPE_NEP_HANDSHAKE_FINAL: memcpy(dst, this->data_hsfinal->partner_ip,sizeof(struct in6_addr)); break; default: return OP_FAILURE; break; } return OP_SUCCESS; } /* End of getPartnerAddress() */ /* On failure, it returns 0xAB */ u8 EchoHeader::getIPVersion(){ switch( this->getMessageType() ){ case TYPE_NEP_HANDSHAKE_CLIENT: return this->data_hsclnt->ip_version; break; case TYPE_NEP_HANDSHAKE_FINAL: return this->data_hsfinal->ip_version; break; case TYPE_NEP_PACKET_SPEC: return this->data_pspec->ip_version; break; default: return 0xAB; break; } } /* End of getIPVersion() */ int EchoHeader::setIPVersion(u8 ver){ switch( this->getMessageType() ){ case TYPE_NEP_HANDSHAKE_CLIENT: this->data_hsclnt->ip_version=ver; break; case TYPE_NEP_HANDSHAKE_FINAL: this->data_hsfinal->ip_version=ver; break; case TYPE_NEP_PACKET_SPEC: this->data_pspec->ip_version=ver; break; default: return OP_FAILURE; break; } return OP_SUCCESS; } /* End of setIPVersion() */ /******************************************************************************/ /* NEP_PACKET_SPEC methods */ /******************************************************************************/ int EchoHeader::setProtocol(u8 proto){ switch( this->getMessageType() ){ case TYPE_NEP_PACKET_SPEC: this->data_pspec->protocol=proto; break; default: return OP_FAILURE; break; } return OP_SUCCESS; } /* End of setProtocol() */ /* On failure, it returns 0xAB */ u8 EchoHeader::getProtocol(){ switch( this->getMessageType() ){ case TYPE_NEP_PACKET_SPEC: return this->data_pspec->protocol; break; default: return 0xAB; break; } } /* End of setProtocol() */ int EchoHeader::setPacketCount(u16 c){ switch( this->getMessageType() ){ case TYPE_NEP_PACKET_SPEC: this->data_pspec->packet_count=htons(c); break; default: return OP_FAILURE; break; } return OP_SUCCESS; } /* End of setPacketCount() */ /* On failure, it returns 0 */ u16 EchoHeader::getPacketCount(){ switch( this->getMessageType() ){ case TYPE_NEP_PACKET_SPEC: return ntohs(this->data_pspec->packet_count); break; default: return 0; break; } } /* End of getPacketCount() */ int EchoHeader::getFieldLength(u8 field){ switch(field){ /* 8bit fields */ case PSPEC_IPv4_TOS: case PSPEC_IPv4_PROTO: case PSPEC_IPv6_FLOW: case PSPEC_IPv6_NHDR: case PSPEC_TCP_FLAGS: case PSPEC_ICMP_TYPE: case PSPEC_ICMP_CODE: return 1; break; /* 16bit fields */ case PSPEC_IPv4_ID: case PSPEC_IPv4_FRAGOFF: case PSPEC_TCP_SPORT: case PSPEC_TCP_DPORT: case PSPEC_TCP_WIN: case PSPEC_TCP_URP: case PSPEC_UDP_SPORT: case PSPEC_UDP_DPORT: case PSPEC_UDP_LEN: return 2; break; /* 24bit fields */ case PSPEC_IPv6_TCLASS: return 3; break; /* 32bit fields */ case PSPEC_TCP_SEQ: case PSPEC_TCP_ACK: return 4; break; /* Error */ case PSPEC_PAYLOAD_MAGIC: default: return -1; break; } } /* End of getFieldLength() */ int EchoHeader::addFieldSpec(u8 field, u8 *val){ int flen; /* Determine the length of the field */ if( (flen=this->getFieldLength(field))==-1 || val==NULL ) return OP_FAILURE; else{ return this->addFieldSpec(field, val, flen); } } /* End of addFieldSpec() */ int EchoHeader::addFieldSpec(u8 field, u8 *val, size_t flen){ if( val==NULL ){ return OP_FAILURE; }else{ /* Store the field spec and update internal pointers and counts */ if( (this->fs_bytes+flen) < PACKETSPEC_FIELD_LEN ){ *(this->fs_off)=field; if(field==PSPEC_PAYLOAD_MAGIC){ /* Check length again since this field requires an extra byte */ if(this->fs_bytes+flen+1 < PACKETSPEC_FIELD_LEN){ *(this->fs_off+1)=flen; memcpy(this->fs_off+2, val, flen); this->fs_off+=(flen+2); this->fs_bytes+=(flen+2); }else{ return OP_FAILURE; } }else{ memcpy(this->fs_off+1, val, flen); this->fs_off+=(flen+1); this->fs_bytes+=(flen+1); } }else{ return OP_FAILURE; } } return OP_SUCCESS; } /* End of addFieldSpec() */ int EchoHeader::rewindFieldSpecCounters(){ this->fs_off=(u8 *)this->data_pspec->packetspec; this->fs_bytes=0; return OP_SUCCESS; } /* rewindFieldSpecCounters */ /** @warning dst_buff must be able to hold at least (PACKETSPEC_FIELD_LEN-2) bytes. */ int EchoHeader::getNextFieldSpec(u8 *field, u8 *dst_buff, size_t *final_len){ u8 nfield=0; int nlen=0; if(field==NULL || dst_buff==NULL || this->fs_bytes>=PACKETSPEC_FIELD_LEN) return OP_FAILURE; /* Determine which is the next field specifier */ nfield=*(this->fs_off); if(nfield==PSPEC_PAYLOAD_MAGIC){ nlen=(int)*(this->fs_off+1); /* Read length from the packet */ if(nlen<=0 || nlen>(PACKETSPEC_FIELD_LEN-2) ) return OP_FAILURE; else if( this->fs_bytes+2+nlen>PACKETSPEC_FIELD_LEN) return OP_FAILURE; else memcpy(dst_buff, this->fs_off+2, nlen); this->fs_off+=(nlen+2); this->fs_bytes+=(nlen+2); }else{ if((nlen=this->getFieldLength(nfield))<=0) /* Determine field length */ return OP_FAILURE; else if(this->fs_bytes+1+nlen>PACKETSPEC_FIELD_LEN) return OP_FAILURE; else memcpy(dst_buff, this->fs_off+1, nlen); this->fs_off+=(nlen+1); this->fs_bytes+=(nlen+2); } /* Store data */ *field=nfield; if(final_len!=NULL) *final_len=nlen; return OP_SUCCESS; } /* End of getNextFieldSpec() */ /******************************************************************************/ /* NEP_PACKET_ECHO methods */ /******************************************************************************/ int EchoHeader::setDLT(u16 dlt){ this->data_echo->dlt_type=htons(dlt); return OP_SUCCESS; } /* End of setDLT() */ u16 EchoHeader::getDLT(){ return ntohs(this->data_echo->dlt_type); } /* End of getDLT() */ int EchoHeader::setPacketLength(u16 len){ this->data_echo->packet_len=htons(len); return OP_SUCCESS; } /* End of setPacketLength() */ u16 EchoHeader::getPacketLength(){ return ntohs(this->data_echo->packet_len); } /* End of setPacketLength() */ int EchoHeader::setEchoedPacket(const u8 *pkt, size_t pktlen){ int padding=0; if(pkt==NULL) return OP_FAILURE; if(pktlen>MAX_ECHOED_PACKET_LEN){ pktlen=MAX_ECHOED_PACKET_LEN; } memcpy(this->data_echo->payload_and_mac, pkt, pktlen); if((pktlen+4)%16!=0){ padding=16-((pktlen+4)%16); memset(this->data_echo->payload_and_mac+pktlen, 0, padding); } this->echo_bytes=pktlen+padding; this->echo_mac+=pktlen+padding; /* Set the packet length field automatically */ this->setPacketLength((u16)pktlen); this->length = STD_NEP_HEADER_LEN + 4 + this->echo_bytes + MAC_LENGTH; assert(this->length%16==0); return OP_SUCCESS; } /* End of setEchoedPacket() */ /* @warning value stored in final_len is not exactly the actual length of the * returned buffer but the value stored in the "Packet Length" field of the * NEP_ECHO message. The caller is supposed to validate received packets before * trusting that length */ u8 *EchoHeader::getEchoedPacket(u16 *final_len){ if(final_len!=NULL) *final_len=this->getPacketLength(); return this->data_echo->payload_and_mac; } /* End of getEchoedPacket() */ u8 *EchoHeader::getEchoedPacket(){ return this->getEchoedPacket(NULL); } /* End of getEchoedPacket() */ /** This method tries to update the object's internal counters for a NEP_ECHO * packet. This should be used when storing a received NEP_ECHO message in * the object. In that case, the internal pointers will not be set up * correctly, as the object did not construct the message. Calling this method * should fix the internal state of the object and make things like * verifyMessageAuthenticationCode() work. */ int EchoHeader::updateEchoInternals(){ if( this->getMessageType()!=TYPE_NEP_ECHO ) return OP_FAILURE; /* Fix echo bytes length */ this->echo_bytes=this->getPacketLength(); if((this->echo_bytes+4)%16!=0){ this->echo_bytes+=16-((this->echo_bytes+4)%16); } /* Fix MAC offset */ this->echo_mac=((u8 *)this->data_echo->payload_and_mac)+this->echo_bytes; return OP_SUCCESS; } /* End of updateEchoInternals() */ /******************************************************************************/ /* NEP_ERROR methods */ /******************************************************************************/ /** @warning error strings longer than MAX_NEP_ERROR_MSG_LEN-1 will be truncated */ int EchoHeader::setErrorMessage(const char *err){ if(err==NULL){ return OP_FAILURE; }else{ strncpy((char *)this->data_error->errmsg, err, ERROR_MSG_LEN); this->data_error->errmsg[ERROR_MSG_LEN-1]='\0'; } return OP_SUCCESS; } /* End of setErrorMessage() */ /* @warning Returned pointer, points to the start of the "Error Message" field * of the NEP_ERROR message. When receiving this kind of messages, there is no * guarantee that the field contains printable characters, or that it is NULL * terminated. The caller should validate it's contents. It is safe to read * MAX_NEP_ERROR_MSG_LEN bytes from the start of the returned buffer pointer. */ char *EchoHeader::getErrorMessage(){ return (char *)this->data_error->errmsg; } /* End of getErrorMessage() */ /******************************************************************************/ /* CRYPTOGRAPHY */ /******************************************************************************/ u8 *EchoHeader::getCiphertextBounds(size_t *final_len){ return this->getCiphertextBounds(final_len, this->getMessageType()); } u8 *EchoHeader::getCiphertextBounds(size_t *final_len, int message_type){ u8 *start=NULL; size_t len=0; switch( message_type ){ case TYPE_NEP_HANDSHAKE_SERVER: /* this msg is never transmitted encrypted */ len=0; start=(u8 *)&this->h; break; case TYPE_NEP_HANDSHAKE_CLIENT: start=this->data_hsclnt->partner_ip; len=32; break; case TYPE_NEP_HANDSHAKE_FINAL: start=this->data_hsfinal->partner_ip; len=32; break; case TYPE_NEP_PACKET_SPEC: start=(u8 *)(&this->h); len=NEP_PACKETSPEC_LEN-MAC_LENGTH; break; case TYPE_NEP_READY: start=(u8 *)(&this->h); len=NEP_READY_LEN-MAC_LENGTH; break; case TYPE_NEP_ECHO: start=(u8 *)(&this->h); len=this->length-MAC_LENGTH; break; case TYPE_NEP_ERROR: start=(u8 *)(&this->h); len=NEP_ERROR_LEN-MAC_LENGTH; break; default: return NULL; break; } if(final_len!=NULL) *final_len=len; return start; } /* End of getCiphertextBounds() */ /** Encrypts the NEP message using the supplied key and initialization vector. * On success it returns a pointer to the beginning of the last ciphertext * block. This should be stored by the caller and used as the IV for the * next encrypted data. It returns NULL in case of error. */ u8 *EchoHeader::encrypt(u8 *key, size_t key_len, u8 *iv){ nping_print(DBG_4, "%s(%p, %lu, %p)", __func__, key, (long unsigned)key_len, iv); u8 *start=NULL; size_t len=0; if(key==NULL || key_len==0 || iv==NULL) return NULL; if((start=this->getCiphertextBounds(&len))==NULL) return NULL; if(len>=CIPHER_BLOCK_SIZE){ if( Crypto::aes128_cbc_encrypt(start, len, (u8 *)(&this->h_tmp), key, key_len, iv) != OP_SUCCESS ) return NULL; else{ memcpy(start, &this->h_tmp, len); return (start+(len-CIPHER_BLOCK_SIZE)); } }else{ return NULL; } } /* End of encrypt() */ u8 *EchoHeader::decrypt(u8 *key, size_t key_len, u8 *iv, int message_type){ nping_print(DBG_4, "%s(%p, %lu, %p)", __func__, key, (long unsigned)key_len, iv); u8 *start=NULL; size_t len=0; static u8 lastblock[CIPHER_BLOCK_SIZE]; if(key==NULL || key_len==0 || iv==NULL) return NULL; if((start=this->getCiphertextBounds(&len, message_type))==NULL) return NULL; if(len>=CIPHER_BLOCK_SIZE){ /* Keep a copy of the last ciphertext block */ memcpy(lastblock, start+len-CIPHER_BLOCK_SIZE, CIPHER_BLOCK_SIZE); if( Crypto::aes128_cbc_decrypt(start, len, (u8 *)(&this->h_tmp), key, key_len, iv) != OP_SUCCESS ) return NULL; else{ memcpy(start, &this->h_tmp, len); return lastblock; } }else{ return NULL; } } /* End of decrypt() */