1 /*
2  * ModSecurity, http://www.modsecurity.org/
3  * Copyright (c) 2015 - 2021 Trustwave Holdings, Inc. (http://www.trustwave.com/)
4  *
5  * You may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * If any of the files related to licensing are missing or if you have any
11  * other questions related to licensing please contact Trustwave Holdings, Inc.
12  * directly using the email address security@modsecurity.org.
13  *
14  */
15 
16 #include "src/operators/verify_cc.h"
17 
18 #include <pcre.h>
19 #include <iostream>
20 #include <cstring>
21 #include <vector>
22 
23 #include "src/operators/operator.h"
24 
25 #if PCRE_HAVE_JIT
26 #define pcre_study_opt PCRE_STUDY_JIT_COMPILE
27 #else
28 #define pcre_study_opt 0
29 #endif
30 
31 
32 namespace modsecurity {
33 namespace operators {
34 
~VerifyCC()35 VerifyCC::~VerifyCC() {
36     if (m_pc != NULL) {
37         pcre_free(m_pc);
38         m_pc = NULL;
39     }
40     if (m_pce != NULL) {
41 #if PCRE_HAVE_JIT
42         pcre_free_study(m_pce);
43 #else
44         pcre_free(m_pce);
45 #endif
46         m_pce = NULL;
47     }
48 }
49 
50 /**
51  * Luhn Mod-10 Method (ISO 2894/ANSI 4.13)
52  */
luhnVerify(const char * ccnumber,int len)53 int VerifyCC::luhnVerify(const char *ccnumber, int len) {
54     int sum[2] = { 0, 0 };
55     int odd = 0;
56     int digits = 0;
57     int i;
58 
59     /* Weighted lookup table which is just a precalculated (i = index):
60      *   i*2 + (( (i*2) > 9 ) ? -9 : 0)
61      */
62     /* weight lookup table */
63     static const int wtable[10] = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9};
64 
65 
66     /* Add up only digits (weighted digits via lookup table)
67      * for both odd and even CC numbers to avoid 2 passes.
68      */
69     for (i = 0; i < len; i++) {
70         if (ccnumber[i] >= (0 + 48) && ccnumber[i] <= (9 + 48)) {
71             sum[0] += (!odd ? wtable[ccnumber[i] - '0'] : (ccnumber[i] - '0'));
72             sum[1] += (odd ? wtable[ccnumber[i] - '0'] : (ccnumber[i] - '0'));
73             odd = 1 - odd; /* alternate weights */
74             digits++;
75         }
76     }
77 
78     /* No digits extracted */
79     if (digits == 0) {
80         return 0;
81     }
82 
83     /* Do a mod 10 on the sum */
84     sum[odd] %= 10;
85 
86     /* If the result is a zero the card is valid. */
87     return sum[odd] ? 0 : 1;
88 }
89 
90 
91 
init(const std::string & param2,std::string * error)92 bool VerifyCC::init(const std::string &param2, std::string *error) {
93     const char *errptr = NULL;
94     int erroffset = 0;
95 
96     m_pc = pcre_compile(m_param.c_str(), PCRE_DOTALL|PCRE_MULTILINE,
97         &errptr, &erroffset, NULL);
98     if (m_pc == NULL) {
99         error->assign(errptr);
100         return false;
101     }
102 
103     m_pce = pcre_study(m_pc, pcre_study_opt, &errptr);
104     if (m_pce == NULL) {
105         if (errptr == NULL) {
106             /*
107              * Per pcre_study(3) m_pce == NULL && errptr == NULL means
108              * that no addional information is found, so no need to study
109              */
110             return true;
111         }
112         error->assign(errptr);
113         return false;
114     }
115 
116     return true;
117 }
118 
119 
evaluate(Transaction * t,RuleWithActions * rule,const std::string & i,std::shared_ptr<RuleMessage> ruleMessage)120 bool VerifyCC::evaluate(Transaction *t, RuleWithActions *rule,
121     const std::string& i, std::shared_ptr<RuleMessage> ruleMessage) {
122     int offset = 0;
123     int target_length = i.length();
124 
125     for (offset = 0; offset < target_length; offset++) {
126         std::string match;
127         int ovector[33];
128         memset(ovector, 0, sizeof(ovector));
129         int ret = pcre_exec(m_pc, m_pce, i.c_str(), i.size(), offset,
130             0, ovector, 33) > 0;
131 
132         /* If there was no match, then we are done. */
133         if (ret == PCRE_ERROR_NOMATCH) {
134             break;
135         }
136         if (ret < 0) {
137             return false;
138         }
139         if (ret > 0) {
140             match = std::string(i, ovector[0], ovector[1] - ovector[0]);
141             int is_cc = luhnVerify(match.c_str(), match.size());
142             if (is_cc) {
143                 if (t) {
144                     if (rule && rule->hasCaptureAction()) {
145                         t->m_collections.m_tx_collection->storeOrUpdateFirst(
146                             "0", std::string(match));
147                         ms_dbg_a(t, 7, "Added VerifyCC match TX.0: " + \
148                             std::string(match));
149                     }
150                     ms_dbg_a(t, 9, "CC# match \"" + m_param +
151                         "\" at " + i + ". [offset " +
152                         std::to_string(offset) + "]");
153                 }
154                 return true;
155             }
156         }
157     }
158 
159     return false;
160 }
161 
162 
163 }  // namespace operators
164 }  // namespace modsecurity
165 
166