136
287
/* Constant strings and tables */
137
288
static char empty_string[]="";
290
#define ANY_CLOAK "(((0[xX])?[a-fA-F0-9])+\\.?)+"
291
#define CLOAK_REGEX_HEXURL "("ANY_CLOAK")?0[xX][a-fA-F0-9]+\\.?"ANY_CLOAK
292
#define OCTAL_CLOAK "("ANY_CLOAK")?000[0-9]+\\.?"ANY_CLOAK
293
#define DWORD_CLOAK "[0-9]{8,}"
295
static const char cloaked_host_regex[] = "^(("CLOAK_REGEX_HEXURL")|("OCTAL_CLOAK")|("DWORD_CLOAK"))$";
298
static const char tld_regex[] = "^"iana_tld"$";
299
static const char cctld_regex[] = "^"iana_cctld"$";
139
300
static const char dotnet[] = ".net";
140
301
static const char adonet[] = "ado.net";
141
302
static const char aspnet[] = "asp.net";
142
/* ; is replaced by ' ' so omit it here*/
143
static const char lt[]="<";
144
static const char gt[]=">";
303
static const char lt[]="<";
304
static const char gt[]=">";
305
static const char cid[] = "cid:";
145
306
static const char src_text[] = "src";
146
307
static const char href_text[] = "href";
147
static const char mailto[] = "mailto:";
148
static const char mailto_proto[] = "mailto://";
149
static const char https[]="https:";
150
static const char http[]="http:";
151
static const char ftp[] = "ftp:";
153
308
static const size_t href_text_len = sizeof(href_text);
154
309
static const size_t src_text_len = sizeof(src_text);
310
static const size_t cid_len = sizeof(cid)-1;
155
311
static const size_t dotnet_len = sizeof(dotnet)-1;
156
312
static const size_t adonet_len = sizeof(adonet)-1;
157
313
static const size_t aspnet_len = sizeof(aspnet)-1;
158
314
static const size_t lt_len = sizeof(lt)-1;
159
315
static const size_t gt_len = sizeof(gt)-1;
160
static const size_t mailto_len = sizeof(mailto)-1;
161
static const size_t mailto_proto_len = sizeof(mailto_proto)-1;
162
static const size_t https_len = sizeof(https)-1;
163
static const size_t http_len = sizeof(http)-1;
164
static const size_t ftp_len = sizeof(ftp)-1;
317
/*static const char* url_regex="^ *([[:alnum:]%_-]+:(//)?)?([[:alnum:]%_-]@)*[[:alnum:]%_-]+\\.([[:alnum:]%_-]+\\.)*[[:alnum:]_%-]+(/[[:alnum:];:@$=?&/.,%_-]+) *$";*/
166
318
/* for urls, including mailto: urls, and (broken) http:www... style urls*/
167
319
/* refer to: http://www.w3.org/Addressing/URL/5_URI_BNF.html
168
320
* Modifications: don't allow empty domains/subdomains, such as www..com <- that is no url
169
321
* So the 'safe' char class has been split up
171
323
/* character classes */
324
#define URI_alpha "a-zA-Z"
172
325
#define URI_digit "0-9"
326
#define URI_safe_nodot "-$_@&"
327
#define URI_safe "-$_@.&"
328
#define URI_extra "!*\"'(),"
329
#define URI_reserved "=;/#?: "
330
#define URI_national "{}|[]\\^~"
331
#define URI_punctuation "<>"
333
#define URI_hex "[0-9a-fA-f]"
334
#define URI_escape "%"URI_hex"{2}"
335
#define URI_xalpha "([" URI_safe URI_alpha URI_digit URI_extra "]|"URI_escape")" /* URI_safe has to be first, because it contains - */
336
#define URI_xalpha_nodot "([" URI_safe_nodot URI_alpha URI_digit URI_extra "]|"URI_escape")"
338
#define URI_xalphas URI_xalpha"+"
339
#define URI_xalphas_nodot URI_xalpha_nodot"*"
341
#define URI_ialpha "["URI_alpha"]"URI_xalphas_nodot""
342
#define URI_xpalpha URI_xalpha"|\\+"
343
#define URI_xpalpha_nodot URI_xalpha_nodot"|\\+"
344
#define URI_xpalphas "("URI_xpalpha")+"
345
#define URI_xpalphas_nodot "("URI_xpalpha_nodot")+"
347
#define URI_scheme URI_ialpha
348
#define URI_tld iana_tld
349
#define URI_path1 URI_xpalphas_nodot"\\.("URI_xpalphas_nodot"\\.)*"
350
#define URI_path2 URI_tld
351
#define URI_path3 "(/("URI_xpalphas"/?)*)?"
353
#define URI_search "("URI_xalphas"\\+)*"
354
#define URI_fragmentid URI_xalphas
173
356
#define URI_IP_digits "["URI_digit"]{1,3}"
174
#define URI_path_start "[/?:]?"
175
#define URI_numeric_path URI_IP_digits"(\\."URI_IP_digits"){3}"URI_path_start
176
#define URI_numeric_URI "(http|https|ftp:(//)?)?"URI_numeric_path
177
#define URI_numeric_fragmentaddress URI_numeric_URI
357
#define URI_numeric_path URI_IP_digits"(\\."URI_IP_digits"){3}(:"URI_xpalphas_nodot")?(/("URI_xpalphas"/?)*)?"
358
#define URI_numeric_URI "("URI_scheme":(//)?)?"URI_numeric_path"(\\?" URI_search")?"
359
#define URI_numeric_fragmentaddress URI_numeric_URI"(#"URI_fragmentid")?"
361
#define URI_URI1 "("URI_scheme":(//)?)?"URI_path1
362
#define URI_URI2 URI_path2
363
#define URI_URI3 URI_path3"(\\?" URI_search")?"
365
#define URI_fragmentaddress1 URI_URI1
366
#define URI_fragmentaddress2 URI_URI2
367
#define URI_fragmentaddress3 URI_URI3"(#"URI_fragmentid")?"
369
#define URI_CHECK_PROTOCOLS "(http|https|ftp)://.+"
180
371
/*Warning: take care when modifying this regex, it has been tweaked, and tuned, just don't break it please.
181
372
* there is fragmentaddress1, and 2 to work around the ISO limitation of 509 bytes max length for string constants*/
184
375
/* generated by contrib/phishing/generate_tables.c */
185
376
static const short int hextable[256] = {
186
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
187
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
188
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
189
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
190
0x0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
191
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
192
0x0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
193
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
194
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
195
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
196
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
197
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
198
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
199
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
200
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
377
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
378
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
379
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
380
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
381
0x0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
382
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
383
0x0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
384
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
385
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
386
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
387
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
388
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
389
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
390
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
391
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
201
392
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
205
static void string_init_c(struct string* dest,char* data);
206
static int string_assign_concatenated(struct string* dest, const char* prefix, const char* begin, const char* end);
207
static void string_assign_null(struct string* dest);
208
static char *rfind(char *start, char c, size_t len);
209
static char hex2int(const unsigned char* src);
396
static inline void string_init_c(struct string* dest,char* data);
397
static void string_assign_null(struct string* dest);
398
static char *rfind(char *start, char c, size_t len);
399
static inline char hex2int(const unsigned char* src);
400
static int isTLD(const struct phishcheck* pchk,const char* str,int len);
210
401
static enum phish_status phishingCheck(const struct cl_engine* engine,struct url_check* urls);
211
402
static const char* phishing_ret_toString(enum phish_status rc);
213
404
static void url_check_init(struct url_check* urls)
215
string_init_c(&urls->realLink, NULL);
216
string_init_c(&urls->displayLink, NULL);
217
string_init_c(&urls->pre_fixup.pre_displayLink, NULL);
406
urls->realLink.refcount=0;
407
urls->realLink.data=empty_string;
408
urls->realLink.ref=NULL;
409
urls->displayLink.refcount=0;
410
urls->displayLink.data=empty_string;
411
urls->displayLink.ref=NULL;
220
414
/* string reference counting implementation,
654
903
/*cli_dbgmsg("%d %d\n", end-begin, len);*/
655
904
if(begin >= end) {
656
905
string_assign_null(URL);
657
string_assign_null(pre_URL);
660
908
while(isspace(*end))
910
/*TODO: convert \ to /, and stuff like that*/
662
911
/* From mailscanner, my comments enclosed in {} */
663
if(!strncmp(begin,dotnet,dotnet_len) || !strncmp(begin,adonet,adonet_len) || !strncmp(begin,aspnet,aspnet_len)) {
912
if(!strncmp(begin,dotnet,dotnet_len) || !strncmp(begin,adonet,adonet_len) || !strncmp(begin,aspnet,aspnet_len))
664
913
string_assign_null(URL);
665
string_assign_null(pre_URL);
669
916
char* host_begin;
672
919
str_replace(begin,end,'\\','/');
673
/* find beginning of hostname, because:
674
* - we want to keep only protocol, host, and
675
* strip path & query parameter(s)
676
* - we want to make hostname lowercase*/
920
str_strip(&begin,&end,"\"",1);
921
str_strip(&begin,&end,lt,lt_len);
922
str_strip(&begin,&end,gt,gt_len);
923
/* convert hostname to lowercase, but only hostname! */
677
924
host_begin = strchr(begin,':');
678
while(host_begin && (host_begin < end) && (host_begin[1] == '/')) host_begin++;
925
while(host_begin && host_begin[1]=='/') host_begin++;
679
926
if(!host_begin) host_begin=begin;
680
927
else host_begin++;
681
host_len = strcspn(host_begin,":/?");
682
if(host_begin + host_len > end + 1) {
683
/* prevent hostname extending beyond end, it can happen
684
* if we have spaces at the end, we don't want those part of
686
host_len = end - host_begin + 1;
688
/* cut the URL after the hostname */
689
/* @end points to last character we want to be part of the URL */
690
end = host_begin + host_len - 1;
692
host_begin[host_len] = '\0';
693
/* convert hostname to lowercase, but only hostname! */
694
str_make_lowercase(host_begin, host_len);
695
/* some broken MUAs put > in the href, and then
696
* we get a false positive, so remove them */
697
str_replace(begin,end,'<',' ');
698
str_replace(begin,end,'>',' ');
699
str_replace(begin,end,'\"',' ');
700
str_replace(begin,end,';',' ');
701
str_strip(&begin,&end,lt,lt_len);
702
str_strip(&begin,&end,gt,gt_len);
928
host_len = strcspn(host_begin,"/?");
929
str_make_lowercase(host_begin,host_len);
703
930
/* convert %xx to real value */
704
931
str_hex_to_char(&begin,&end);
706
/* htmlnorm converts \n to space, so we have to strip spaces */
707
str_strip(&begin, &end, " ", 1);
711
while((begin <= end) && (begin[0]==' ')) begin++;
712
while((begin <= end) && (end[0]==' ')) end--;
714
if (( rc = string_assign_dup(isReal ? URL : pre_URL,begin,end+1) )) {
715
string_assign_null(URL);
932
str_fixup_spaces(&begin,&end);
933
if (( rc = string_assign_dup(URL,begin,end+1) ))
719
str_fixup_spaces(&begin,&end);
720
if (( rc = string_assign_dup(URL, begin, end+1) )) {
935
/*cli_dbgmsg("%p::%s\n",URL->data,URL->data);*/
941
/* ---- runtime disable ------*/
942
void phish_disable(struct cl_engine* engine, const char* reason)
944
cli_warnmsg("Disabling phishing checks, reason:%s\n",reason);
945
phishing_done(engine);/* sets is_disabled, and frees allocated mem for phishcheck */
728
947
/* -------end runtime disable---------*/
729
int phishingScan(cli_ctx* ctx,tag_arguments_t* hrefs)
949
int phishingScan(message* m,const char* dir,cli_ctx* ctx,tag_arguments_t* hrefs)
731
/* TODO: get_host and then apply regex, etc. */
733
952
struct phishcheck* pchk = (struct phishcheck*) ctx->engine->phishcheck;
734
/* check for status of whitelist fatal error, etc. */
735
953
if(!pchk || pchk->is_disabled)
738
if(!ctx->found_possibly_unwanted)
741
FILE *f = fopen("/home/edwin/quarantine/urls","r");
745
struct url_check urls;
750
fgets(line1, sizeof(line1), f);
751
fgets(line2, sizeof(line2), f);
752
fgets(line3, sizeof(line3), f);
753
if(strcmp(line3, "\n") != 0) {
754
strcpy(line1, line2);
755
strcpy(line2, line3);
756
fgets(line3, sizeof(line3), f);
757
while(strcmp(line3, "\n") != 0) {
758
fgets(line3, sizeof(line3),f);
761
urls.flags = CL_PHISH_ALL_CHECKS;
763
string_init_c(&urls.realLink, line1);
764
string_init_c(&urls.displayLink, line2);
765
string_init_c(&urls.pre_fixup.pre_displayLink, NULL);
766
urls.realLink.refcount=-1;
767
urls.displayLink.refcount=-1;
768
int rc = phishingCheck(ctx->engine, &urls);
773
for(i=0;i<hrefs->count;i++) {
957
for(i=0;i<hrefs->count;i++)
958
if(hrefs->contents[i]) {
774
959
struct url_check urls;
775
960
enum phish_status rc;
961
urls.always_check_flags = DOMAINLIST_REQUIRED;/* required to work correctly */
776
962
urls.flags = strncmp((char*)hrefs->tag[i],href_text,href_text_len)? (CL_PHISH_ALL_CHECKS&~CHECK_SSL): CL_PHISH_ALL_CHECKS;
777
963
urls.link_type = 0;
778
964
if(!strncmp((char*)hrefs->tag[i],src_text,src_text_len)) {
779
965
if (!(urls.flags&CHECK_IMG_URL))
781
urls.link_type |= LINKTYPE_IMAGE;
967
urls.link_type |= LINKTYPE_IMAGE;
783
urls.always_check_flags = 0;
969
if (ctx->options&CL_SCAN_PHISHING_DOMAINLIST)
970
urls.flags |= DOMAINLIST_REQUIRED;
784
971
if (ctx->options & CL_SCAN_PHISHING_BLOCKSSL) {
785
972
urls.always_check_flags |= CHECK_SSL;
891
1144
struct phishcheck* pchk = engine->phishcheck;
892
1145
cli_dbgmsg("Cleaning up phishcheck\n");
893
1146
if(pchk && !pchk->is_disabled) {
1147
free_regex(&pchk->preg);
1148
free_regex(&pchk->preg_hexurl);
1149
free_regex(&pchk->preg_cctld);
1150
free_regex(&pchk->preg_tld);
894
1151
free_regex(&pchk->preg_numeric);
1152
if(pchk->url_regex) {
1153
free(pchk->url_regex);
1154
pchk->url_regex = NULL;
1156
pchk->is_disabled = 1;
896
1158
whitelist_done(engine);
897
1159
domainlist_done(engine);
899
1161
cli_dbgmsg("Freeing phishcheck struct\n");
900
mpool_free(engine->mempool, pchk);
1163
engine->phishcheck = NULL;
902
1165
cli_dbgmsg("Phishcheck cleaned up\n");
906
/*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz*/
907
static const uint8_t URI_alpha[256] = {
908
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
909
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
910
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
911
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
912
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
913
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
914
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
915
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
916
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
917
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
918
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
919
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
920
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
921
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
922
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
923
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
926
/*!"$%&'()*,-0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz*/
927
static const uint8_t URI_xalpha_nodot[256] = {
928
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
929
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
930
0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0,
931
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
932
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
933
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
934
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
935
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
936
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
937
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
938
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
939
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
940
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
941
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
942
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
943
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
946
/*!"#$%&'()*+,-0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz*/
947
static const uint8_t URI_xpalpha_nodot[256] = {
948
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
949
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
950
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
951
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
952
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
953
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
954
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
955
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
956
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
957
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
958
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
959
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
960
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
961
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
962
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
963
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
966
static inline int validate_uri_xalphas_nodot(const char *start, const char *end)
968
const unsigned char *p;
969
for(p=(const unsigned char*)start;p < (const unsigned char*)end; p++) {
970
if(!URI_xalpha_nodot[*p])
976
static inline int validate_uri_xpalphas_nodot(const char *start, const char *end)
978
const unsigned char *p;
979
for(p=(const unsigned char*)start;p < (const unsigned char*)end; p++) {
980
if(!URI_xpalpha_nodot[*p])
983
/* must have at least on char */
984
return p > (const unsigned char*)start;
988
static inline int validate_uri_ialpha(const char *start, const char *end)
990
const unsigned char *p = (const unsigned char*) start;
991
if(start >= end || !URI_alpha[*p])
993
return validate_uri_xalphas_nodot(start + 1, end);
997
1169
* Only those URLs are identified as URLs for which phishing detection can be performed.
999
static int isURL(char* URL, int accept_anyproto)
1001
char *last_tld_end = NULL, *q;
1002
const char *start = NULL, *p, *end;
1007
while (*URL == ' ') URL++;
1010
if (strncmp(URL, https, https_len) == 0)
1011
start = URL + https_len - 1;
1012
else if (strncmp(URL, http, http_len) == 0)
1013
start = URL + http_len - 1;
1016
if (strncmp(URL, ftp, ftp_len) == 0)
1017
start = URL + ftp_len - 1;
1020
if (strncmp(URL, mailto_proto, mailto_proto_len) == 0)
1021
start = URL + mailto_proto_len - 1;
1024
if(start && start[1] == '/' && start[2] == '/') {
1025
/* has a valid protocol, it is a URL */
1028
start = accept_anyproto ? strchr(URL, ':') : start;
1030
/* validate URI scheme */
1031
if(validate_uri_ialpha(URL, start)) {
1033
if (start[1] == '/') {
1042
start = URL; /* scheme invalid */
1046
end = strchr(p, '/');
1048
end = p + strlen(p);
1050
if (!has_proto && (q = memchr(p, '@', end-p))) {
1051
/* don't phishcheck if displayed URL is email, but do phishcheck if
1052
* foo.TLD@host is used */
1053
const char *q2 = q-1;
1054
while (q2 > p && *q2 != '.') q2--;
1055
if (q2 == p || !in_tld_set(q2+1, q-q2-1))
1064
if(!validate_uri_xpalphas_nodot(p, q))
1066
if (accept_anyproto && in_tld_set(p, q-p))
1071
if (p == start) /* must have at least one dot in the URL */
1075
while (*end == ' ' && end > p) --end;
1077
if (in_tld_set(p, end - p))
1079
if (!accept_anyproto)
1082
*last_tld_end = '\0';
1089
* Check if this is a real URL, which basically means to check if it has a known URL scheme (http,https,ftp).
1090
* This prevents false positives with outbind:// and blocked:: links.
1093
static int isRealURL(const struct phishcheck* pchk,const char* URL)
1095
return URL ? !cli_regexec(&pchk->preg_realurl,URL,0,NULL,0) : 0;
1170
* This means that no attempt is made to properly recognize 'cid:' URLs
1172
static int isURL(const struct phishcheck* pchk,const char* URL)
1174
return URL ? !regexec(&pchk->preg,URL,0,NULL,0) : 0;
1099
1177
static int isNumericURL(const struct phishcheck* pchk,const char* URL)
1101
return URL ? !cli_regexec(&pchk->preg_numeric,URL,0,NULL,0) : 0;
1179
return URL ? !regexec(&pchk->preg_numeric,URL,0,NULL,0) : 0;
1104
1182
/* Cleans up @urls
1173
1239
return fallback;
1242
static int isEncoded(const char* url)
1244
const char* start=url;
1249
start=strstr(start,"&#");
1251
start=strstr(start,";");
1253
return (cnt-1 >strlen(url)*7/10);/*more than 70% made up of &#;*/
1176
1256
static int whitelist_check(const struct cl_engine* engine,struct url_check* urls,int hostOnly)
1178
1258
return whitelist_match(engine,urls->realLink.data,urls->displayLink.data,hostOnly);
1181
static int hash_match(const struct regex_matcher *rlist, const char *host, size_t hlen, const char *path, size_t plen, int *prefix_matched)
1183
const char *virname;
1186
strncpy(s, host, hlen);
1187
strncpy(s+hlen, path, plen);
1188
s[hlen+plen] = '\0';
1189
cli_dbgmsg("hash lookup for: %s\n",s);
1191
if(rlist->sha256_hashes.bm_patterns) {
1192
const char hexchars[] = "0123456789ABCDEF";
1193
unsigned char h[65];
1194
unsigned char sha256_dig[32];
1198
sha256_init(&sha256);
1199
sha256_update(&sha256, host, hlen);
1200
sha256_update(&sha256, path, plen);
1201
sha256_final(&sha256, sha256_dig);
1203
h[2*i] = hexchars[sha256_dig[i]>>4];
1204
h[2*i+1] = hexchars[sha256_dig[i]&0xf];
1207
cli_dbgmsg("Looking up hash %s for %s(%u)%s(%u)\n", h, host, (unsigned)hlen, path, (unsigned)plen);
1209
if (prefix_matched) {
1210
if (cli_bm_scanbuff(sha256_dig, 4, &virname, NULL, &rlist->hostkey_prefix,0,NULL,NULL) == CL_VIRUS) {
1211
cli_dbgmsg("prefix matched\n");
1212
*prefix_matched = 1;
1217
if (cli_bm_scanbuff(sha256_dig, 32, &virname, NULL, &rlist->sha256_hashes,0,NULL,NULL) == CL_VIRUS) {
1218
cli_dbgmsg("This hash matched: %s\n", h);
1221
cli_dbgmsg("Hash is whitelisted, skipping\n");
1224
return CL_PHISH_HASH1;
1226
return CL_PHISH_HASH2;
1228
return CL_PHISH_HASH0;
1235
#define URL_MAX_LEN 1024
1236
#define COMPONENTS 4
1237
int cli_url_canon(const char *inurl, size_t len, char *urlbuff, size_t dest_len, char **host, size_t *hostlen, const char **path, size_t *pathlen)
1239
char *url, *p, *last;
1240
char *host_begin, *path_begin;
1241
const char *urlend = urlbuff + len;
1242
size_t host_len, path_len;
1245
strncpy(urlbuff, inurl, dest_len);
1246
urlbuff[dest_len] = urlbuff[dest_len+1] = urlbuff[dest_len+2] = '\0';
1249
/* canonicalize only real URLs, with a protocol */
1250
host_begin = strchr(url, ':');
1252
return CL_PHISH_CLEAN;
1255
/* ignore username in URL */
1256
p = strchr(host_begin, '@');
1260
/* repeatedly % unescape characters */
1261
str_hex_to_char(&url, &urlend);
1264
/* skip to beginning of hostname */
1265
while((host_begin < urlend) && *host_begin == '/') ++host_begin;
1266
while(*host_begin == '.' && host_begin < urlend) ++host_begin;
1268
last = strchr(host_begin, '/');
1270
while (p < urlend) {
1271
if (p+2 < urlend && *p == '/' && p[1] == '.' ) {
1275
memmove(p+1, p+3, urlend - p - 3);
1278
else if (p[2] == '.' && (p[3] == '/' || p[3] == '\0') && last) {
1279
/* remove /component/../ */
1281
memmove(last+1, p+4, urlend - p - 4);
1282
urlend -= 3 + (p - last);
1289
p = &url[urlend - url];
1293
while (p < urlend && p+2 < url + dest_len && urlend < urlbuff+dest_len) {
1294
unsigned char c = *p;
1295
if (c <= 32 || c >= 127 || c == '%' || c == '#') {
1296
/* convert non-ascii characters back to % escaped */
1297
const char hexchars[] = "0123456789ABCDEF";
1298
memmove(p+3, p+1, urlend - p - 1);
1300
*p++ = hexchars[c>>4];
1301
*p = hexchars[c&0xf];
1309
/* determine end of hostname */
1310
host_len = strcspn(host_begin, ":/?");
1311
path_begin = host_begin + host_len;
1312
if(host_len <= len) {
1313
/* url without path, use a single / */
1314
memmove(path_begin + 2, path_begin + 1, len - host_len);
1315
*path_begin++ = '/';
1316
*path_begin++ = '\0';
1317
} else path_begin = url+len;
1318
if(url + len >= path_begin) {
1319
path_len = url + len - path_begin + 1;
1320
p = strchr(path_begin, '#');
1324
path_len = p - path_begin;
1331
/* lowercase entire URL */
1332
str_make_lowercase(host_begin, host_len);
1334
*hostlen = host_len;
1335
*pathlen = path_len;
1336
return CL_PHISH_NODECISION;
1339
static int url_hash_match(const struct regex_matcher *rlist, const char *inurl, size_t len)
1341
size_t j, k, ji, ki;
1343
const char *path_begin;
1344
const char *component;
1348
int rc, prefix_matched=0;
1349
const char *lp[COMPONENTS+1];
1350
size_t pp[COMPONENTS+2];
1351
char urlbuff[URL_MAX_LEN+3];/* htmlnorm truncates at 1024 bytes + terminating null + slash + host end null */
1354
if(!rlist || !rlist->sha256_hashes.bm_patterns) {
1355
/* no hashes loaded -> don't waste time canonicalizing and
1362
rc = cli_url_canon(inurl, len, urlbuff, sizeof(urlbuff), &host_begin, &host_len, &path_begin, &path_len);
1363
if (rc == CL_PHISH_CLEAN)
1366
/* get last 5 components of hostname */
1368
component = strrchr(host_begin, '.');
1369
while(component && j > 0) {
1372
} while(*component != '.' && component > host_begin);
1373
if(*component != '.')
1376
lp[j--] = component + 1;
1380
/* get first 5 components of path */
1383
pp[1] = strcspn(path_begin, "?");
1384
if(pp[1] != pp[0]) k = 2;
1387
while(k < COMPONENTS+2) {
1388
p = strchr(path_begin + pp[k-1] + 1, '/');
1389
if(p && p > path_begin) {
1390
pp[k++] = p - path_begin;
1399
for(ji=COMPONENTS+1;ji > j;) {
1400
/* lookup last 2 and 3 components of host, as hostkey prefix,
1401
* if not matched, shortcircuit lookups */
1402
int need_prefixmatch = (count<2 && !prefix_matched) &&
1403
rlist->hostkey_prefix.bm_patterns;
1405
assert(pp[ki] <= path_len);
1406
/* lookup prefix/suffix hashes of URL */
1407
rc = hash_match(rlist, lp[ji], host_begin + host_len - lp[ji] + 1, path_begin, pp[ki],
1408
need_prefixmatch ? &prefix_matched : NULL);
1414
if (count == 2 && !prefix_matched && rlist->hostkey_prefix.bm_patterns) {
1415
/* if hostkey is not matched, don't bother calculating
1416
* hashes for other parts of the URL, they are not in the DB
1418
cli_dbgmsg("hostkey prefix not matched, short-circuiting lookups\n");
1427
1262
/* urls can't contain null pointer, caller must ensure this */
1428
1263
static enum phish_status phishingCheck(const struct cl_engine* engine,struct url_check* urls)
1430
1265
struct url_check host_url;
1431
int rc = CL_PHISH_NODECISION;
1266
enum phish_status rc=CL_PHISH_NODECISION;
1433
1268
const struct phishcheck* pchk = (const struct phishcheck*) engine->phishcheck;
1435
1270
if(!urls->realLink.data)
1436
1271
return CL_PHISH_CLEAN;
1438
cli_dbgmsg("Phishcheck:Checking url %s->%s\n", urls->realLink.data,
1273
cli_dbgmsg("PH:Checking url %s->%s\n", urls->realLink.data,
1439
1274
urls->displayLink.data);
1441
1276
if(!strcmp(urls->realLink.data,urls->displayLink.data))
1442
1277
return CL_PHISH_CLEAN;/* displayed and real URL are identical -> clean */
1444
if(!isURL(urls->realLink.data, 0)) {
1445
cli_dbgmsg("Real 'url' is not url:%s\n",urls->realLink.data);
1446
return CL_PHISH_CLEAN;
1449
if(( rc = url_hash_match(engine->domainlist_matcher, urls->realLink.data, strlen(urls->realLink.data)) )) {
1450
if (rc == CL_PHISH_CLEAN) {
1451
cli_dbgmsg("not analyzing, not a real url: %s\n", urls->realLink.data);
1452
return CL_PHISH_CLEAN;
1454
cli_dbgmsg("Hash matched for: %s\n", urls->realLink.data);
1459
if (urls->displayLink.data[0] == '\0') {
1460
return CL_PHISH_CLEAN;
1463
1279
if((rc = cleanupURLs(urls))) {
1464
/* it can only return an error, or say its clean;
1465
* it is not allowed to decide it is phishing */
1466
return rc < 0 ? rc : CL_PHISH_CLEAN;
1469
cli_dbgmsg("Phishcheck:URL after cleanup: %s->%s\n", urls->realLink.data,
1470
urls->displayLink.data);
1472
if((!isURL(urls->displayLink.data, 1) ) &&
1473
( (phishy&PHISHY_NUMERIC_IP && !isNumericURL(pchk, urls->displayLink.data)) ||
1474
!(phishy&PHISHY_NUMERIC_IP))) {
1475
cli_dbgmsg("Displayed 'url' is not url:%s\n",urls->displayLink.data);
1476
return CL_PHISH_CLEAN;
1479
if(whitelist_check(engine, urls, 0))
1480
return CL_PHISH_CLEAN;/* if url is whitelisted don't perform further checks */
1280
if(isPhishing(rc))/* not allowed to decide this is phishing */
1281
return CL_PHISH_CLEAN;
1282
return rc;/* URLs identical after cleanup */
1285
if(whitelist_check(engine,urls,0))
1286
return CL_PHISH_WHITELISTED;/* if url is whitelist don't perform further checks */
1288
if(urls->flags&DOMAINLIST_REQUIRED && domainlist_match(engine,urls->realLink.data,urls->displayLink.data,0,&urls->flags))
1289
phishy |= DOMAIN_LISTED;
1291
/* although entire url is not listed, the host might be,
1292
* so defer phishing decisions till we know if host is listed*/
1482
1296
url_check_init(&host_url);
1484
if((rc = url_get_host(urls, &host_url, DOMAIN_DISPLAY, &phishy))) {
1298
if((rc = url_get_host(pchk, urls,&host_url,DOMAIN_DISPLAY,&phishy))) {
1485
1299
free_if_needed(&host_url);
1486
return rc < 0 ? rc : CL_PHISH_CLEAN;
1489
if (domainlist_match(engine, host_url.displayLink.data,host_url.realLink.data,&urls->pre_fixup,1)) {
1490
phishy |= DOMAIN_LISTED;
1301
return CL_PHISH_CLEAN;
1306
if(urls->flags&DOMAINLIST_REQUIRED) {
1307
if(!(phishy&DOMAIN_LISTED)) {
1308
if(domainlist_match(engine,host_url.displayLink.data,host_url.realLink.data,1,&urls->flags))
1309
phishy |= DOMAIN_LISTED;
1315
/* link type filtering must occur after last domainlist_match */
1316
if(urls->link_type & LINKTYPE_IMAGE && !(urls->flags&CHECK_IMG_URL))
1317
return CL_PHISH_HOST_NOT_LISTED;/* its listed, but this link type is filtered */
1319
if(urls->flags & DOMAINLIST_REQUIRED && !(phishy & DOMAIN_LISTED) ) {
1492
1320
urls->flags &= urls->always_check_flags;
1493
/* don't return, we may need to check for ssl/cloaking */
1496
/* link type filtering must occur after last domainlist_match */
1497
if(urls->link_type & LINKTYPE_IMAGE && !(urls->flags&CHECK_IMG_URL)) {
1498
free_if_needed(&host_url);
1499
return CL_PHISH_CLEAN;/* its listed, but this link type is filtered */
1322
free_if_needed(&host_url);
1323
return CL_PHISH_HOST_NOT_LISTED;
1502
1327
if(urls->flags&CHECK_CLOAKING) {
1503
1328
/*Checks if URL is cloaked.
1504
Should we check if it contains another http://, https://?
1329
Should we check if it containts another http://, https://?
1505
1330
No because we might get false positives from redirect services.*/
1506
1331
if(strchr(urls->realLink.data,0x1)) {
1507
1332
free_if_needed(&host_url);
1508
1333
return CL_PHISH_CLOAKED_NULL;
1335
if(isEncoded(urls->displayLink.data)) {
1336
free_if_needed(&host_url);
1337
return CL_PHISH_HEX_URL;
1342
if(urls->displayLink.data[0]=='\0') {
1343
free_if_needed(&host_url);
1344
return CL_PHISH_CLEAN;
1512
1347
if(urls->flags&CHECK_SSL && isSSL(urls->displayLink.data) && !isSSL(urls->realLink.data)) {
1514
1349
return CL_PHISH_SSL_SPOOF;
1517
if (!(phishy & DOMAIN_LISTED)) {
1352
if(!urls->flags&CHECK_CLOAKING && urls->flags & DOMAINLIST_REQUIRED && !(phishy&DOMAIN_LISTED) ) {
1518
1353
free_if_needed(&host_url);
1519
return CL_PHISH_CLEAN;
1354
return CL_PHISH_HOST_NOT_LISTED;
1522
if((rc = url_get_host(urls,&host_url,DOMAIN_REAL,&phishy)))
1357
if((rc = url_get_host(pchk, urls,&host_url,DOMAIN_REAL,&phishy)))
1524
1359
free_if_needed(&host_url);
1525
return rc < 0 ? rc : CL_PHISH_CLEAN;
1363
if(urls->flags&DOMAINLIST_REQUIRED && !(phishy&DOMAIN_LISTED)) {
1364
free_if_needed(&host_url);
1365
return CL_PHISH_HOST_NOT_LISTED;
1368
if(!strncmp(urls->displayLink.data,cid,cid_len))/* cid: image */{
1369
free_if_needed(&host_url);
1370
return CL_PHISH_CLEAN_CID;
1528
1373
if(whitelist_check(engine,&host_url,1)) {
1529
1374
free_if_needed(&host_url);
1530
return CL_PHISH_CLEAN;
1375
return CL_PHISH_HOST_WHITELISTED;
1533
if(!strcmp(urls->realLink.data,urls->displayLink.data)) {
1378
if(!isURL(pchk, urls->displayLink.data) &&
1379
( (phishy&PHISHY_NUMERIC_IP && !isNumericURL(pchk, urls->displayLink.data)) ||
1380
!(phishy&PHISHY_NUMERIC_IP))) {
1534
1381
free_if_needed(&host_url);
1535
return CL_PHISH_CLEAN;
1382
return CL_PHISH_TEXTURL;
1539
struct url_check domain_url;
1540
url_check_init(&domain_url);
1541
url_get_domain(&host_url,&domain_url);
1542
if(!strcmp(domain_url.realLink.data,domain_url.displayLink.data)) {
1385
if(urls->flags&HOST_SUFFICIENT) {
1386
if(!strcmp(urls->realLink.data,urls->displayLink.data)) {
1543
1387
free_if_needed(&host_url);
1388
return CL_PHISH_HOST_OK;
1392
if(urls->flags&DOMAIN_SUFFICIENT) {
1393
struct url_check domain_url;
1394
url_check_init(&domain_url);
1395
url_get_domain(pchk, &host_url,&domain_url);
1396
if(!strcmp(domain_url.realLink.data,domain_url.displayLink.data)) {
1397
free_if_needed(&host_url);
1398
free_if_needed(&domain_url);
1399
return CL_PHISH_DOMAIN_OK;
1544
1401
free_if_needed(&domain_url);
1545
return CL_PHISH_CLEAN;
1547
free_if_needed(&domain_url);
1550
free_if_needed(&host_url);
1551
/*we failed to find a reason why the 2 URLs are different, this is definitely phishing*/
1404
/*if(urls->flags&CHECK_REDIR) {
1405
//see where the realLink redirects, and compare that with the displayed Link
1406
const uchar* redirectedURL = getRedirectedURL(urls->realLink);
1408
free(urls->realLink);
1409
urls->realLink = redirectedURL;
1411
if(!strcmp(urls->realLink,urls->displayLink))
1412
return CL_PHISH_REDIR_OK;
1414
if(urls->flags&HOST_SUFFICIENT) {
1415
if(rc = url_get_host(urls,&host_url,DOMAIN_REAL))
1416
if(!strcmp(host_url.realLink,host_url.displayLink)) {
1417
free_if_needed(&host_url);
1418
return CL_PHISH_HOST_REDIR_OK;
1420
if(urls->flags&DOMAIN_SUFFICIENT) {
1421
struct url_check domain_url;
1422
url_get_domain(&host_url,&domain_url);
1423
if(!strcmp(domain_url.realLink,domain_url.displayLink)) {
1424
free_if_needed(&host_url);
1425
free_if_needed(&domain_url);
1426
return CL_PHISH_DOMAIN_REDIR_OK;
1429
}//HOST_SUFFICIENT&CHECK_REDIR
1431
free_if_needed(&host_url);*/
1432
/* if(urls->flags&CHECK_DOMAIN_REVERSE) {
1433
//do a DNS lookup of the domain, and see what IP it corresponds to
1434
//then do a reverse lookup on the IP, and see what domain you get
1435
//There are some corporate signatures that mix different domains belonging to same company
1436
struct url_check domain_url;
1437
url_check_init(&domain_url);
1438
if(!dns_to_ip_and_reverse(&host_url,DOMAIN_DISPLAY)) {
1439
if(!strcmp(host_url.realLink.data,host_url.displayLink.data)) {
1440
free_if_needed(&host_url);
1441
return CL_PHISH_HOST_REVERSE_OK;
1443
if(urls->flags&DOMAIN_SUFFICIENT) {
1444
url_get_domain(&host_url,&domain_url);
1445
if(!strcmp(domain_url.realLink.data,domain_url.displayLink.data)) {
1446
free_if_needed(&host_url);
1447
free_if_needed(&domain_url);
1448
return CL_PHISH_DOMAIN_REVERSE_OK;
1450
free_if_needed(&domain_url);
1454
free_if_needed(&host_url);
1455
}/*HOST_SUFFICIENT*/
1456
/*we failed to find a reason why the 2 URLs are different, this is definetely phishing*/
1457
if(urls->flags&DOMAINLIST_REQUIRED && !(phishy&DOMAIN_LISTED))
1458
return CL_PHISH_HOST_NOT_LISTED;
1552
1459
return phishy_map(phishy,CL_PHISH_NOMATCH);