List of commits:
Subject Hash Author Date (UTC)
Fixed a bug in totp, added keys in memory abec61861e2f37398026dbe7342d7751390e95d8 Catalin(ux) M. BOIE 2018-02-04 18:36:12
Initial version c641fafbd46342cd24fde45129cc3637b7ca65bc Catalin(ux) M. BOIE 2018-02-03 23:42:32
Commit abec61861e2f37398026dbe7342d7751390e95d8 - Fixed a bug in totp, added keys in memory
Author: Catalin(ux) M. BOIE
Author date (UTC): 2018-02-04 18:36
Committer name: Catalin(ux) M. BOIE
Committer date (UTC): 2018-02-04 18:36
Parent(s): c641fafbd46342cd24fde45129cc3637b7ca65bc
Signing key:
Tree: eae74c9060ebf90bd700f0893a56737cf70b0d5e
File Lines added Lines deleted
.gitignore 4 0
Makefile 11 6
README 6 9
TODO 6 2
key.c 10 36
key.h 2 3
nf2fa_common.h 2 0
nf2fac.c 29 19
nf2fad.c 345 68
totp.c 15 8
totp.h 2 2
File .gitignore added (mode: 100644) (index 0000000..ef4bba7)
1 nf2fac
2 nf2fad
3 *.o
4
File Makefile changed (mode: 100644) (index bad84d6..6052c9b)
1 TARGETS := nf2fad nf2fac
1 TARGETS := nf2fad nf2fac #nf2fa_check
2 2
3 3 all: $(TARGETS) all: $(TARGETS)
4 4
5 CFLAGS := -Wall -Wextra -g -O2 $(CFLAGS)
6
5 7 OBJS := totp.o key.o protocol.o util.o OBJS := totp.o key.o protocol.o util.o
6 8 LIBS := -lmnl -lnetfilter_queue -lssl -lcrypto -lqrencode LIBS := -lmnl -lnetfilter_queue -lssl -lcrypto -lqrencode
7 9
8 10 totp.o: totp.c totp.h totp.o: totp.c totp.h
9 gcc -O2 -Wall -Wextra totp.c -c
11 gcc $(CFLAGS) totp.c -c
10 12
11 13 key.o: key.c key.h key.o: key.c key.h
12 gcc -O2 -Wall -Wextra key.c -c
14 gcc $(CFLAGS) key.c -c
13 15
14 16 protocol.o: protocol.c protocol.h protocol.o: protocol.c protocol.h
15 gcc -O2 -Wall -Wextra protocol.c -c
17 gcc $(CFLAGS) protocol.c -c
16 18
17 19 nf2fad: nf2fad.c $(OBJS) totp.h key.h protocol.h util.h nf2fad: nf2fad.c $(OBJS) totp.h key.h protocol.h util.h
18 gcc -O2 -Wall -Wextra nf2fad.c -o nf2fad $(OBJS) $(LIBS)
20 gcc $(CFLAGS) nf2fad.c -o nf2fad $(OBJS) $(LIBS)
19 21
20 22 nf2fac: nf2fac.c $(OBJS) totp.h key.h protocol.h util.h nf2fac: nf2fac.c $(OBJS) totp.h key.h protocol.h util.h
21 gcc -O2 -Wall -Wextra nf2fac.c -o nf2fac $(OBJS) $(LIBS)
23 gcc $(CFLAGS) nf2fac.c -o nf2fac $(OBJS) $(LIBS)
24
25 nf2fa_check: nf2fa_check.c $(OBJS) totp.h
26 gcc $(CFLAGS) nf2fa_check.c -o nf2fa_check $(OBJS) $(LIBS)
22 27
23 28 .PHONY: clean .PHONY: clean
24 29 clean: clean:
File README changed (mode: 100644) (index 4e9cf59..75b8358)
1 1 Build dependency: libnetfilter_queue-devel libmnl-devel qrencode-devel Build dependency: libnetfilter_queue-devel libmnl-devel qrencode-devel
2 2
3 We need to have a daemon, so we can listen to clients for enrolling.
4 So, we need a tool for enrollment, connecting to a unix socket, to be able
5 to identify the user.
6 We will store, as separate files, the enrollments.
7 3 We also will store them in RAM, to be able to check them. We also will store them in RAM, to be able to check them.
8 4
9 5 At boot, load all files and store them in ram; limit per user. At boot, load all files and store them in ram; limit per user.
 
