File README changed (mode: 100644) (index 3aad56c..ef115c0) |
... |
... |
Name: nf2fa |
2 |
2 |
Description: Port knocking meets two-factor authentication (2fa) |
Description: Port knocking meets two-factor authentication (2fa) |
3 |
3 |
Start date: 1st Feb 2018 |
Start date: 1st Feb 2018 |
4 |
4 |
Author: Catalin(ux) M. BOIE |
Author: Catalin(ux) M. BOIE |
|
5 |
|
Code: https://rocketgit.com/user/catalinux/nf2fa |
5 |
6 |
|
|
6 |
|
Why we need this? |
|
7 |
|
- Because classic port knocking can be replicated from anywhere, if the attacker |
|
8 |
|
can "see" the packets. |
|
9 |
|
- With a digital signature program, you need some code on the client side. |
|
10 |
|
- |
|
|
7 |
|
|
|
8 |
|
. How it works? |
|
9 |
|
By default your firewall will drop all packets. With a special packet, which |
|
10 |
|
will contain a code generated by your mobile device, you will be able to open |
|
11 |
|
the firewall only from your connecting IP. Also you will be able to close |
|
12 |
|
the firewall as soon as you do not need it open anymore. |
|
13 |
|
|
|
14 |
|
|
|
15 |
|
. Installation & configuration |
|
16 |
|
After installation, edit the configuration file (/etc/nf2fa.conf) and set |
|
17 |
|
the desired parameters. Then, start the daemon. |
|
18 |
|
You will run 'nf2fa enroll' command to enroll a new device. |
|
19 |
|
You will need to type a password and then confirm it. |
|
20 |
|
Install FreeIPA or Google Authenticator on your mobile device and scan the QR |
|
21 |
|
code shown on the screen. Now your user is enrolled. |
|
22 |
|
This procedure will set the same key on both server and mobile device. |
|
23 |
|
Now, your 2fa application will be able to generate some time based |
|
24 |
|
tokens (6 digit codes). |
|
25 |
|
Check below how to setup your firewall. The rules will force control packets |
|
26 |
|
to hit nf2fa daemon and will trigger the validation. |
|
27 |
|
On another machine, you will need to run the special ping/nc command to open |
|
28 |
|
the firewall. For this, you will need the password choosed at enroll time and |
|
29 |
|
the 6 digits token. See below the examples. |
|
30 |
|
Please note that the time must be in sync on both server and mobile device |
|
31 |
|
because the tokens are time dependent. |
|
32 |
|
|
|
33 |
|
|
|
34 |
|
. Why you need this program? |
|
35 |
|
- Because classic port knocking can be replicated from anywhere, if the |
|
36 |
|
attacker can "watch" the packets. |
|
37 |
|
- With a digital signature program, you need some code on the client side, |
|
38 |
|
which may not be available for all platforms. |
|
39 |
|
- This program allows hosts to be completely silent, to not answer any |
|
40 |
|
request from outside: no code exposed to attacks and no log pollution. |
|
41 |
|
|
|
42 |
|
|
|
43 |
|
. Firewall preparation |
|
44 |
|
For iptables: |
|
45 |
|
# Connections already marked by nf2fad are accepted |
|
46 |
|
-A INPUT -i ethX -m connmark --mark 0x77777777 -j ACCEPT |
|
47 |
|
# Packets marked by nf2fad but with no connection tracking marking, |
|
48 |
|
# will trigger the saving into connection tracking table |
|
49 |
|
-A INPUT -i ethX -m mark --mark 0x77777777 -j CONNMARK --save-mark |
|
50 |
|
# We need this rule again, to not hit again nf2fad. |
|
51 |
|
-A INPUT -i ethX -m connmark --mark 0x77777777 -j ACCEPT |
|
52 |
|
# These are the control channels: |
|
53 |
|
-A INPUT -i ethX -p icmp --icmp-type echo-request -j NFQUEUE --queue-num 4444 |
|
54 |
|
-A INPUT -i ethX -p udp --dport 1 -j NFQUEUE --queue-num 4444 |
|
55 |
|
# Now, what ports to allow. |
|
56 |
|
# Next rule will pass port 22 (for example) to nf2fa and the connection will |
|
57 |
|
# be marked if allowed. You may add here other ports as well. |
|
58 |
|
-A INPUT -i ethX -p tcp --dport 22 -j NFQUEUE --queue-num 4444 |
|
59 |
|
|
|
60 |
|
For ip6tables: |
|
61 |
|
<insert here the IPv4 rules till the control channels> |
|
62 |
|
-A INPUT -i ethX -p ipv6-icmp --icmpv6-type echo-request -j NFQUEUE --queue-num 4444 |
|
63 |
|
-A INPUT -i ethX -p udp --dport 1 -j NFQUEUE --queue-num 4444 |
|
64 |
|
-A INPUT -i ethX -p tcp --dport 22 -j NFQUEUE --queue-num 4444 |
|
65 |
|
|
|
66 |
|
Notes: |
|
67 |
|
- Above, only SSH port was activated, but you may add any other protocol or |
|
68 |
|
ports. |
|
69 |
|
- These should be the first rules. If you add rules allowing ESTABLISHED conns |
|
70 |
|
before these, you will not be able to close the firewall or renew the timer. |
|
71 |
|
- Of course, you must have final DROP rule which drops anything. |
|
72 |
|
|
|
73 |
|
|
|
74 |
|
. Example enrollment: |
|
75 |
|
$ nf2fac enroll MySpecialPhone1 |
|
76 |
|
Password (only 0-9 and a-f): <type a password> |
|
77 |
|
Password (confirmation): <type the confirmation> |
|
78 |
|
Key is QDCAXNGWJWCIOSI5 (id 001). Scan the QR code below or type the key. |
|
79 |
|
█████████████████████████████████████████ |
|
80 |
|
██ ▄▄▄▄▄ █▄ ███▄█▄█▄█▀▄ ██ ▀█▀▄█ ▄▄▄▄▄ ██ |
|
81 |
|
██ █ █ █▄ █▄ ▀▄ █▄▀ ▄█▀ ▀▄██ █ █ ██ |
|
82 |
|
██ █▄▄▄█ █▀ ▄▄▄▄▀ ▄█▀█▄ ██ █▄█▀█ █▄▄▄█ ██ |
|
83 |
|
██▄▄▄▄▄▄▄█▄▀ ▀▄█▄█ █▄█▄▀▄█ ▀ ▀ █▄▄▄▄▄▄▄██ |
|
84 |
|
██▄▀▄█▀▀▄ ▀ █▀▀▄█▄ ██ ▄██ ▀█▀█ █▀ ▀ ██ |
|
85 |
|
███▀█▀ ▀▄█ ▀ ▄█ ▄▀ █▀▀ ▄▄█▄▄▀ █▀▀ ▄▀██▄██ |
|
86 |
|
██▄ ▀▀ ▄▄▄▀▀▄▀█▄▄▀██▀ ███▀▄▀▀ ▄▄ ▄█▄▄▀▄██ |
|
87 |
|
██▀▄▄ ▄▄ ▀▄▄▄█▀▄ █▀▄▄▀█▄▀ ▄▄▀█▄▀ ▄▄███ |
|
88 |
|
██▀▄█ ▄ ▄ █ ▄██ █▀ ██▀ ▄█ ▄▀█ █▀ ██ |
|
89 |
|
██▀▄▀▄█ ▄█ ▀ ▀ ▄██ ▀▀▄ ▄█▄ ▄ ▀▀▀█ ▀ ██ |
|
90 |
|
██▄█▀▄▄▀▄ ▄ ▀ ▀ ▀▀█ ███▀▄▀ ▀▀▀ ███▄████ |
|
91 |
|
██▄ ▀█ ▄ ▀▄█▀ █▄▄▀▄ ▄▄▄ ▄▄▀▄▄ ▀▄▄ ▀▄▄███ |
|
92 |
|
██▄█ ▄▄▄▄ ██▀ ▄ █ ██ ██ ▄▄ ▄██▀█▄ ██ |
|
93 |
|
██▄▄▄ ▄▀█ ▄▄▀ ▄▀ ▄▀▄█▄█▀ █▄ ▄█▄▄ █▀█ ██ |
|
94 |
|
██▄▄▄▄██▄█ ▀ ▀ ▄█▀ ██▀██▄▀██▄▀ ▄▄▄ ▀▄▄▀██ |
|
95 |
|
██ ▄▄▄▄▄ █▄▀▄▄▀▄▄ ▄▀▀ ▄▀▄ ▄▀▄▀ █▄█ ▀▄ ▄██ |
|
96 |
|
██ █ █ ██ ▄█▀▀ ▄▄▄▄ ██ ▄██ ▄▄ ███▀██ |
|
97 |
|
██ █▄▄▄█ █▄▀█▀▀▄▀▄▄█ ▄▄▀▄█▄▀▀▀▄▀█▄▀▄▄▄██ |
|
98 |
|
██▄▄▄▄▄▄▄█▄▄▄██▄█▄███▄██▄███▄█▄█▄█▄███▄██ |
|
99 |
|
█████████████████████████████████████████ |
|
100 |
|
|
|
101 |
|
|
|
102 |
|
. List enrollments: |
|
103 |
|
$ nf2fac list |
|
104 |
|
|
|
105 |
|
Sample output: |
|
106 |
|
No Enroll time Name |
|
107 |
|
000 2018-02-13 22:46 key1 |
|
108 |
|
Last access from IP 192.168.79.155 on 2018-02-13 23:36 |
|
109 |
|
001 2018-02-13 23:44 key2 |
|
110 |
|
Last access from IP 192.168.79.155 on 2018-02-14 00:44 |
|
111 |
|
002 2018-03-04 09:13 aaa |
|
112 |
|
003 2018-03-04 09:15 aaa |
|
113 |
|
|
|
114 |
|
|
|
115 |
|
. Un-enrolling |
|
116 |
|
$ nf2fac unenroll 003 |
|
117 |
|
|
|
118 |
|
Sample outputs: |
|
119 |
|
Successfully un-enrolled. |
|
120 |
|
or: |
|
121 |
|
Error: key id not found! |
|
122 |
|
|
|
123 |
|
|
|
124 |
|
. How to send commands from the client: |
|
125 |
|
$ ping -c1 destination -p aaPP...PPIIIIIICC |
|
126 |
|
Where: |
|
127 |
|
aa is the start of the command (just type two 'a' letters) |
|
128 |
|
PP...PP is the password choosed at enrollment phase |
|
129 |
|
IIIIII is the 6 digit pin generated by the 2fa application |
|
130 |
|
CC is the command |
|
131 |
|
11 - open firewall |
|
132 |
|
cc - close firewall |
|
133 |
|
|
|
134 |
|
Sample: |
|
135 |
|
|
|
136 |
|
# Unlocking my IP |
|
137 |
|
PASSWORD="12fa45" |
|
138 |
|
PIN="082653" |
|
139 |
|
$ ping -c1 172.30.43.4 -p aa${PASSWORD}${PIN}11 |
|
140 |
|
|
|
141 |
|
# Locking my IP |
|
142 |
|
PASSWORD="12fa45" |
|
143 |
|
PIN="539252" |
|
144 |
|
$ ping -c1 172.30.43.4 -p aa${PASSWORD}${PIN}cc |
|
145 |
|
|
|
146 |
|
If an answer is received, the command executed with success. |
|
147 |
|
On timeout, an error occured. |
|
148 |
|
|
|
149 |
|
Note that you can send any type of packet, not only ICMP. |
|
150 |
|
Here, we will use UDP: |
|
151 |
|
# On the server (we use here port 1): |
|
152 |
|
# -A INPUT -i ethX -p udp --dport 1 -j NFQUEUE --queue-num 4444 |
|
153 |
|
PASSWORD="12fa45" |
|
154 |
|
PIN="539252" |
|
155 |
|
$ echo "aa${PASSWORD}$PIN}11" | nc --udp -4 172.30.43.4 1 |
|
156 |
|
# No answer will be received |
File nf2fad.c changed (mode: 100644) (index cf7745b..4244d60) |
31 |
31 |
#include <sys/capability.h> |
#include <sys/capability.h> |
32 |
32 |
#include <grp.h> |
#include <grp.h> |
33 |
33 |
#include <pwd.h> |
#include <pwd.h> |
|
34 |
|
#include <ctype.h> |
34 |
35 |
#include <arpa/inet.h> |
#include <arpa/inet.h> |
35 |
36 |
#include <netinet/ip.h> |
#include <netinet/ip.h> |
36 |
37 |
#include <netinet/ip6.h> |
#include <netinet/ip6.h> |
|
45 |
46 |
|
|
46 |
47 |
#include <libnetfilter_queue/libnetfilter_queue.h> |
#include <libnetfilter_queue/libnetfilter_queue.h> |
47 |
48 |
|
|
|
49 |
|
#include <linux/netfilter/nfnetlink_conntrack.h> |
|
50 |
|
|
|
51 |
|
|
48 |
52 |
#include "nf2fa_common.h" |
#include "nf2fa_common.h" |
49 |
53 |
#include "util.h" |
#include "util.h" |
50 |
54 |
#include "conf.h" |
#include "conf.h" |
|
... |
... |
static struct nlmsghdr *nfq_hdr_put(unsigned char *buf, const int type, |
92 |
96 |
} |
} |
93 |
97 |
|
|
94 |
98 |
static int send_verdict(const int queue_num, const uint32_t id, |
static int send_verdict(const int queue_num, const uint32_t id, |
95 |
|
const int verdict) |
|
|
99 |
|
const int verdict, const unsigned int mark) |
96 |
100 |
{ |
{ |
97 |
101 |
unsigned char buf[MNL_SOCKET_BUFFER_SIZE]; |
unsigned char buf[MNL_SOCKET_BUFFER_SIZE]; |
98 |
102 |
struct nlmsghdr *nlh; |
struct nlmsghdr *nlh; |
|
103 |
|
struct nlattr *nest; |
99 |
104 |
|
|
100 |
105 |
nlh = nfq_hdr_put(buf, NFQNL_MSG_VERDICT, queue_num); |
nlh = nfq_hdr_put(buf, NFQNL_MSG_VERDICT, queue_num); |
101 |
106 |
nfq_nlmsg_verdict_put(nlh, id, verdict); |
nfq_nlmsg_verdict_put(nlh, id, verdict); |
|
107 |
|
|
|
108 |
|
nfq_nlmsg_verdict_put_mark(nlh, htonl(mark)); |
|
109 |
|
|
|
110 |
|
/* Set the connmark. First, start NFQA_CT section: */ |
|
111 |
|
nest = mnl_attr_nest_start(nlh, NFQA_CT); |
|
112 |
|
if (!nest) { |
|
113 |
|
xlog("%s: mnl_attr_nest_start: %s\n", __func__, strerror(errno)); |
|
114 |
|
return -1; |
|
115 |
|
} |
|
116 |
|
|
|
117 |
|
/* then, add the connmark attribute: */ |
|
118 |
|
mnl_attr_put_u32(nlh, CTA_MARK, htonl(mark)); |
|
119 |
|
/* TODO: it is not working because Fedora kernel, at least |
|
120 |
|
* does not enable needed config option *_GLUE_CT */ |
|
121 |
|
|
|
122 |
|
/* more conntrack attributes, e.g. CTA_LABEL/ZONE, could be set here */ |
|
123 |
|
|
|
124 |
|
/* end conntrack section */ |
|
125 |
|
mnl_attr_nest_end(nlh, nest); |
|
126 |
|
|
102 |
127 |
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { |
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { |
103 |
|
xlog("mnl_socket_send send_verdict: %s\n", strerror(errno)); |
|
|
128 |
|
xlog("%s: mnl_socket_send: %s\n", __func__, strerror(errno)); |
104 |
129 |
return -1; |
return -1; |
105 |
130 |
} |
} |
106 |
131 |
|
|
|
... |
... |
static int send_verdict(const int queue_num, const uint32_t id, |
111 |
136 |
* Try to find the code inside the packet |
* Try to find the code inside the packet |
112 |
137 |
* Returns 0 if not ok, else, the command |
* Returns 0 if not ok, else, the command |
113 |
138 |
*/ |
*/ |
114 |
|
static int test_2fa(const unsigned char *data, const unsigned int data_len, |
|
|
139 |
|
static int test_2fa_bin(const unsigned char *data, const unsigned int data_len, |
115 |
140 |
const unsigned int now, const char *ip) |
const unsigned int now, const char *ip) |
116 |
141 |
{ |
{ |
117 |
142 |
unsigned short i, j, start, last_check = 0xFFFF; |
unsigned short i, j, start, last_check = 0xFFFF; |
|
... |
... |
static int test_2fa(const unsigned char *data, const unsigned int data_len, |
122 |
147 |
char pin[7]; |
char pin[7]; |
123 |
148 |
int r; |
int r; |
124 |
149 |
|
|
125 |
|
xlog("DEBUG: %s: %s\n", __func__, ip); |
|
|
150 |
|
if (data_len == 0) |
|
151 |
|
return 0; |
|
152 |
|
|
|
153 |
|
xlog("DEBUG: %s: %s data_len=%u\n", __func__, ip, data_len); |
126 |
154 |
dump(data, data_len); |
dump(data, data_len); |
127 |
155 |
|
|
128 |
156 |
// Find first 0xAA |
// Find first 0xAA |
|
... |
... |
static int test_2fa(const unsigned char *data, const unsigned int data_len, |
138 |
166 |
while (k) { |
while (k) { |
139 |
167 |
unsigned char pass_len, needed; |
unsigned char pass_len, needed; |
140 |
168 |
|
|
141 |
|
xlog("DEBUG: uid %u key %03hhu pass=%s\n", |
|
142 |
|
q->uid, k->id, k->pass); |
|
|
169 |
|
xlog("DEBUG: uid %u key %03hhu\n", q->uid, k->id); |
143 |
170 |
|
|
144 |
171 |
pass_len = strlen(k->pass) / 2; |
pass_len = strlen(k->pass) / 2; |
145 |
172 |
needed = 1 + pass_len + 3 + 1; // 0xaa + pass + pin + cmd |
needed = 1 + pass_len + 3 + 1; // 0xaa + pass + pin + cmd |
|
... |
... |
static int test_2fa(const unsigned char *data, const unsigned int data_len, |
150 |
177 |
unsigned short off; |
unsigned short off; |
151 |
178 |
|
|
152 |
179 |
if (i + needed > data_len) { |
if (i + needed > data_len) { |
153 |
|
xlog("\tDEBUG: too little data: i(%u) + needed(%hhu) > data_len(%hu)\n", |
|
154 |
|
i, needed, data_len); |
|
|
180 |
|
//xlog("\tDEBUG: too little data: i(%u) + needed(%hhu) > data_len(%hu)\n", |
|
181 |
|
// i, needed, data_len); |
155 |
182 |
break; |
break; |
156 |
183 |
} |
} |
157 |
184 |
|
|
|
... |
... |
static int test_2fa(const unsigned char *data, const unsigned int data_len, |
160 |
187 |
continue; |
continue; |
161 |
188 |
} |
} |
162 |
189 |
|
|
163 |
|
xlog("\tDEBUG: i=%hu 0x%02hhx\n", i, data[i]); |
|
164 |
|
|
|
165 |
|
// Optimization |
|
|
190 |
|
// Optimization - skip checking same string |
166 |
191 |
if ((last_check != 0xFFFF) |
if ((last_check != 0xFFFF) |
167 |
192 |
&& (memcmp(data + last_check, data + i, needed) == 0)) { |
&& (memcmp(data + last_check, data + i, needed) == 0)) { |
168 |
|
//xlog("\tDEBUG: same string\n"); |
|
169 |
193 |
i += needed; |
i += needed; |
170 |
194 |
continue; |
continue; |
171 |
195 |
} |
} |
|
... |
... |
static int test_2fa(const unsigned char *data, const unsigned int data_len, |
207 |
231 |
switch (cmd) { |
switch (cmd) { |
208 |
232 |
case 0x11: // open |
case 0x11: // open |
209 |
233 |
snprintf(k->ip, sizeof(k->ip), "%s", ip); |
snprintf(k->ip, sizeof(k->ip), "%s", ip); |
210 |
|
// TODO: maybe we shoul not allow access if we cannot save key? But it is saved in memory! |
|
|
234 |
|
// TODO: maybe we should not allow access if we cannot save key? But it is saved in memory! |
211 |
235 |
xlog("%s: uid %u: key %03hhu: authorized\n", |
xlog("%s: uid %u: key %03hhu: authorized\n", |
212 |
236 |
ip, q->uid, k->id); |
ip, q->uid, k->id); |
213 |
237 |
break; |
break; |
|
... |
... |
static int test_2fa(const unsigned char *data, const unsigned int data_len, |
232 |
256 |
return 0; |
return 0; |
233 |
257 |
} |
} |
234 |
258 |
|
|
|
259 |
|
/* |
|
260 |
|
* Binary version |
|
261 |
|
*/ |
|
262 |
|
static int test_2fa(const unsigned char *data, const unsigned int data_len, |
|
263 |
|
const unsigned int now, const char *ip) |
|
264 |
|
{ |
|
265 |
|
int r; |
|
266 |
|
unsigned char buf[4096]; |
|
267 |
|
unsigned short i, j, max; |
|
268 |
|
|
|
269 |
|
// This test is for ICMP packets |
|
270 |
|
r = test_2fa_bin(data, data_len, now, ip); |
|
271 |
|
if (r > 0) |
|
272 |
|
return r; |
|
273 |
|
|
|
274 |
|
// In UDP packets, we have 'aa' as 0x61 0x61. |
|
275 |
|
// We try to find this string and test from there. |
|
276 |
|
// We will not try packets odd aligned. |
|
277 |
|
|
|
278 |
|
max = sizeof(buf); |
|
279 |
|
if (data_len / 2 < max) |
|
280 |
|
max = data_len / 2; |
|
281 |
|
|
|
282 |
|
// Find first marker |
|
283 |
|
for (i = 0; i < max; i += 2) { |
|
284 |
|
if ((data[i] != 0x61) || (data[i + 1] != 0x61)) |
|
285 |
|
continue; |
|
286 |
|
|
|
287 |
|
xlog("\tDEBUG: Found 0x61 0x61 marker at offset %hu\n", i); |
|
288 |
|
|
|
289 |
|
// we found a start marker - let's see how much we can use |
|
290 |
|
j = 0; |
|
291 |
|
while (1) { |
|
292 |
|
unsigned char c; |
|
293 |
|
|
|
294 |
|
c = data[i]; |
|
295 |
|
//xlog("\tDEBUG: i=%hu j=%hu c=0x%02hhx\n", i, j, c); |
|
296 |
|
if ((c >= '0') && (c <= '9')) { |
|
297 |
|
if (i % 2 == 0) { |
|
298 |
|
buf[j] = (c - '0') << 4; |
|
299 |
|
} else { |
|
300 |
|
buf[j] |= (c - '0'); |
|
301 |
|
j++; |
|
302 |
|
} |
|
303 |
|
i++; |
|
304 |
|
continue; |
|
305 |
|
} |
|
306 |
|
|
|
307 |
|
c = toupper(c); |
|
308 |
|
if ((c >= 'A') && (c <= 'F')) { |
|
309 |
|
if (i % 2 == 0) { |
|
310 |
|
buf[j] = (10 + (c - 'A')) << 4; |
|
311 |
|
} else { |
|
312 |
|
buf[j] |= 10 + (c - 'A'); |
|
313 |
|
j++; |
|
314 |
|
} |
|
315 |
|
i++; |
|
316 |
|
continue; |
|
317 |
|
} |
|
318 |
|
|
|
319 |
|
break; |
|
320 |
|
} |
|
321 |
|
|
|
322 |
|
r = test_2fa_bin(buf, j, now, ip); |
|
323 |
|
if (r > 0) |
|
324 |
|
return r; |
|
325 |
|
|
|
326 |
|
if (i % 2 != 0) |
|
327 |
|
i++; |
|
328 |
|
} |
|
329 |
|
|
|
330 |
|
return 0; |
|
331 |
|
} |
|
332 |
|
|
235 |
333 |
/* |
/* |
236 |
334 |
* Check if IP is in the list (1 if found) |
* Check if IP is in the list (1 if found) |
237 |
335 |
* Returns 1 if ok |
* Returns 1 if ok |
|
... |
... |
static void add_ip(const unsigned int pos, const unsigned char *src, |
303 |
401 |
/* |
/* |
304 |
402 |
* Process packet |
* Process packet |
305 |
403 |
*/ |
*/ |
306 |
|
static unsigned int process(const unsigned short eth, |
|
307 |
|
const unsigned char *p, const unsigned int len) |
|
|
404 |
|
static int process(const unsigned short eth, |
|
405 |
|
const unsigned char *p, const unsigned int len, unsigned int *mark) |
308 |
406 |
{ |
{ |
309 |
407 |
struct iphdr *iph; |
struct iphdr *iph; |
310 |
408 |
const unsigned char *data; |
const unsigned char *data; |
|
... |
... |
static unsigned int process(const unsigned short eth, |
315 |
413 |
int r, af; |
int r, af; |
316 |
414 |
char ip[40]; |
char ip[40]; |
317 |
415 |
|
|
|
416 |
|
*mark = 0; |
|
417 |
|
|
318 |
418 |
switch (eth) { |
switch (eth) { |
319 |
419 |
case ETH_P_IP: |
case ETH_P_IP: |
320 |
420 |
iph = (struct iphdr *) p; |
iph = (struct iphdr *) p; |
321 |
|
data = p + iph->ihl * 4 + 8; |
|
|
421 |
|
data = p + iph->ihl * 4; |
322 |
422 |
data_len = len - (data - p); |
data_len = len - (data - p); |
323 |
423 |
memcpy(src, p + 12, 4); src_len = 4; |
memcpy(src, p + 12, 4); src_len = 4; |
324 |
424 |
af = AF_INET; |
af = AF_INET; |
325 |
425 |
break; |
break; |
326 |
426 |
|
|
327 |
427 |
case ETH_P_IPV6: |
case ETH_P_IPV6: |
328 |
|
data = p + 40 + 4; |
|
|
428 |
|
data = p + 40; |
329 |
429 |
data_len = len - (data - p); |
data_len = len - (data - p); |
330 |
430 |
memcpy(src, p + 8, 16); src_len = 16; |
memcpy(src, p + 8, 16); src_len = 16; |
331 |
431 |
af = AF_INET6; |
af = AF_INET6; |
|
... |
... |
static unsigned int process(const unsigned short eth, |
334 |
434 |
default: return NF_DROP; |
default: return NF_DROP; |
335 |
435 |
} |
} |
336 |
436 |
|
|
337 |
|
//xlog("DEBUG: src: "); dump(src, src_len); |
|
338 |
|
//xlog("DEBUG: packet: "); dump(data, data_len); |
|
|
437 |
|
//xlog("\tDEBUG: src: "); dump(src, src_len); |
|
438 |
|
//xlog("\tDEBUG: packet: "); dump(data, data_len); |
339 |
439 |
inet_ntop(af, src, ip, sizeof(ip)); |
inet_ntop(af, src, ip, sizeof(ip)); |
340 |
|
//xlog("DEBUG: ip=%s\n", ip); |
|
|
440 |
|
//xlog("\tDEBUG: ip=%s\n", ip); |
341 |
441 |
|
|
342 |
442 |
gettimeofday(&now, NULL); |
gettimeofday(&now, NULL); |
343 |
443 |
|
|
344 |
444 |
// Checking if the IP is already in the whitelist |
// Checking if the IP is already in the whitelist |
345 |
445 |
r = check_ip(src, src_len, &pos, &now); |
r = check_ip(src, src_len, &pos, &now); |
346 |
446 |
if (r == 1) { |
if (r == 1) { |
347 |
|
//xlog("\t%s: ip is in the white list.\n", ip); |
|
|
447 |
|
// IP is in the whilelist, but we cannot just accept it |
|
448 |
|
// Because we may have a "close firewall" command. |
348 |
449 |
|
|
349 |
450 |
r = test_2fa(data, data_len, now.tv_sec, ip); |
r = test_2fa(data, data_len, now.tv_sec, ip); |
350 |
451 |
if (r == 0xcc) { |
if (r == 0xcc) { |
|
... |
... |
static unsigned int process(const unsigned short eth, |
359 |
460 |
return NF_ACCEPT; |
return NF_ACCEPT; |
360 |
461 |
} |
} |
361 |
462 |
|
|
362 |
|
xlog("\t%s: invalid command (%d)\n", ip, r); |
|
363 |
|
return NF_DROP; |
|
|
463 |
|
xlog("\t%s: in the white list, allow it [mark 0x%08x]\n", |
|
464 |
|
ip, nfmark); |
|
465 |
|
*mark = nfmark; |
|
466 |
|
return NF_REPEAT; |
364 |
467 |
} |
} |
365 |
468 |
|
|
366 |
469 |
r = brute_check(src, src_len, now.tv_sec); |
r = brute_check(src, src_len, now.tv_sec); |
|
... |
... |
static unsigned int process(const unsigned short eth, |
369 |
472 |
return NF_DROP; |
return NF_DROP; |
370 |
473 |
} |
} |
371 |
474 |
|
|
372 |
|
//xlog("\t%s: ip is NOT in the white list.\n", ip); |
|
|
475 |
|
xlog("\t%s: ip is NOT in the white list.\n", ip); |
373 |
476 |
r = test_2fa(data, data_len, now.tv_sec, ip); |
r = test_2fa(data, data_len, now.tv_sec, ip); |
374 |
477 |
if (r == 0x11) { |
if (r == 0x11) { |
375 |
478 |
add_ip(pos, src, src_len, now.tv_sec); |
add_ip(pos, src, src_len, now.tv_sec); |
|
... |
... |
static int queue_cb(const struct nlmsghdr *nlh, void *data) |
387 |
490 |
{ |
{ |
388 |
491 |
struct nfqnl_msg_packet_hdr *ph = NULL; |
struct nfqnl_msg_packet_hdr *ph = NULL; |
389 |
492 |
struct nlattr *attr[NFQA_MAX + 1] = {}; |
struct nlattr *attr[NFQA_MAX + 1] = {}; |
390 |
|
unsigned int id = 0, verdict; |
|
|
493 |
|
unsigned int id = 0, mark; |
391 |
494 |
struct nfgenmsg *nfg; |
struct nfgenmsg *nfg; |
392 |
495 |
uint16_t plen; |
uint16_t plen; |
393 |
496 |
unsigned char *payload; |
unsigned char *payload; |
394 |
|
int ret; |
|
|
497 |
|
int ret, verdict; |
395 |
498 |
|
|
396 |
499 |
if (nfq_nlmsg_parse(nlh, attr) < 0) { |
if (nfq_nlmsg_parse(nlh, attr) < 0) { |
397 |
500 |
xlog("Problems parsing netlink message!\n"); |
xlog("Problems parsing netlink message!\n"); |
|
... |
... |
static int queue_cb(const struct nlmsghdr *nlh, void *data) |
401 |
504 |
nfg = mnl_nlmsg_get_payload(nlh); |
nfg = mnl_nlmsg_get_payload(nlh); |
402 |
505 |
|
|
403 |
506 |
if (attr[NFQA_PACKET_HDR] == NULL) { |
if (attr[NFQA_PACKET_HDR] == NULL) { |
404 |
|
xlog("Packet metaheader not set!\n"); |
|
|
507 |
|
xlog("Packet header not set!\n"); |
405 |
508 |
return MNL_CB_ERROR; |
return MNL_CB_ERROR; |
406 |
509 |
} |
} |
407 |
510 |
|
|
|
... |
... |
static int queue_cb(const struct nlmsghdr *nlh, void *data) |
410 |
513 |
|
|
411 |
514 |
ph = mnl_attr_get_payload(attr[NFQA_PACKET_HDR]); |
ph = mnl_attr_get_payload(attr[NFQA_PACKET_HDR]); |
412 |
515 |
id = ntohl(ph->packet_id); |
id = ntohl(ph->packet_id); |
413 |
|
xlog("packet received (id=%u hw=0x%04hx hook=%hhu, payload len %u" |
|
414 |
|
" data=%p\n", |
|
415 |
|
id, ntohs(ph->hw_protocol), ph->hook, plen, data); |
|
|
516 |
|
xlog("packet received (id=%u hw=0x%04hx hook=%hhu, payload len %u\n", |
|
517 |
|
id, ntohs(ph->hw_protocol), ph->hook, plen); |
416 |
518 |
|
|
417 |
|
verdict = process(ntohs(ph->hw_protocol), payload, plen); |
|
418 |
|
ret = send_verdict(ntohs(nfg->res_id), id, verdict); |
|
|
519 |
|
verdict = process(ntohs(ph->hw_protocol), payload, plen, &mark); |
|
520 |
|
ret = send_verdict(ntohs(nfg->res_id), id, verdict, mark); |
419 |
521 |
if (ret == -1) |
if (ret == -1) |
420 |
522 |
return MNL_CB_ERROR; |
return MNL_CB_ERROR; |
421 |
523 |
|
|
|
... |
... |
int main(int argc, char *argv[]) |
930 |
1032 |
|
|
931 |
1033 |
nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, queue); |
nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, queue); |
932 |
1034 |
nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff); |
nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff); |
|
1035 |
|
/* TODO |
|
1036 |
|
mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(NFQA_CFG_F_CONNTRACK)); |
|
1037 |
|
mnl_attr_put_u32(nlh, NFQA_CFG_MASK, htonl(NFQA_CFG_F_CONNTRACK)); |
|
1038 |
|
*/ |
933 |
1039 |
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { |
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { |
934 |
1040 |
perror("mnl_socket_send copy_packet"); |
perror("mnl_socket_send copy_packet"); |
935 |
1041 |
return 1; |
return 1; |
|
... |
... |
int main(int argc, char *argv[]) |
1015 |
1121 |
strerror(errno)); |
strerror(errno)); |
1016 |
1122 |
goto clean; |
goto clean; |
1017 |
1123 |
} |
} |
1018 |
|
xlog("Connection from " |
|
|
1124 |
|
xlog("Connection from" |
1019 |
1125 |
" pid=%ld, euid=%ld, egid=%ld\n", |
" pid=%ld, euid=%ld, egid=%ld\n", |
1020 |
1126 |
(long) ucred.pid, (long) ucred.uid, |
(long) ucred.pid, (long) ucred.uid, |
1021 |
1127 |
(long) ucred.gid); |
(long) ucred.gid); |
File totp.c changed (mode: 100644) (index a3f5324..bd7c056) |
... |
... |
int totp_text(const char *secret, const char *name) |
261 |
261 |
unsigned char i, j, v1, v2; |
unsigned char i, j, v1, v2; |
262 |
262 |
unsigned short x, y, limit; |
unsigned short x, y, limit; |
263 |
263 |
const char *sym[4] = { "█", "▄", "▀", " " }; |
const char *sym[4] = { "█", "▄", "▀", " " }; |
|
264 |
|
const unsigned char sym_len[4] = { 3, 3, 3, 1 }; |
264 |
265 |
char ename[128], ehost[400]; |
char ename[128], ehost[400]; |
265 |
266 |
|
|
266 |
267 |
gethostname(host, sizeof(host) - 1); |
gethostname(host, sizeof(host) - 1); |
|
... |
... |
int totp_text(const char *secret, const char *name) |
271 |
272 |
snprintf(extra, sizeof(extra), "otpauth://totp/%s?secret=%s&issuer=%s", |
snprintf(extra, sizeof(extra), "otpauth://totp/%s?secret=%s&issuer=%s", |
272 |
273 |
ename, secret, ehost); |
ename, secret, ehost); |
273 |
274 |
|
|
274 |
|
qr = QRcode_encodeString(extra, 0, QR_ECLEVEL_M, QR_MODE_8, 1); |
|
|
275 |
|
qr = QRcode_encodeString(extra, 0, QR_ECLEVEL_L, QR_MODE_8, 0 /*case*/); |
275 |
276 |
if (!qr) { |
if (!qr) { |
276 |
277 |
snprintf(error, sizeof(error), |
snprintf(error, sizeof(error), |
277 |
278 |
"cannot generate qr code: %s", strerror(errno)); |
"cannot generate qr code: %s", strerror(errno)); |
|
... |
... |
int totp_text(const char *secret, const char *name) |
280 |
281 |
|
|
281 |
282 |
// top margin |
// top margin |
282 |
283 |
for (j = 0; j < qr->width + 4; j++) |
for (j = 0; j < qr->width + 4; j++) |
283 |
|
printf(sym[0]); |
|
284 |
|
printf("\n"); |
|
|
284 |
|
write(STDOUT_FILENO, sym[0], sym_len[0]); |
|
285 |
|
write(STDOUT_FILENO, "\n", 1); |
285 |
286 |
|
|
286 |
287 |
limit = qr->width * qr->width; |
limit = qr->width * qr->width; |
287 |
288 |
for (i = 0; i < qr->width; i += 2) { |
for (i = 0; i < qr->width; i += 2) { |
288 |
|
printf("%s%s", sym[0], sym[0]); |
|
|
289 |
|
write(STDOUT_FILENO, sym[0], sym_len[0]); |
|
290 |
|
write(STDOUT_FILENO, sym[0], sym_len[0]); |
|
291 |
|
|
289 |
292 |
for (j = 0; j < qr->width; j++) { |
for (j = 0; j < qr->width; j++) { |
290 |
293 |
x = i * qr->width + j; |
x = i * qr->width + j; |
291 |
294 |
y = (i + 1) * qr->width + j; |
y = (i + 1) * qr->width + j; |
|
... |
... |
int totp_text(const char *secret, const char *name) |
294 |
297 |
v2 = qr->data[y] & 1; |
v2 = qr->data[y] & 1; |
295 |
298 |
else |
else |
296 |
299 |
v2 = 0; |
v2 = 0; |
297 |
|
printf(sym[(v2 << 1) | v1]); |
|
|
300 |
|
write(STDOUT_FILENO, sym[(v2 << 1) | v1], sym_len[(v2 << 1) | v1]); |
298 |
301 |
} |
} |
299 |
|
printf("%s%s\n", sym[0], sym[0]); |
|
|
302 |
|
|
|
303 |
|
write(STDOUT_FILENO, sym[0], sym_len[0]); |
|
304 |
|
write(STDOUT_FILENO, sym[0], sym_len[0]); |
|
305 |
|
write(STDOUT_FILENO, "\n", 1); |
300 |
306 |
} |
} |
301 |
307 |
|
|
302 |
308 |
// bottom margin |
// bottom margin |
303 |
309 |
for (j = 0; j < qr->width + 4; j++) |
for (j = 0; j < qr->width + 4; j++) |
304 |
|
printf(sym[0]); |
|
305 |
|
printf("\n"); |
|
|
310 |
|
write(STDOUT_FILENO, sym[0], sym_len[0]); |
|
311 |
|
write(STDOUT_FILENO, "\n", 1); |
306 |
312 |
|
|
307 |
313 |
QRcode_free(qr); |
QRcode_free(qr); |
308 |
314 |
|
|