From: Shawn Turner (shaturne) Date: Wed, 7 Sep 2016 17:13:21 +0000 (-0400) Subject: Merge pull request #613 in SNORT/snort3 from appid_client_smtp5 to master X-Git-Tag: 3.0.0-233~269 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=288111f3bf5d45eb10d4896e22079667eff94cf4;p=thirdparty%2Fsnort3.git Merge pull request #613 in SNORT/snort3 from appid_client_smtp5 to master Squashed commit of the following: commit 244930cb9245e978861f61ee0387c726fc1974e5 Author: Steve Chew Date: Tue Sep 6 11:50:55 2016 -0400 Added smtp client counters and unit tests. --- diff --git a/configure.ac b/configure.ac index c76f850b2..53eaef708 100644 --- a/configure.ac +++ b/configure.ac @@ -1158,6 +1158,7 @@ src/stream/user/Makefile \ src/stream/file/Makefile \ src/network_inspectors/Makefile \ src/network_inspectors/appid/Makefile \ +src/network_inspectors/appid/client_plugins/test/Makefile \ src/network_inspectors/appid/service_plugins/test/Makefile \ src/network_inspectors/arp_spoof/Makefile \ src/network_inspectors/binder/Makefile \ diff --git a/src/network_inspectors/appid/CMakeLists.txt b/src/network_inspectors/appid/CMakeLists.txt index 8b12ffd53..0646818a9 100644 --- a/src/network_inspectors/appid/CMakeLists.txt +++ b/src/network_inspectors/appid/CMakeLists.txt @@ -198,6 +198,7 @@ endif (STATIC_INSPECTORS) target_include_directories ( appid PRIVATE ${APPID_INCLUDE_DIR} ) add_subdirectory(service_plugins/test) +add_subdirectory(client_plugins/test) #install (FILES ${APPID_INCLUDES} # DESTINATION "${INCLUDE_INSTALL_PATH}/appid" diff --git a/src/network_inspectors/appid/Makefile.am b/src/network_inspectors/appid/Makefile.am index dde53b8df..fc2cfbb64 100644 --- a/src/network_inspectors/appid/Makefile.am +++ b/src/network_inspectors/appid/Makefile.am @@ -191,6 +191,7 @@ $(util_file_list) endif if ENABLE_UNIT_TESTS -SUBDIRS=service_plugins/test +SUBDIRS=service_plugins/test \ +client_plugins/test endif diff --git a/src/network_inspectors/appid/appid_module.cc b/src/network_inspectors/appid/appid_module.cc index 248606683..d5db3cbb3 100644 --- a/src/network_inspectors/appid/appid_module.cc +++ b/src/network_inspectors/appid/appid_module.cc @@ -64,6 +64,18 @@ const PegInfo appid_pegs[] = { "netbios_flows", "count of netbios service flows discovered by appid" }, { "pop_flows", "count of pop service flows discovered by appid" }, { "rsync_flows", "count of rsync service flows discovered by appid" }, + { "smtp_aol_clients", "count of AOL smtp clients discovered by appid" }, + { "smtp_applemail_clients", "count of Apple Mail smtp clients discovered by appid" }, + { "smtp_eudora_clients", "count of Eudora smtp clients discovered by appid" }, + { "smtp_eudora_pro_clients", "count of Eudora Pro smtp clients discovered by appid" }, + { "smtp_evolution_clients", "count of Evolution smtp clients discovered by appid" }, + { "smtp_kmail_clients", "count of KMail smtp clients discovered by appid" }, + { "smtp_lotus_notes_clients", "count of Lotus Notes smtp clients discovered by appid" }, + { "smtp_microsoft_outlook_clients", "count of Microsoft Outlook smtp clients discovered by appid" }, + { "smtp_microsoft_outlook_express_clients", "count of Microsoft Outlook Express smtp clients discovered by appid" }, + { "smtp_microsoft_outlook_imo_clients", "count of Microsoft Outlook IMO smtp clients discovered by appid" }, + { "smtp_mutt_clients", "count of Mutt smtp clients discovered by appid" }, + { "smtp_thunderbird_clients", "count of Thunderbird smtp clients discovered by appid" }, { "smtp_flows", "count of smtp flows discovered by appid" }, { "smtps_flows", "count of smtps flows discovered by appid" }, { "ssh_clients", "count of ssh clients discovered by appid" }, diff --git a/src/network_inspectors/appid/appid_module.h b/src/network_inspectors/appid/appid_module.h index 056555b7c..e64614d3d 100644 --- a/src/network_inspectors/appid/appid_module.h +++ b/src/network_inspectors/appid/appid_module.h @@ -63,6 +63,18 @@ struct AppIdStats PegCount netbios_flows; PegCount pop_flows; PegCount rsync_flows; + PegCount smtp_aol_clients; + PegCount smtp_applemail_clients; + PegCount smtp_eudora_clients; + PegCount smtp_eudora_pro_clients; + PegCount smtp_evolution_clients; + PegCount smtp_kmail_clients; + PegCount smtp_lotus_notes_clients; + PegCount smtp_microsoft_outlook_clients; + PegCount smtp_microsoft_outlook_express_clients; + PegCount smtp_microsoft_outlook_imo_clients; + PegCount smtp_mutt_clients; + PegCount smtp_thunderbird_clients; PegCount smtp_flows; PegCount smtps_flows; PegCount ssh_clients; diff --git a/src/network_inspectors/appid/client_plugins/client_app_smtp.cc b/src/network_inspectors/appid/client_plugins/client_app_smtp.cc index e0aafb224..dff622845 100644 --- a/src/network_inspectors/appid/client_plugins/client_app_smtp.cc +++ b/src/network_inspectors/appid/client_plugins/client_app_smtp.cc @@ -29,6 +29,7 @@ #include "app_info_table.h" #include "appid_api.h" #include "application_ids.h" +#include "appid_module.h" #define UNIT_TESTING 0 @@ -73,7 +74,6 @@ char* stateName [] = #define CLIENT_FLAG_STARTTLS_SENT 0x01 #define CLIENT_FLAG_SMTPS 0x02 -#define MAX_VERSION_SIZE 64 struct ClientSMTPData { int flags; @@ -142,7 +142,7 @@ static const uint8_t APP_SMTP_OUTLOOK[] = "Microsoft Outlook"; static const uint8_t APP_SMTP_OUTLOOK_EXPRESS[] = "Microsoft Outlook Express "; static const uint8_t APP_SMTP_IMO[] = "IMO, "; static const uint8_t APP_SMTP_EVOLUTION[] = "Ximian Evolution "; -static const uint8_t APP_SMTP_LOTUSNOTES[] = "Lotus Notes "; +static const uint8_t APP_SMTP_LOTUS_NOTES[] = "Lotus Notes "; static const uint8_t APP_SMTP_APPLEMAIL[] = "Apple Mail ("; static const uint8_t APP_SMTP_EUDORA[] = "QUALCOMM Windows Eudora Version "; static const uint8_t APP_SMTP_EUDORAPRO[] = "Windows Eudora Pro Version "; @@ -156,13 +156,13 @@ static const uint8_t APP_SMTP_THUNDERBIRD_SHORT[] = "Thunderbird/"; static Client_App_Pattern patterns[] = { - { (uint8_t*)HELO, sizeof(HELO)-1, 0, APP_ID_SMTP }, - { (uint8_t*)EHLO, sizeof(EHLO)-1, 0, APP_ID_SMTP }, + { (uint8_t*)HELO, sizeof(HELO)-1, -1, APP_ID_SMTP }, + { (uint8_t*)EHLO, sizeof(EHLO)-1, -1, APP_ID_SMTP }, { APP_SMTP_OUTLOOK, sizeof(APP_SMTP_OUTLOOK)-1, -1, APP_ID_OUTLOOK }, { APP_SMTP_OUTLOOK_EXPRESS, sizeof(APP_SMTP_OUTLOOK_EXPRESS)-1,-1, APP_ID_OUTLOOK_EXPRESS }, { APP_SMTP_IMO, sizeof(APP_SMTP_IMO)-1, -1, APP_ID_SMTP_IMO }, { APP_SMTP_EVOLUTION, sizeof(APP_SMTP_EVOLUTION)-1, -1, APP_ID_EVOLUTION }, - { APP_SMTP_LOTUSNOTES, sizeof(APP_SMTP_LOTUSNOTES)-1, -1, APP_ID_LOTUS_NOTES }, + { APP_SMTP_LOTUS_NOTES, sizeof(APP_SMTP_LOTUS_NOTES)-1, -1, APP_ID_LOTUS_NOTES }, { APP_SMTP_APPLEMAIL, sizeof(APP_SMTP_APPLEMAIL)-1, -1, APP_ID_APPLE_EMAIL }, { APP_SMTP_EUDORA, sizeof(APP_SMTP_EUDORA)-1, -1, APP_ID_EUDORA }, { APP_SMTP_EUDORAPRO, sizeof(APP_SMTP_EUDORAPRO)-1, -1, APP_ID_EUDORA_PRO }, @@ -234,8 +234,47 @@ static CLIENT_APP_RETCODE smtp_init(const IniClientAppAPI* const init_api, SF_LI return CLIENT_APP_SUCCESS; } -static int ExtractVersion(ClientSMTPData* const fd, const uint8_t* product, - const uint8_t* data, AppIdSession* flowp, Packet*) +/* + * product - The product data should not include any characters + * after the end of the product version (e.g. no CR, LF, etc). + * prefix_len - The number of characters that are the prefix to the version, + * including the NUL terminating character. + */ +static int extract_version_and_add_client_app(ApplicationId clientId, + const int prefix_len, const uint8_t* product, const uint8_t* product_end, + ClientSMTPData* const client_data, AppIdSession* flowp, + AppId appId, PegCount *stat_counter) +{ + const u_int8_t* p; + u_int8_t* v; + u_int8_t* v_end; + + v_end = client_data->version; + v_end += MAX_VERSION_SIZE - 1; + + // The prefix_len includes the NUL character, but product does not, so + // subtract 1 from length to skip. + p = product + prefix_len - 1; + if (p >= product_end || isspace(*p)) + return 1; + for (v=client_data->version; vadd_app(flowp, appId, clientId, (char*)client_data->version); + (*stat_counter)++; + return 0; +} + + +/* + * Identify the product and version of the SMTP client. + * + * Returns 0 if a recognized product is found. Otherwise returns 1. + */ +static int IdentifyClientVersion(ClientSMTPData* const fd, const uint8_t* product, + const uint8_t* data_end, AppIdSession* flowp, Packet*) { const u_int8_t* p; u_int8_t* v; @@ -246,61 +285,39 @@ static int ExtractVersion(ClientSMTPData* const fd, const uint8_t* product, v_end = fd->version; v_end += MAX_VERSION_SIZE - 1; - len = data - product; + len = data_end - product; if (len >= sizeof(MICROSOFT) && memcmp(product, MICROSOFT, sizeof(MICROSOFT)-1) == 0) { p = product + sizeof(MICROSOFT) - 1; - if (data-p >= (int)sizeof(OUTLOOK) && memcmp(p, OUTLOOK, sizeof(OUTLOOK)-1) == 0) + if (data_end-p >= (int)sizeof(OUTLOOK) && memcmp(p, OUTLOOK, sizeof(OUTLOOK)-1) == 0) { p += sizeof(OUTLOOK) - 1; - if (p >= data) + if (p >= data_end) return 1; if (*p == ',') { p++; - if (p >= data || *p != ' ') + if (p >= data_end || *p != ' ') return 1; - p++; - if (p >= data || isspace(*p)) - return 1; - for (v=fd->version; vadd_app(flowp, appId, APP_ID_OUTLOOK, (char*)fd->version); - return 0; + return extract_version_and_add_client_app(APP_ID_OUTLOOK, + 2, p, data_end, fd, flowp, appId, + &appid_stats.smtp_microsoft_outlook_clients); } else if (*p == ' ') { p++; - if (data-p >= (int)sizeof(EXPRESS) && memcmp(p, EXPRESS, sizeof(EXPRESS)-1) == 0) + if (data_end-p >= (int)sizeof(EXPRESS) && memcmp(p, EXPRESS, sizeof(EXPRESS)-1) == 0) { - p += sizeof(EXPRESS) - 1; - if (p >= data || isspace(*p)) - return 1; - for (v=fd->version; vadd_app(flowp, appId, APP_ID_OUTLOOK_EXPRESS, - (char*)fd->version); - return 0; + return extract_version_and_add_client_app(APP_ID_OUTLOOK_EXPRESS, + sizeof(EXPRESS), p, data_end, fd, flowp, appId, + &appid_stats.smtp_microsoft_outlook_express_clients); } - else if (data-p >= (int)sizeof(IMO) && memcmp(p, IMO, sizeof(IMO)-1) == 0) + else if (data_end-p >= (int)sizeof(IMO) && memcmp(p, IMO, sizeof(IMO)-1) == 0) { - p += sizeof(IMO) - 1; - if (p >= data) - return 1; - for (v=fd->version; vadd_app(flowp, appId, APP_ID_OUTLOOK, (char*)fd->version); - return 0; + return extract_version_and_add_client_app(APP_ID_OUTLOOK, + sizeof(IMO), p, data_end, fd, flowp, appId, + &appid_stats.smtp_microsoft_outlook_imo_clients); } } } @@ -308,166 +325,96 @@ static int ExtractVersion(ClientSMTPData* const fd, const uint8_t* product, else if (len >= sizeof(APP_SMTP_EVOLUTION) && memcmp(product, APP_SMTP_EVOLUTION, sizeof(APP_SMTP_EVOLUTION)-1) == 0) { - p = product + sizeof(APP_SMTP_EVOLUTION) - 1; - if (p >= data || isspace(*p)) - return 1; - for (v=fd->version; vadd_app(flowp, appId, APP_ID_EVOLUTION, (char*)fd->version); - return 0; + return extract_version_and_add_client_app(APP_ID_EVOLUTION, + sizeof(APP_SMTP_EVOLUTION), product, data_end, fd, flowp, appId, + &appid_stats.smtp_evolution_clients); } - else if (len >= sizeof(APP_SMTP_LOTUSNOTES) && memcmp(product, APP_SMTP_LOTUSNOTES, - sizeof(APP_SMTP_LOTUSNOTES)-1) == 0) + else if (len >= sizeof(APP_SMTP_LOTUS_NOTES) && memcmp(product, APP_SMTP_LOTUS_NOTES, + sizeof(APP_SMTP_LOTUS_NOTES)-1) == 0) { - p = product + sizeof(APP_SMTP_LOTUSNOTES) - 1; - if (p >= data || isspace(*p)) - return 1; - for (v=fd->version; vadd_app(flowp, appId, APP_ID_LOTUS_NOTES, (char*)fd->version); - return 0; + return extract_version_and_add_client_app(APP_ID_LOTUS_NOTES, + sizeof(APP_SMTP_LOTUS_NOTES), product, data_end, fd, flowp, appId, + &appid_stats.smtp_lotus_notes_clients); } else if (len >= sizeof(APP_SMTP_APPLEMAIL) && memcmp(product, APP_SMTP_APPLEMAIL, sizeof(APP_SMTP_APPLEMAIL)-1) == 0) { p = product + sizeof(APP_SMTP_APPLEMAIL) - 1; - if (p >= data || *(data - 1) != ')' || *p == ')' || isspace(*p)) + if (p >= data_end || *(data_end - 1) != ')' || *p == ')' || isspace(*p)) return 1; - for (v=fd->version; vversion; vadd_app(flowp, appId, APP_ID_APPLE_EMAIL, (char*)fd->version); + appid_stats.smtp_applemail_clients++; return 0; } else if (len >= sizeof(APP_SMTP_EUDORA) && memcmp(product, APP_SMTP_EUDORA, sizeof(APP_SMTP_EUDORA)-1) == 0) { - p = product + sizeof(APP_SMTP_EUDORA) - 1; - if (p >= data || isspace(*p)) - return 1; - for (v=fd->version; vadd_app(flowp, appId, APP_ID_EUDORA, (char*)fd->version); - return 0; + return extract_version_and_add_client_app(APP_ID_EUDORA, + sizeof(APP_SMTP_EUDORA), product, data_end, fd, flowp, appId, + &appid_stats.smtp_eudora_clients); } else if (len >= sizeof(APP_SMTP_EUDORAPRO) && memcmp(product, APP_SMTP_EUDORAPRO, sizeof(APP_SMTP_EUDORAPRO)-1) == 0) { - p = product + sizeof(APP_SMTP_EUDORAPRO) - 1; - if (p >= data || isspace(*p)) - return 1; - for (v=fd->version; vadd_app(flowp, appId, APP_ID_EUDORA_PRO, (char*)fd->version); - return 0; + return extract_version_and_add_client_app(APP_ID_EUDORA_PRO, + sizeof(APP_SMTP_EUDORAPRO), product, data_end, fd, flowp, appId, + &appid_stats.smtp_eudora_pro_clients); } - else if (len >= sizeof(APP_SMTP_AOL) && memcmp(product, APP_SMTP_AOL, sizeof(APP_SMTP_AOL)- - 1) == 0) + else if (len >= sizeof(APP_SMTP_AOL) && memcmp(product, APP_SMTP_AOL, + sizeof(APP_SMTP_AOL)-1) == 0) { - p = product + sizeof(APP_SMTP_AOL) - 1; - if (p >= data || isspace(*p)) - return 1; - for (v=fd->version; vadd_app(flowp, appId, APP_ID_AOL_EMAIL, (char*)fd->version); - return 0; + return extract_version_and_add_client_app(APP_ID_AOL_EMAIL, + sizeof(APP_SMTP_AOL), product, data_end, fd, flowp, appId, + &appid_stats.smtp_aol_clients); } - else if (len >= sizeof(APP_SMTP_MUTT) && memcmp(product, APP_SMTP_MUTT, sizeof(APP_SMTP_MUTT)- - 1) == 0) + else if (len >= sizeof(APP_SMTP_MUTT) && memcmp(product, APP_SMTP_MUTT, + sizeof(APP_SMTP_MUTT)-1) == 0) { - p = product + sizeof(APP_SMTP_MUTT) - 1; - if (p >= data || isspace(*p)) - return 1; - for (v=fd->version; vadd_app(flowp, appId, APP_ID_MUTT, (char*)fd->version); - return 0; + return extract_version_and_add_client_app(APP_ID_MUTT, + sizeof(APP_SMTP_MUTT), product, data_end, fd, flowp, appId, + &appid_stats.smtp_mutt_clients); } else if (len >= sizeof(APP_SMTP_KMAIL) && memcmp(product, APP_SMTP_KMAIL, sizeof(APP_SMTP_KMAIL)-1) == 0) { - p = product + sizeof(APP_SMTP_KMAIL) - 1; - if (p >= data || isspace(*p)) - return 1; - for (v=fd->version; vadd_app(flowp, appId, appId /*KMAIL_ID*/, (char*)fd->version); - return 0; + return extract_version_and_add_client_app(APP_ID_KMAIL, + sizeof(APP_SMTP_KMAIL), product, data_end, fd, flowp, appId, + &appid_stats.smtp_kmail_clients); } else if (len >= sizeof(APP_SMTP_THUNDERBIRD) && memcmp(product, APP_SMTP_THUNDERBIRD, sizeof(APP_SMTP_THUNDERBIRD)-1) == 0) { - p = product + sizeof(APP_SMTP_THUNDERBIRD) - 1; - if (p >= data || isspace(*p)) - return 1; - for (v=fd->version; vadd_app(flowp, appId, APP_ID_THUNDERBIRD, (char*)fd->version); - return 0; + return extract_version_and_add_client_app(APP_ID_THUNDERBIRD, + sizeof(APP_SMTP_THUNDERBIRD), product, data_end, fd, flowp, appId, + &appid_stats.smtp_thunderbird_clients); } else if (len >= sizeof(APP_SMTP_MTHUNDERBIRD) && memcmp(product, APP_SMTP_MTHUNDERBIRD, sizeof(APP_SMTP_MTHUNDERBIRD)-1) == 0) { - p = product + sizeof(APP_SMTP_MTHUNDERBIRD) - 1; - if (p >= data || isspace(*p)) - return 1; - for (v=fd->version; vadd_app(flowp, appId, APP_ID_THUNDERBIRD, (char*)fd->version); - return 0; + return extract_version_and_add_client_app(APP_ID_THUNDERBIRD, + sizeof(APP_SMTP_MTHUNDERBIRD), product, data_end, fd, flowp, appId, + &appid_stats.smtp_thunderbird_clients); } else if (len >= sizeof(APP_SMTP_MOZILLA) && memcmp(product, APP_SMTP_MOZILLA, sizeof(APP_SMTP_MOZILLA)-1) == 0) { - for (p = product + sizeof(APP_SMTP_MOZILLA) - 1; p < data; p++) + for (p = product + sizeof(APP_SMTP_MOZILLA) - 1; p < data_end; p++) { if (*p == 'T') { - sublen = data - p; + sublen = data_end - p; if (sublen >= sizeof(APP_SMTP_THUNDERBIRD_SHORT) && memcmp(p, APP_SMTP_THUNDERBIRD_SHORT, sizeof(APP_SMTP_THUNDERBIRD_SHORT)-1) == 0) { - p = p + sizeof(APP_SMTP_THUNDERBIRD_SHORT) - 1; - for (v=fd->version; vadd_app(flowp, appId, APP_ID_THUNDERBIRD, - (char*)fd->version); - return 0; + return extract_version_and_add_client_app( + APP_ID_THUNDERBIRD, sizeof(APP_SMTP_THUNDERBIRD_SHORT), + p, data_end, fd, flowp, appId, + &appid_stats.smtp_thunderbird_clients); } } } @@ -520,7 +467,7 @@ static CLIENT_APP_RETCODE smtp_validate(const uint8_t* data, uint16_t size, cons { /* Because we can't see any further info without decryption we settle for plain APP_ID_SMTPS instead of perhaps finding data that would make calling - ExtractVersion() worthwhile, So set the appid and call it good. */ + IdentifyClientVersion() worthwhile, So set the appid and call it good. */ smtp_client_mod.api->add_app(flowp, APP_ID_SMTPS, APP_ID_SMTPS, nullptr); goto done; } @@ -701,7 +648,7 @@ static CLIENT_APP_RETCODE smtp_validate(const uint8_t* data, uint16_t size, cons { if (fd->headerline && fd->pos) { - ExtractVersion(fd, fd->headerline, fd->headerline + fd->pos, flowp, pkt); + IdentifyClientVersion(fd, fd->headerline, fd->headerline + fd->pos, flowp, pkt); snort_free(fd->headerline); fd->headerline = nullptr; } diff --git a/src/network_inspectors/appid/client_plugins/test/CMakeLists.txt b/src/network_inspectors/appid/client_plugins/test/CMakeLists.txt new file mode 100644 index 000000000..0f73377be --- /dev/null +++ b/src/network_inspectors/appid/client_plugins/test/CMakeLists.txt @@ -0,0 +1,12 @@ +set ( + SMTP_TEST_LIBS + utils +) + +add_library(smtp_test_depends_on_lib ../../appid_stats_counter.cc) + +add_cpputest(client_app_smtp_test smtp_test_depends_on_lib ${SMTP_TEST_LIBS}) + +include_directories ( appid PRIVATE ${APPID_INCLUDE_DIR} ) + + diff --git a/src/network_inspectors/appid/client_plugins/test/Makefile.am b/src/network_inspectors/appid/client_plugins/test/Makefile.am new file mode 100644 index 000000000..3af23bf0e --- /dev/null +++ b/src/network_inspectors/appid/client_plugins/test/Makefile.am @@ -0,0 +1,15 @@ + +AM_DEFAULT_SOURCE_EXT = .cc + +check_PROGRAMS = \ +client_app_smtp_test + +TESTS = $(check_PROGRAMS) + +client_app_smtp_test_CPPFLAGS = -I$(top_srcdir)/src/network_inspectors/appid @AM_CPPFLAGS@ @CPPUTEST_CPPFLAGS@ + +client_app_smtp_test_LDADD = \ +../../appid_stats_counter.o \ +../../../../utils/libutils.a \ +@CPPUTEST_LDFLAGS@ + diff --git a/src/network_inspectors/appid/client_plugins/test/client_app_smtp_test.cc b/src/network_inspectors/appid/client_plugins/test/client_app_smtp_test.cc new file mode 100644 index 000000000..75f62fab7 --- /dev/null +++ b/src/network_inspectors/appid/client_plugins/test/client_app_smtp_test.cc @@ -0,0 +1,245 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2016-2016 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- + +// client_app_smtp_test.cc author Steve Chew +// unit test for client_app_smtp + +#include +#include +#include + +#include "network_inspectors/appid/client_plugins/client_app_smtp.cc" + +#include + +void Debug::print(const char*, int, uint64_t, const char*, ...) { } + +struct AddAppData +{ + AppId client_id = 0; + std::string *version_str = nullptr; +} app_data; + +void fake_add_app(AppIdSession*, AppId, AppId client_id, const char* version) +{ + mock().actualCall("add_app"); + app_data.client_id = client_id; + if(app_data.version_str) + delete app_data.version_str; + app_data.version_str = new std::string(version); +} + +ClientAppApi fake_clientappapi = +{ + nullptr, + nullptr, + &fake_add_app, + nullptr, + nullptr, + nullptr, +}; + +void check_client_version(const uint8_t *client_str, AppId client_id, + const char *version_str, PegCount *client_count) +{ + ClientSMTPData clientData; + const uint8_t *data_end = client_str + strlen((const char *)client_str)-2; + smtp_client_mod.api = &fake_clientappapi; + + mock().expectOneCall("add_app"); + + LONGS_EQUAL(0, IdentifyClientVersion(&clientData, client_str, data_end, nullptr, nullptr)); + LONGS_EQUAL(client_id, app_data.client_id); + LONGS_EQUAL(1, *client_count); + STRCMP_EQUAL(version_str, app_data.version_str->c_str()) + mock().checkExpectations(); + +} + +TEST_GROUP(client_app_smtp) +{ + void setup() + { + memset(&appid_stats, 0, sizeof(appid_stats)); + } + + void teardown() + { + delete app_data.version_str; + app_data.version_str = nullptr; + mock().clear(); + } +}; + +TEST(client_app_smtp, identify_client_version_empty_data) +{ + ClientSMTPData clientData; + LONGS_EQUAL(1, IdentifyClientVersion(&clientData, nullptr, nullptr, nullptr, nullptr)); +} + +TEST(client_app_smtp, extract_version_and_add_client_app_success) +{ + ClientSMTPData clientData; + PegCount client_count = 0; + const uint8_t *client_str=(const uint8_t *)"Thunderbird 17.2\r\n"; + const uint8_t *data_end = client_str + strlen((const char *)client_str)-2; + smtp_client_mod.api = &fake_clientappapi; + + mock().expectOneCall("add_app"); + + LONGS_EQUAL(0, extract_version_and_add_client_app(APP_ID_THUNDERBIRD, sizeof(APP_SMTP_THUNDERBIRD), client_str, data_end, &clientData, nullptr, 0, &client_count)); + LONGS_EQUAL(1, client_count); + STRCMP_EQUAL("17.2", app_data.version_str->c_str()) + +} + +TEST(client_app_smtp, extract_version_and_add_client_app_missing_version) +{ + ClientSMTPData clientData; + PegCount client_count = 0; + const uint8_t *client_str=(const uint8_t *)"Thunderbird \r\n"; + const uint8_t *data_end = client_str + strlen((const char *)client_str)-2; + smtp_client_mod.api = &fake_clientappapi; + + LONGS_EQUAL(1, extract_version_and_add_client_app(APP_ID_THUNDERBIRD, sizeof(APP_SMTP_THUNDERBIRD), client_str, data_end, &clientData, nullptr, 0, &client_count)); + LONGS_EQUAL(0, client_count); + mock().checkExpectations(); + +} + +TEST(client_app_smtp, extract_version_and_add_client_invalid_extra_space) +{ + ClientSMTPData clientData; + PegCount client_count = 0; + const uint8_t *client_str=(const uint8_t *)"Thunderbird 1.0\r\n"; + const uint8_t *data_end = client_str + strlen((const char *)client_str)-2; + smtp_client_mod.api = &fake_clientappapi; + + LONGS_EQUAL(1, extract_version_and_add_client_app(APP_ID_THUNDERBIRD, sizeof(APP_SMTP_THUNDERBIRD), client_str, data_end, &clientData, nullptr, 0, &client_count)); + LONGS_EQUAL(0, client_count); + mock().checkExpectations(); + +} + +TEST(client_app_smtp, identify_client_version_microsoft_outlook) +{ + const uint8_t *client_str=(const uint8_t *)"Microsoft Outlook, 15.0\r\n"; + check_client_version(client_str, APP_ID_OUTLOOK, "15.0", &appid_stats.smtp_microsoft_outlook_clients); +} + +TEST(client_app_smtp, identify_client_version_microsoft_outlook_express) +{ + const uint8_t *client_str=(const uint8_t *)"Microsoft Outlook Express 13.0\r\n"; + check_client_version(client_str, APP_ID_OUTLOOK_EXPRESS, "13.0", &appid_stats.smtp_microsoft_outlook_express_clients); +} + +TEST(client_app_smtp, identify_client_version_microsoft_outlook_imo) +{ + const uint8_t *client_str=(const uint8_t *)"Microsoft Outlook IMO, 11.0\r\n"; + check_client_version(client_str, APP_ID_OUTLOOK, "11.0", &appid_stats.smtp_microsoft_outlook_imo_clients); +} + +TEST(client_app_smtp, identify_client_version_evolution) +{ + const uint8_t *client_str=(const uint8_t *)"Ximian Evolution 7.0\r\n"; + check_client_version(client_str, APP_ID_EVOLUTION, "7.0", &appid_stats.smtp_evolution_clients); +} + +TEST(client_app_smtp, identify_client_version_lotus_notes) +{ + const uint8_t *client_str=(const uint8_t *)"Lotus Notes 2.0\r\n"; + check_client_version(client_str, APP_ID_LOTUS_NOTES, "2.0", &appid_stats.smtp_lotus_notes_clients); +} + +TEST(client_app_smtp, identify_client_version_applemail) +{ + const uint8_t *client_str=(const uint8_t *)"Apple Mail (1984.1)\r\n"; + check_client_version(client_str, APP_ID_APPLE_EMAIL, "1984.1", &appid_stats.smtp_applemail_clients); +} + +TEST(client_app_smtp, identify_client_version_applemail_missing_end_parenthesis) +{ + // Missing trailing parenthesis after version. + const uint8_t *client_str=(const uint8_t *)"Apple Mail (1984.1\r\n"; + + ClientSMTPData clientData; + const uint8_t *data_end = client_str + strlen((const char *)client_str)-2; + smtp_client_mod.api = &fake_clientappapi; + + LONGS_EQUAL(1, IdentifyClientVersion(&clientData, client_str, data_end, nullptr, nullptr)); + mock().checkExpectations(); +} + +TEST(client_app_smtp, identify_client_version_eudora) +{ + const uint8_t *client_str=(const uint8_t *)"QUALCOMM Windows Eudora Version 0.3\r\n"; + check_client_version(client_str, APP_ID_EUDORA, "0.3", &appid_stats.smtp_eudora_clients); +} + +TEST(client_app_smtp, identify_client_version_eudora_pro) +{ + const uint8_t *client_str=(const uint8_t *)"Windows Eudora Pro Version 2.2\r\n"; + check_client_version(client_str, APP_ID_EUDORA_PRO, "2.2", &appid_stats.smtp_eudora_pro_clients); +} + +TEST(client_app_smtp, identify_client_version_aol) +{ + const uint8_t *client_str=(const uint8_t *)"AOL 6.4\r\n"; + check_client_version(client_str, APP_ID_AOL_EMAIL, "6.4", &appid_stats.smtp_aol_clients); +} + +TEST(client_app_smtp, identify_client_version_mutt) +{ + const uint8_t *client_str=(const uint8_t *)"Mutt/1.5.21\r\n"; + check_client_version(client_str, APP_ID_MUTT, "1.5.21", &appid_stats.smtp_mutt_clients); +} + +TEST(client_app_smtp, identify_client_version_kmail) +{ + const uint8_t *client_str=(const uint8_t *)"KMail/2112\r\n"; + check_client_version(client_str, APP_ID_KMAIL, "2112", &appid_stats.smtp_kmail_clients); +} + +TEST(client_app_smtp, identify_client_version_mozilla) +{ + const uint8_t *client_str=(const uint8_t *)"Mozilla/5.0 (Windows NT 5.1; rv:8.0) Gecko/20111105 Thunderbird/21.12\r\n"; + + check_client_version(client_str, APP_ID_THUNDERBIRD, "21.12", &appid_stats.smtp_thunderbird_clients); +} + +TEST(client_app_smtp, identify_client_version_thunderbird) +{ + const uint8_t *client_str=(const uint8_t *)"Thunderbird 17.2\r\n"; + + check_client_version(client_str, APP_ID_THUNDERBIRD, "17.2", &appid_stats.smtp_thunderbird_clients); +} + +TEST(client_app_smtp, identify_client_version_mozilla_thunderbird) +{ + const uint8_t *client_str=(const uint8_t *)"Mozilla Thunderbird 5.0\r\n"; + check_client_version(client_str, APP_ID_THUNDERBIRD, "5.0", &appid_stats.smtp_thunderbird_clients); +} + +// FIXIT-M: Add additional tests for other client types (Outlook, etc). + +int main(int argc, char** argv) +{ + int return_value = CommandLineTestRunner::RunAllTests(argc, argv); + return return_value; +} +