... ... At boot, load all files and store them in ram; limit per user.
11 7 Of course, also root must be able to enroll. Of course, also root must be able to enroll.
12 8
13 9 Why we need this? Why we need this?
14 Because classic port knocking can be replicated from anywhere, if the attacker
15 can sniff the packets.
10 - Because classic port knocking can be replicated from anywhere, if the attacker
11 can "see" the packets.
12 - With a digital signature program, you need some part on the client side.
13 -
16 14
17 Directory layout for storing 2fa keys
18
19 KEY_DIR
15 Directory layout for storing 2fa keys:
16 KEY_DIR (/var/lib/nf2fa)
20 17 uid uid
21 18 XXX XXX
File TODO changed (mode: 100644) (index 93918d9..7b1ba9f)
3 3 But, we can have a DOS here! So, maybe not do it. But, we can have a DOS here! So, maybe not do it.
4 4 Warn user that nf2fa is not for allowing non authenticated connections. Warn user that nf2fa is not for allowing non authenticated connections.
5 5 [ ] Create /var/lib/nf2fa; pay attention to rights [ ] Create /var/lib/nf2fa; pay attention to rights
6 [ ] Use protocol_enqueue_string!
6 Refuse to load keys if the rights are not ok.
7 7 [ ] Clean keys from memory! [ ] Clean keys from memory!
8 [ ] Add the date of the enrollment
8 [ ] Show the date of the enrollment
9 9 [ ] Order the listing? [ ] Order the listing?
10 [ ] How to store keys in memory? Should I?
11 [ ] Drop privileges.
12 [ ] Allow user to send a code to close the firewall.
13 [ ] When searching for the next free slot, search in memory, not on disk!
10 14 [ ] [ ]
File key.c changed (mode: 100644) (index 6891c57..07ac839)
17 17 */ */
18 18 void key_print(const struct key *k) void key_print(const struct key *k)
19 19 { {
20 printf("Key %03hhu name=[%s] itime=%u key=[%s] ts=%u ip=%s\n",
20 printf("Key %03hhu name=[%s] itime=%ld key=[%s] ts=%ld ip=[%s]\n",
21 21 k->id, k->name, k->itime, k->key, k->ts, k->ip); k->id, k->name, k->itime, k->key, k->ts, k->ip);
22 22 } }
23 23
 
... ... int key_save(const unsigned int uid, const struct key *k)
51 51
52 52 gettimeofday(&now, NULL); gettimeofday(&now, NULL);
53 53
54 snprintf(buf, sizeof(buf), "%s\n%s\n%u\n%s\n%lu\n",
55 k->name, k->key, k->ts, k->ip, now.tv_sec);
54 snprintf(buf, sizeof(buf), "%s\n%s\n%ld\n%s\n%ld\n",
55 k->name, k->key, k->ts, k->ip, k->itime);
56 56
57 57 len = strlen(buf); len = strlen(buf);
58 58 n = write(fd, buf, len); n = write(fd, buf, len);
 
... ... int key_load(struct key *k, const unsigned int uid, const unsigned char key_id)
133 133 break; break;
134 134 } }
135 135
136 memset(k, 0, sizeof(struct key));
137
136 138 pos = 0; pos = 0;
137 139 i = 0; i = 0;
138 140 while ((pos < n) && (buf[pos] != '\n')) while ((pos < n) && (buf[pos] != '\n'))
139 141 k->name[i++] = buf[pos++]; k->name[i++] = buf[pos++];
140 k->name[i] = '\0';
142 k->name[i] = '\0'; pos++;
141 143
142 144 i = 0; i = 0;
143 145 while ((pos < n) && (buf[pos] != '\n')) while ((pos < n) && (buf[pos] != '\n'))
144 146 k->key[i++] = buf[pos++]; k->key[i++] = buf[pos++];
145 k->key[i] = '\0';
147 k->key[i] = '\0'; pos++;
146 148
147 149 i = 0; i = 0;
148 150 while ((pos < n) && (buf[pos] != '\n')) while ((pos < n) && (buf[pos] != '\n'))
149 151 sts[i++] = buf[pos++]; sts[i++] = buf[pos++];
150 152 sts[i] = '\0'; sts[i] = '\0';
151 k->ts = strtoul(sts, NULL, 10);
153 k->ts = strtol(sts, NULL, 10); pos++;
152 154
153 155 i = 0; i = 0;
154 156 while ((pos < n) && (buf[pos] != '\n')) while ((pos < n) && (buf[pos] != '\n'))
155 157 k->ip[i++] = buf[pos++]; k->ip[i++] = buf[pos++];
156 k->ip[i] = '\0';
158 k->ip[i] = '\0'; pos++;
157 159
158 160 i = 0; i = 0;
159 161 while ((pos < n) && (buf[pos] != '\n')) while ((pos < n) && (buf[pos] != '\n'))
160 162 sts[i++] = buf[pos++]; sts[i++] = buf[pos++];
161 163 sts[i] = '\0'; sts[i] = '\0';
162 k->itime = strtoul(sts, NULL, 10);
164 k->itime = strtol(sts, NULL, 10); pos++;
163 165
164 166 k->id = key_id; k->id = key_id;
165 167 ret = 0; ret = 0;
 
... ... int key_load(struct key *k, const unsigned int uid, const unsigned char key_id)
171 173 return ret; return ret;
172 174 } }
173 175
174 /*
175 * Returns first free key slot be scanning the folder
176 * Returns 0xFF if no slot is free
177 */
178 unsigned char key_next_free(const unsigned int uid)
179 {
180 char path[256];
181 ssize_t n;
182 unsigned char i;
183 struct stat s;
184
185 snprintf(path, sizeof(path), "%s/%u", KEY_DIR, uid);
186 n = stat(path, &s);
187 if (n != 0)
188 return 0;
189
190 for (i = 0; i < 0xFF; i++) {
191 snprintf(path, sizeof(path), "%s/%u/%03hhu", KEY_DIR, uid, i);
192
193 n = stat(path, &s);
194 if (n != 0)
195 return i;
196 }
197
198 return 0xFF;
199 }
200
201 176 /* /*
202 177 * Removes a key * Removes a key
203 178 */ */
204 179 int key_remove(const unsigned int uid, const unsigned char id) int key_remove(const unsigned int uid, const unsigned char id)
205 180 { {
206 181 char path[256]; char path[256];
207 int ret;
208 182
209 183 snprintf(path, sizeof(path), "%s/%u/%03hhu", KEY_DIR, uid, id); snprintf(path, sizeof(path), "%s/%u/%03hhu", KEY_DIR, uid, id);
210 184 return unlink(path); return unlink(path);
File key.h changed (mode: 100644) (index 2770a7e..a5dac1e)
2 2
3 3 struct key struct key
4 4 { {
5 unsigned int itime;
6 unsigned int ts;
5 time_t itime;
6 time_t ts;
7 7 unsigned char id; unsigned char id;
8 8 char name[32]; char name[32];
9 9 char key[32]; char key[32];
 
... ... void key_print(const struct key *k);
17 17 int key_save(const unsigned int uid, const struct key *k); int key_save(const unsigned int uid, const struct key *k);
18 18 int key_load(struct key *k, const unsigned int uid, int key_load(struct key *k, const unsigned int uid,
19 19 const unsigned char key_id); const unsigned char key_id);
20 unsigned char key_next_free(const unsigned int uid);
21 20
22 21 int key_remove(const unsigned int uid, const unsigned char id); int key_remove(const unsigned int uid, const unsigned char id);
File nf2fa_common.h changed (mode: 100644) (index e25f266..0ff150b)
1 1 #define NF2FA_CMD_ENROLL 0 #define NF2FA_CMD_ENROLL 0
2 2 #define NF2FA_CMD_LIST 1 #define NF2FA_CMD_LIST 1
3 3 #define NF2FA_CMD_UNENROLL 2 #define NF2FA_CMD_UNENROLL 2
4
5 #define SOCKET_NAME "/run/nf2fa.sock"
File nf2fac.c changed (mode: 100644) (index 1118476..cc8fdaa)
5 5
6 6 #define _GNU_SOURCE #define _GNU_SOURCE
7 7
8 #define SOCKET_NAME "/run/nf2fa.sock"
9
10 8 #include <errno.h> #include <errno.h>
11 9 #include <stdio.h> #include <stdio.h>
12 10 #include <stdlib.h> #include <stdlib.h>
 
... ... int main(int argc, char *argv[])
109 107 dump(buf, n); dump(buf, n);
110 108
111 109 i = 0; i = 0;
112 cmd = buf[i++];
113 err = buf[i++];
110 cmd = buf[i++]; n--;
111 err = buf[i++]; n--;
114 112
115 113 if (err != 0x00) { if (err != 0x00) {
116 114 char error[64]; char error[64];
117 115
118 len = buf[i++];
116 len = buf[i++]; n--;
119 117 memcpy(error, buf + i, len); memcpy(error, buf + i, len);
120 118 error[len] = '\0'; error[len] = '\0';
121 119 printf("ERROR: %s!\n", error); printf("ERROR: %s!\n", error);
 
... ... int main(int argc, char *argv[])
132 130 k.key[len] = '\0'; k.key[len] = '\0';
133 131 printf("Key is %s (id %03hhu). Scan the QR code or type the key.\n", printf("Key is %s (id %03hhu). Scan the QR code or type the key.\n",
134 132 k.key, id); k.key, id);
135 snprintf(info, sizeof(info), "%s (id %03hhu)", k.name, id);
133 snprintf(info, sizeof(info), "%s - id %03hhu", k.name, id);
136 134 totp_text(k.key, info); totp_text(k.key, info);
137 135 return 0; return 0;
138 136 } }
139 137
140 138 if (cmd == NF2FA_CMD_LIST) { if (cmd == NF2FA_CMD_LIST) {
141 unsigned char entries, j;
142
143 entries = buf[i++];
144 if (entries == 0) {
139 if (n == 0) {
145 140 printf("No enrollments.\n"); printf("No enrollments.\n");
146 141 return 0; return 0;
147 142 } }
148 143
149 printf("No Last used Last IP Name\n");
150 for (j = 0; j < entries; j++) {
144 printf("No Last used Last IP"
145 " Name\n");
146 while (n > 0) {
151 147 struct key k; struct key k;
148 unsigned int x;
149 char t[32];
150 struct tm tm;
152 151
153 k.id = buf[i++];
154 len = buf[i++];
155 memcpy(k.name, buf + i, len); i += len;
152 k.id = buf[i++]; n--;
153
154 len = buf[i++]; n--;
155 memcpy(k.name, buf + i, len); i += len; n -= len;
156 156 k.name[len] = '\0'; k.name[len] = '\0';
157 k.ts = ntohs((unsigned int *) (buf + i)); i += 4;
158 memcpy(k.ip, buf + i, len); i += len;
157
158 memcpy(&x, buf + i, 4); i += 4; n -= 4;
159 k.ts = ntohl(x);
160
161 len = buf[i++]; n--;
162 memcpy(k.ip, buf + i, len); i += len; n -= len;
159 163 k.ip[len] = '\0'; k.ip[len] = '\0';
160 164
161 printf("%3hhu\t%u\t%39s\t%s\n",
162 k.id, k.ts, k.ip, k.name);
165 if (k.ts == 0) {
166 sprintf(t, "%-14s", "not used");
167 } else {
168 localtime_r(&k.ts, &tm);
169 strftime(t, sizeof(t), "%D %H:%M", &tm);
170 }
171 printf("%03hhu %s %-39s %s\n",
172 k.id, t, k.ip, k.name);
163 173 } }
164 174 return 0; return 0;
165 175 } }
File nf2fad.c changed (mode: 100644) (index d7a0921..c7b5814)
5 5 */ */
6 6
7 7 // TODO: Cache codes already computed! // TODO: Cache codes already computed!
8 // TODO: Test x codes below/beyond the correct timestamp
9 8 // TODO: What ports to open? Based on user/group? // TODO: What ports to open? Based on user/group?
10 9 // TODO: If the IP is validated, do not test anymore anything. // TODO: If the IP is validated, do not test anymore anything.
11 10 // TODO: We need to specify the time after we request to re-authenticate. // TODO: We need to specify the time after we request to re-authenticate.
11 // TODO: Log to syslog all operations.
12 12
13 13 #define _GNU_SOURCE #define _GNU_SOURCE
14 14
15 15 #define KEY_DIGITS 16 #define KEY_DIGITS 16
16 #define SOCKET_NAME "/run/nf2fa.sock"
17 16
18 17 #include <errno.h> #include <errno.h>
19 18 #include <stdio.h> #include <stdio.h>
20 19 #include <stdlib.h> #include <stdlib.h>
21 20 #include <unistd.h> #include <unistd.h>
22 21 #include <string.h> #include <string.h>
22 #include <dirent.h>
23 23 #include <time.h> #include <time.h>
24 24 #include <sys/time.h> #include <sys/time.h>
25 25 #include <sys/types.h> #include <sys/types.h>
 
... ... struct my_ip
58 58 static struct my_ip my_ips[MAX_IPS]; static struct my_ip my_ips[MAX_IPS];
59 59
60 60 /* Here we keep track of 2fa codes */ /* Here we keep track of 2fa codes */
61 struct my_list
61 struct user
62 62 { {
63 char key[30];
64 unsigned int ts_last_used;
65 struct my_list *next;
63 unsigned int uid;
64 struct key *head, *tail;
65 struct user *next;
66 66 }; };
67 static struct my_list *head, *tail;
67 static struct user *users_head, *users_tail;
68 68
69 69 static struct mnl_socket *nl; static struct mnl_socket *nl;
70 70
 
... ... static void send_verdict(const int queue_num, const uint32_t id,
98 98 } }
99 99
100 100 /* /*
101 * Test if a 2fa code is ok
101 * Try to find the code inside the packet
102 102 * Returns 1 if ok * Returns 1 if ok
103 103 */ */
104 104 static int test_2fa(const unsigned char *data, const unsigned int data_len, static int test_2fa(const unsigned char *data, const unsigned int data_len,
105 unsigned int now30, struct my_list *q)
105 const unsigned int now, const char *ip)
106 106 { {
107 unsigned int i, j;
108 unsigned char code[3], c, computed = 0, found = 0;
107 unsigned short i, j;
108 unsigned int now30;
109 unsigned char c;
110 struct user *q;
111 struct key *k;
112 char pin[7];
113 int r;
114
115 now30 = now / 30;
109 116
110 // Try to find the code inside the packet
111 117 for (i = 0; i < data_len - (3 - 1); i++) { for (i = 0; i < data_len - (3 - 1); i++) {
112 118 for (j = 0; j < 3; j++) { for (j = 0; j < 3; j++) {
113 119 c = data[i + j]; c = data[i + j];
 
... ... static int test_2fa(const unsigned char *data, const unsigned int data_len,
117 123 if (j != 3) // not found if (j != 3) // not found
118 124 continue; continue;
119 125
120 // Found, compute code if not present TODO
121 if (!computed) {
122 code[0] = 0x09;
123 code[1] = 0x12;
124 code[2] = 0x34;
125 computed = 1;
126 }
126 snprintf(pin, sizeof(pin), "%02hhx%02hhx%02hhx",
127 data[i], data[i + 1], data[i + 2]);
128 printf("DEBUG: Packet pin: %s\n", pin);
127 129
128 // Test
129 if (memcmp(data + i, code, 3) != 0)
130 continue;
130 q = users_head;
131 while (q) {
132 k = q->head;
133 while (k) {
134 r = totp_verify(k->key, now30, k->ts / 30, pin);
135 if (r == 1) {
136 printf(" DEBUG: uid %u key %03hhu matched!\n", q->uid, k->id);
137 k->ts = now;
138 snprintf(k->ip, sizeof(k->ip), "%s", ip);
139 key_save(q->uid, k);
140 return 1;
141 }
142
143 k = k->next;
144 }
131 145
132 printf("\tFound code at offset %u\n", i);
133 found = 1;
134 break;
146 q = q->next;
147 }
135 148 } }
136 if (!found)
137 return 0;
138 149
139 return 1;
150 return 0;
140 151 } }
141 152
142 153 /* /*
 
... ... static unsigned int process(const unsigned short eth,
216 227 const unsigned char *data; const unsigned char *data;
217 228 unsigned char src[16]; unsigned char src[16];
218 229 unsigned int data_len, src_len, verdict = NF_DROP; unsigned int data_len, src_len, verdict = NF_DROP;
219 struct my_list *q;
220 230 struct timeval now; struct timeval now;
221 231 unsigned int pos = 0xFFFFFFFF; unsigned int pos = 0xFFFFFFFF;
222 int r;
232 int r, af;
233 char ip[40];
223 234
224 235 switch (eth) { switch (eth) {
225 236 case ETH_P_IP: case ETH_P_IP:
 
... ... static unsigned int process(const unsigned short eth,
227 238 data = p + iph->ihl * 4; data = p + iph->ihl * 4;
228 239 data_len = len - iph->ihl * 4; data_len = len - iph->ihl * 4;
229 240 memcpy(src, p + 12, 4); src_len = 4; memcpy(src, p + 12, 4); src_len = 4;
241 af = AF_INET;
230 242 break; break;
231 243
232 244 case ETH_P_IPV6: case ETH_P_IPV6:
233 245 data = p + 40; data = p + 40;
234 246 data_len = len - 40; data_len = len - 40;
235 247 memcpy(src, p + 8, 16); src_len = 16; memcpy(src, p + 8, 16); src_len = 16;
248 af = AF_INET6;
236 249 break; break;
237 250
238 251 default: return verdict; default: return verdict;
 
... ... static unsigned int process(const unsigned short eth,
247 260 r = check_ip(src, src_len, &pos, &now); r = check_ip(src, src_len, &pos, &now);
248 261 if (r == 1) { if (r == 1) {
249 262 printf("\tGive access because already in list!\n"); printf("\tGive access because already in list!\n");
250 my_ips[pos].expire = now.tv_sec + 3600;
263 my_ips[pos].expire = now.tv_sec + 3600; // TODO: configurable if we want to do it
251 264 return NF_ACCEPT; return NF_ACCEPT;
252 265 } }
253 266
254 while (1) {
255 unsigned int now30;
256
257 now30 = now.tv_sec / 30;
258 printf("now30=%u\n", now30);
259
260 // TODO: fake data
261 struct my_list fake;
262 fake.next = NULL;
263 head = &fake;
264
265 q = head;
266 while (q) {
267 r = test_2fa(data, data_len, now30, q);
268 if (r == 1) {
269 add_ip(pos, src, src_len, &now);
270 verdict = NF_ACCEPT;
271 break;
272 }
273 q = q->next;
274 }
275
276 break;
267 inet_ntop(af, src, ip, sizeof(ip));
268 printf("DBEUG: ip=%s\n", ip);
269 r = test_2fa(data, data_len, now.tv_sec, ip);
270 if (r == 1) {
271 add_ip(pos, src, src_len, &now);
272 verdict = NF_ACCEPT;
277 273 } }
278 274
279 printf(" verdict=%s\n", verdict == NF_ACCEPT ? "ACCEPT" : "DROP");
275 printf("\tverdict=%s\n", verdict == NF_ACCEPT ? "ACCEPT" : "DROP");
280 276 return verdict; return verdict;
281 277 } }
282 278
 
... ... static int queue_cb(const struct nlmsghdr *nlh, void *data)
319 315 return MNL_CB_OK; return MNL_CB_OK;
320 316 } }
321 317
318 /*
319 * Add a key to a struct user, ordering them correctly
320 */
321 void key_add(struct user *u, struct key *k)
322 {
323 struct key *q, *prev;
324
325 prev = NULL;
326 q = u->head;
327 while (q) {
328 printf("DEBUG: comparing q->id (%hhu) with k->id (%hhu)\n",
329 q->id, k->id);
330 if (q->id > k->id)
331 break;
332
333 prev = q;
334 q = q->next;
335 }
336
337 if (prev == NULL) {
338 k->next = u->head;
339 u->head = k;
340 } else {
341 k->next = prev->next;
342 prev->next = k;
343 }
344 }
345
346 /*
347 * Returns a struct user by uid
348 */
349 struct user *uid_to_user(const unsigned int uid)
350 {
351 struct user *q;
352
353 q = users_head;
354 while (q) {
355 if (q->uid == uid)
356 return q;
357
358 q = q->next;
359 }
360
361 return NULL;
362 }
363
364 /*
365 * Returns the next free key slot.
366 * Returns 0xFF if not available
367 */
368 unsigned char key_next_free(struct user *u)
369 {
370 struct key *k;
371 unsigned char min = 0;
372
373 k = u->head;
374 while (k) {
375 if (k->id > min)
376 return min;
377
378 k = k->next;
379 min++;
380 }
381
382 return min;
383 }
384
322 385 /* /*
323 386 * Process a client command * Process a client command
324 387 * Returns the number of bytes stored in @buf * Returns the number of bytes stored in @buf
 
... ... int process_client(const unsigned int uid, unsigned char *buf,
334 397 cmd = buf[i++]; cmd = buf[i++];
335 398
336 399 if (cmd == NF2FA_CMD_ENROLL) { if (cmd == NF2FA_CMD_ENROLL) {
337 struct key k;
400 struct user *u;
401 struct key k, *k2;
338 402 unsigned char len8; unsigned char len8;
403 struct timeval now;
339 404
340 405 memset(&k, 0, sizeof(struct key)); memset(&k, 0, sizeof(struct key));
341 406
 
... ... int process_client(const unsigned int uid, unsigned char *buf,
348 413
349 414 i = 1; /* we now overwrite the buffer */ i = 1; /* we now overwrite the buffer */
350 415
351 k.id = key_next_free(uid);
416 u = uid_to_user(uid); // TODO: use this anywhere
417 if (!u)
418 k.id = 0;
419 else
420 k.id = key_next_free(u);
352 421 if (k.id == 0xFF) if (k.id == 0xFF)
353 422 return i + protocol_enqueue_error(buf + i, return i + protocol_enqueue_error(buf + i,
354 423 "no free slots; please try to free some"); "no free slots; please try to free some");
 
... ... int process_client(const unsigned int uid, unsigned char *buf,
358 427 return i + protocol_enqueue_error(buf + i, return i + protocol_enqueue_error(buf + i,
359 428 "cannot generate key: too low entropy"); "cannot generate key: too low entropy");
360 429
430 gettimeofday(&now, NULL);
431 k.itime = now.tv_sec;
432
361 433 r = key_save(uid, &k); r = key_save(uid, &k);
362 434 if (r != 0) if (r != 0)
363 435 return i + protocol_enqueue_error(buf + i, return i + protocol_enqueue_error(buf + i,
364 436 "cannot save key"); "cannot save key");
365 437
438 // Add the key to memory
439 u = users_head;
440 while (u) {
441 if (u->uid == uid)
442 break;
443 u = u->next;
444 }
445 if (!u) {
446 u = malloc(sizeof(struct user));
447 if (!u)
448 return i + protocol_enqueue_error(buf + i,
449 "cannot alloc memory");
450 memset(u, 0, sizeof(struct user));
451 u->uid = uid;
452 users_head = u;
453 } else {
454 users_tail->next = u;
455 }
456 users_tail = u;
457
458 k2 = malloc(sizeof(struct key));
459 if (!k2)
460 return i + protocol_enqueue_error(buf + i,
461 "cannot alloc memory");
462 memcpy(k2, &k, sizeof(struct key));
463
464 key_add(u, k2);
465
366 466 buf[i++] = 0x00; /* marks the success */ buf[i++] = 0x00; /* marks the success */
367 467 buf[i++] = k.id; buf[i++] = k.id;
368 468 i += protocol_enqueue_string(buf + i, k.key); i += protocol_enqueue_string(buf + i, k.key);
 
... ... int process_client(const unsigned int uid, unsigned char *buf,
370 470 } }
371 471
372 472 if (cmd == NF2FA_CMD_LIST) { if (cmd == NF2FA_CMD_LIST) {
473 struct user *u;
474 struct key *k;
475
476 i = 1; /* we now overwrite the buffer */
477
478 buf[i++] = 0x00; /* OK */
479
480 u = users_head;
481 while (u) {
482 if (u->uid == uid)
483 break;
484 }
485 if (!u)
486 return i;
487
488 k = u->head;
489 while (k) {
490 unsigned int x;
491
492 buf[i++] = k->id;
493 i += protocol_enqueue_string(buf + i, k->name);
494 x = htonl(k->ts);
495 memcpy(buf + i, &x, 4); i += 4;
496 i += protocol_enqueue_string(buf + i, k->ip);
497
498 k = k->next;
499 }
500
501 return i;
373 502 } }
374 503
375 504 if (cmd == NF2FA_CMD_UNENROLL) { if (cmd == NF2FA_CMD_UNENROLL) {
505 struct user *u;
506 struct key *k, *kprev;
376 507 int r; int r;
377 508 unsigned char id; unsigned char id;
378 509
 
... ... int process_client(const unsigned int uid, unsigned char *buf,
385 516 return i + protocol_enqueue_error(buf + i, return i + protocol_enqueue_error(buf + i,
386 517 "cannot remove key"); "cannot remove key");
387 518
519 // Remove it from memory
520 u = users_head;
521 while (u) {
522 if (u->uid == uid)
523 break;
524 }
525 if (u) {
526 kprev = NULL;
527 k = u->head;
528 while (k) {
529 if (k->id != id) {
530 kprev = k;
531 k = k->next;
532 continue;
533 }
534
535 if (kprev)
536 kprev->next = k->next;
537 else
538 u->head = k->next;
539 free(k);
540 break;
541 }
542 }
543
388 544 buf[i++] = 0x00; /* marks the success */ buf[i++] = 0x00; /* marks the success */
389 545 return i; return i;
390 546 } }
 
... ... int process_client(const unsigned int uid, unsigned char *buf,
392 548 return i + protocol_enqueue_error(buf + i, "unknown command"); return i + protocol_enqueue_error(buf + i, "unknown command");
393 549 } }
394 550
551 /*
552 * Load the keys for a user
553 */
554 int load_keys_uid(struct user *u, const unsigned int uid)
555 {
556 int err;
557
558 err = -1;
559 while (1) {
560 DIR *d;
561 char path[255];
562
563 snprintf(path, sizeof(path), "%s/%u", KEY_DIR, uid);
564 d = opendir(path);
565 if (!d) {
566 printf("Cannot open dir %s: %s!\n",
567 path, strerror(errno));
568 break;
569 }
570
571 errno = 0;
572 while (1) {
573 unsigned char key_id;
574 struct key *k;
575 struct dirent *de;
576 int r;
577
578 de = readdir(d);
579 if (!de) {
580 if (errno == 0)
581 err = 0;
582 break;
583 }
584
585 if (strlen(de->d_name) != 3)
586 continue;
587
588 //printf(" Loading key %s...\n", de->d_name);
589 k = malloc(sizeof(struct key));
590 if (!k) {
591 printf("Cannot alloc memory!\n");
592 break;
593 }
594
595 key_id = strtoul(de->d_name, NULL, 10);
596
597 r = key_load(k, uid, key_id);
598 if (r != 0)
599 break;
600
601 key_add(u, k);
602 }
603
604 closedir(d);
605 break;
606 }
607
608 return err;
609 }
610
611 /*
612 * Load all keys from KEY_DIR
613 */
614 int load_keys(void)
615 {
616 DIR *d;
617 int err, r;
618
619 d = opendir(KEY_DIR);
620 if (!d) {
621 printf("Cannot open keys dir: %s!\n", strerror(errno));
622 return -1;
623 }
624
625 err = -1;
626 errno = 0;
627 while (1) {
628 struct dirent *de;
629 struct user *u;
630 unsigned int uid;
631
632 de = readdir(d);
633 if (!de) {
634 if (errno == 0)
635 err = 0;
636 break;
637 }
638
639 if (de->d_name[0] == '.')
640 continue;
641
642 //printf("Loading uid [%s]...\n", de->d_name);
643
644 u = malloc(sizeof(struct user));
645 if (!u) {
646 printf("Cannot alloc memory!\n");
647 break;
648 }
649 memset(u, 0, sizeof(struct user));
650
651 if (users_head == NULL)
652 users_head = u;
653 else
654 users_tail->next = u;
655 users_tail = u;
656
657 uid = strtoul(de->d_name, NULL, 10);
658 r = load_keys_uid(u, uid);
659 if (r != 0)
660 break;
661 }
662 closedir(d);
663
664 return err;
665 }
666
395 667 int main(int argc, char *argv[]) int main(int argc, char *argv[])
396 668 { {
397 669 unsigned char buf[MNL_SOCKET_BUFFER_SIZE]; unsigned char buf[MNL_SOCKET_BUFFER_SIZE];
 
... ... int main(int argc, char *argv[])
403 675 struct sockaddr_un sa_un; struct sockaddr_un sa_un;
404 676 socklen_t slen; socklen_t slen;
405 677
678 ret = load_keys();
679 if (ret == -1) {
680 printf("Cannot load keys!\n");
681 return 1;
682 }
683
406 684 if (argc < 2) if (argc < 2)
407 685 queue_num = 0x4545; queue_num = 0x4545;
408 686 else else
409 queue_num = atoi(argv[1]);
687 queue_num = strtoul(argv[1], NULL, 0);
410 688 printf("queue_num: %u\n", queue_num); printf("queue_num: %u\n", queue_num);
411 689
412 690 nl = mnl_socket_open(NETLINK_NETFILTER); nl = mnl_socket_open(NETLINK_NETFILTER);
413 691 if (nl == NULL) { if (nl == NULL) {
414 692 perror("mnl_socket_open"); perror("mnl_socket_open");
415 exit(EXIT_FAILURE);
693 return 1;
416 694 } }
417 695
418 696 if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
419 697 perror("mnl_socket_bind"); perror("mnl_socket_bind");
420 exit(EXIT_FAILURE);
698 return 1;
421 699 } }
422 700 portid = mnl_socket_get_portid(nl); portid = mnl_socket_get_portid(nl);
423 701
 
... ... int main(int argc, char *argv[])
425 703 nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_PF_UNBIND); nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_PF_UNBIND);
426 704 if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
427 705 perror("mnl_socket_send"); perror("mnl_socket_send");
428 exit(EXIT_FAILURE);
706 return 1;
429 707 } }
430 708
431 709 nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, 0); nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, 0);
432 710 nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_PF_BIND); nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_PF_BIND);
433 711 if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
434 712 perror("mnl_socket_send"); perror("mnl_socket_send");
435 exit(EXIT_FAILURE);
713 return 1;
436 714 } }
437 715
438 716 nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, queue_num); nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, queue_num);
439 717 nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_BIND); nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_BIND);
440 718 if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
441 719 perror("mnl_socket_send"); perror("mnl_socket_send");
442 exit(EXIT_FAILURE);
720 return 1;
443 721 } }
444 722
445 723 nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, queue_num); nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, queue_num);
446 724 nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff); nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff);
447 725 if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
448 726 perror("mnl_socket_send"); perror("mnl_socket_send");
449 exit(EXIT_FAILURE);
727 return 1;
450 728 } }
451 729
452 730 /* ENOBUFS is signalled to userspace when packets were lost /* ENOBUFS is signalled to userspace when packets were lost
 
... ... int main(int argc, char *argv[])
515 793 ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
516 794 if (ret == -1) { if (ret == -1) {
517 795 perror("mnl_socket_recvfrom"); perror("mnl_socket_recvfrom");
518 exit(EXIT_FAILURE);
796 return 1;
519 797 } }
520 798
521 799 ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, NULL); ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, NULL);
522 800 if (ret < 0){ if (ret < 0){
523 801 perror("mnl_cb_run"); perror("mnl_cb_run");
524 exit(EXIT_FAILURE);
802 return 1;
525 803 } }
526 804 continue; continue;
527 805 } }
 
... ... int main(int argc, char *argv[])
558 836 printf("Cannot receive: %s\n", strerror(errno)); printf("Cannot receive: %s\n", strerror(errno));
559 837 goto clean; goto clean;
560 838 } }
561 printf("Data on UNIX socket:\n");
562 dump(buf, n);
839 printf("Data on UNIX socket:\n"); dump(buf, n);
563 840
564 841 n = process_client(ucred.uid, buf, n); n = process_client(ucred.uid, buf, n);
565 842 if (n == -1) if (n == -1)
File totp.c changed (mode: 100644) (index 6a56546..9062a69)
... ... int totp_base32_decode(unsigned char *out, const unsigned char out_size,
83 83
84 84 // Now we have more than 8 bits in buffer, extract // Now we have more than 8 bits in buffer, extract
85 85 buf_bits -= 8; buf_bits -= 8;
86 out[i++] = buf >> buf_bits; out++;
86 out[i++] = buf >> buf_bits;
87 87 buf &= ((1 << buf_bits) - 1); buf &= ((1 << buf_bits) - 1);
88 88
89 89 if (i == out_size) if (i == out_size)
 
... ... int totp_compute(char *out, const char *key, unsigned int tc,
129 129 unsigned int v; unsigned int v;
130 130 unsigned long long z; unsigned long long z;
131 131
132 printf("DEBUG: %s: key=%s tc=%u\n", __func__, key, tc);
133
132 134 r = totp_base32_decode(key_bin, sizeof(key_bin), key); r = totp_base32_decode(key_bin, sizeof(key_bin), key);
133 135 if (r == -1) if (r == -1)
134 136 return -1; return -1;
135 137 key_len = r; key_len = r;
138 dump(key_bin, key_len);
136 139
137 140 snprintf(stc, sizeof(stc), "%016x", tc); snprintf(stc, sizeof(stc), "%016x", tc);
138 printf("DBEUG: stc: %s\n", stc);
139 141 // Transform stc into a binary // Transform stc into a binary
140 142 for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
141 143 btc[i] = totp_hex2bin(stc + i * 2); btc[i] = totp_hex2bin(stc + i * 2);
 
... ... int totp_compute(char *out, const char *key, unsigned int tc,
152 154 z *= 10; z *= 10;
153 155
154 156 sprintf(format, "%%0%hhuu", digits); sprintf(format, "%%0%hhuu", digits);
155 printf("DEBUG: format=%s\n", format);
156 157 sprintf(out, format, v % z); sprintf(out, format, v % z);
157 158
158 159 return 0; return 0;
 
... ... int totp_verify_tc(const char *key, const unsigned int tc, const char *pin)
173 174 if (r == -1) if (r == -1)
174 175 return -1; return -1;
175 176
177 printf("DEBUG: tc=%u Comparing %s with %s\n", tc, cpin, pin);
176 178 if (strcmp(cpin, pin) != 0) if (strcmp(cpin, pin) != 0)
177 179 return 0; return 0;
178 180
 
... ... int totp_verify_tc(const char *key, const unsigned int tc, const char *pin)
180 182 } }
181 183
182 184 /* /*
183 * Verifies a token based on @ts, trying 2 in the past and 2 in the future,
185 * Verifies a token based on @tc, trying 2 in the past and 2 in the future,
184 186 * to allow clock skew. * to allow clock skew.
187 * @last_used - we should not allow to reuse the last token
185 188 * Returns -1 on error, 0 if they do not match, else 1 * Returns -1 on error, 0 if they do not match, else 1
186 189 */ */
187 int totp_verify(const char *key, const unsigned int ts, const char *pin)
190 int totp_verify(const char *key, const unsigned int tc,
191 const unsigned int last_used, const char *pin)
188 192 { {
189 unsigned int values[5], tc;
193 unsigned int values[5];
190 194 unsigned char i; unsigned char i;
191 195
192 196 if (*pin == '\0') if (*pin == '\0')
193 197 return -1; return -1;
194 198
195 tc = ts / 30;
196 199 values[0] = tc; values[0] = tc;
197 200 values[1] = tc - 1; values[1] = tc - 1;
198 201 values[2] = tc + 1; values[2] = tc + 1;
 
... ... int totp_verify(const char *key, const unsigned int ts, const char *pin)
200 203 values[4] = tc + 2; values[4] = tc + 2;
201 204
202 205 for (i = 0; i < 5; i++) { for (i = 0; i < 5; i++) {
206 if (values[i] <= last_used)
207 continue;
208
203 209 if (totp_verify_tc(key, values[i], pin) == 1) if (totp_verify_tc(key, values[i], pin) == 1)
204 210 return 1; return 1;
205 211 } }
 
... ... static void encode(char *out, const char *in)
222 228 continue; continue;
223 229 } }
224 230
231 #if 0
225 232 if (*in == ' ') { if (*in == ' ') {
226 233 *out = '+'; *out = '+';
227 234 out++; in++; out++; in++;
228 235 continue; continue;
229 236 } }
230
237 #endif
231 238
232 239 snprintf(out, 4, "%%%02hhx", *in); snprintf(out, 4, "%%%02hhx", *in);
233 240 out += 3; in++; out += 3; in++;
File totp.h changed (mode: 100644) (index 24a8d99..de72caf)
... ... int totp_compute(char *out, const char *key, unsigned int tc,
6 6 unsigned char digits); unsigned char digits);
7 7 int totp_verify_tc(const char *key, const unsigned int tc, int totp_verify_tc(const char *key, const unsigned int tc,
8 8 const char *pin); const char *pin);
9 int totp_verify(const char *key, const unsigned int ts,
10 const char *pin);
9 int totp_verify(const char *key, const unsigned int tc,
10 const unsigned int last_used, const char *pin);
11 11 void totp_text(const char *secret, const char *name); void totp_text(const char *secret, const char *name);
12 12
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/catalinux/nf2fa

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/catalinux/nf2fa

Clone this repository using git:
git clone git://git.rocketgit.com/user/catalinux/nf2fa

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main