1 /* PKNF
2 a portknocking Netfilter LKM for linux kernel >= 2.6.25
3 Copyright (C) 2008 ithilgore - ithilgore.ryu.L@gmail.com
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /* usage: insmod pknf.ko
20 * OR insmod pknf.ko port_trigs=19,26,... max_time=2
21 * port_trigs list has to be less or equal than MAX_PORTS
22 * max_time is defined in seconds - see source
23 */
24
25
26 #include <linux/init.h>
27 #include <linux/module.h>
28 #include <linux/kernel.h>
29 #include <linux/sched.h>
30 #include <linux/skbuff.h>
31 #include <linux/ip.h>
32 #include <linux/tcp.h>
33 #include <linux/netfilter_ipv4.h>
34 #include <linux/netfilter.h>
35
36 MODULE_AUTHOR("ithilgore - ithilgore.ryu.L@gmail.com");
37 MODULE_DESCRIPTION("port knocking netfilter hook");
38 MODULE_LICENSE("GPL");
39 MODULE_VERSION("0.7");
40
41 #define MAX_PORTS 5
42 #define MAX_TIME 5
43 /*
44 * ports: number of ports - overriden by user parameter
45 * port_trigs: array containing port sequence
46 * port_flags: array containing flags for so far triggered ports
47 * timestamp: time when first knock hit successfully
48 * max_time: maximum time (in seconds) between port knocks
49 */
50 static unsigned int ports = MAX_PORTS;
51 static unsigned int port_flags[MAX_PORTS];
52 static unsigned short port_trigs[MAX_PORTS] = { 25, 23, 21, 2340, 9047 };
53 static unsigned long timestamp = 0;
54 static unsigned long max_time = MAX_TIME;
55
56 /* hook register struct */
57 static struct nf_hook_ops nfho;
58
59 /*** functions ***/
60 static inline void clean_flags(void);
61 static int test_port(unsigned int portnum, unsigned short dport);
62 static int exec_sshd(void);
63 static inline int time_expired(void);
64 #ifdef DEBUG
65 static inline void print_flags(void); /* debugging routine */
66 #endif
67
68 /*** parameters ***/
69 module_param_array(port_trigs, ushort, &ports, 0);
70 module_param(max_time, ulong, 0);
71
72
73
74 /* returns:
75 * - 1: time expired - caller must reset port flags
76 * - 0: hasn't expired yet
77 */
78 static inline int
79 time_expired(void)
80 {
81 unsigned long current_time = jiffies;
82 #ifdef DEBUG
83 printk("%ld %ld \n", current_time, timestamp + max_time);
84 #endif
85
86 if (time_after(current_time, timestamp + max_time)) {
87 #ifdef DEBUG
88 printk("timer expired !!!\n");
89 #endif
90 return 1;
91 }
92 else
93 return 0;
94 }
95
96 /* execute sshd for userspace */
97 static int
98 exec_sshd(void)
99 {
100 static char *path = "/usr/sbin/sshd";
101 static char *argv[] = { "/usr/sbin/sshd", NULL };
102 static char *envp[] = {
103 "HOME=/",
104 "PATH=/sbin/:/usr/sbin/:/bin/:/usr/bin/:/usr/local\
105 /bin/:/usr/local/sbin",
106 NULL };
107 int ret;
108
109 ret = call_usermodehelper(path, argv, envp, 1);
110 #ifdef DEBUG
111 if (ret)
112 printk("failed to exec sshd\n");
113 #endif
114 return ret;
115 }
116
117 #ifdef DEBUG
118 /* debugging function */
119 static inline void
120 print_flags(void)
121 {
122 int i;
123 printk("port_flags:");
124 for (i = 0; i < ports; i++) {
125 printk(" %d", port_flags[i]);
126 }
127 printk("\n");
128
129 }
130 #endif
131
132 /* cleanup flags after timer expires */
133 static inline void
134 clean_flags(void)
135 {
136 memset(port_flags, 0, sizeof(port_flags));
137 }
138
139
140 /* test if particular port is the correct one
141 * in the sequence
142 * returns:
143 * 0 --- don't do anything with this packet
144 * 1 --- full port knocking sequence SUCCESSFUL
145 * -1 --- tell caller to drop the packet -> NF_DROP
146 */
147 static int
148 test_port(unsigned int portnum, unsigned short dport)
149 {
150 unsigned int i;
151 /* test if a port in the pksequence is in the packet and if it is,
152 * check if all the previous ports have been already triggered
153 */
154 if (dport == htons(port_trigs[portnum])) {
155 if (!port_flags[portnum]) {
156 for (i = 0; i < portnum; i++) {
157 if (!port_flags[portnum - i - 1]) {
158 clean_flags();
159 #ifdef DEBUG
160 print_flags();
161 #endif
162 return -1;
163 }
164 }
165 port_flags[portnum] = 1;
166 timestamp = jiffies;
167 //printk("port %d triggered\n", portnum);
168 }
169 else {
170 clean_flags();
171 }
172 /* if you reach here then port knocking sequence is
173 * progressing so far */
174 #ifdef DEBUG
175 print_flags();
176 #endif
177 if (portnum == ports - 1) /* final success */
178 return 1;
179
180 return -1;
181 }
182
183 return 0;
184 }
185
186
187 /* netfilter hook function */
188 static unsigned int
189 hook_func(
190 unsigned int hooknum,
191 struct sk_buff *skb,
192 const struct net_device *in,
193 const struct net_device *out,
194 int (*okfn)(struct sk_buff *))
195 {
196 struct iphdr *iph;
197 struct tcphdr *tcph;
198 unsigned int i;
199 int test_ret;
200
201 iph = ip_hdr(skb); /* 2.6.25 quirks - no more
202 nh,h,mac unions in skbuff */
203
204 //printk("saddr %x : %x \n", iph->saddr, (iph->saddr & 0x000000ff));
205
206 if (time_expired())
207 clean_flags();
208
209 if (iph->protocol != IPPROTO_TCP) {
210 //printk("protocol not tcp\n");
211 return NF_ACCEPT;
212 }
213
214 /* normally to get the tcp header we would need
215 * to use skb_header_pointer() knowing the offset of skb->data
216 * where the tcp header actually begins - however I don't know
217 * at the moment a way to get this value
218 */
219
220 tcph = (struct tcphdr *)(skb->data + (iph->ihl * 4));
221
222 if (!tcph) {
223 //printk("tcp header zero\n");
224 return NF_ACCEPT;
225 }
226 //printk("dport: %x \n", tcph->dest);
227
228 /* test the destination port of the packet received for a port
229 * in the port knocking sequence as defined in port_trigs[]
230 */
231 for (i = 0; i < ports; i++) {
232 test_ret = test_port(i, tcph->dest);
233 if (test_ret < 0) {
234 return NF_DROP;
235 }
236 else if (test_ret == 1) {
237 //printk("PORTKNOCK!!\n");
238 exec_sshd();
239 clean_flags();
240 }
241 }
242
243 clean_flags();
244 #ifdef DEBUG
245 print_flags();
246 #endif
247
248 return NF_ACCEPT;
249 }
250
251
252 /* Initialization routine */
253 static int __init init(void)
254 {
255 nfho.hook = hook_func;
256 nfho.hooknum = NF_INET_PRE_ROUTING;
257 nfho.pf = PF_INET;
258 nfho.priority = NF_IP_PRI_FIRST;
259
260 nf_register_hook(&nfho);
261
262 clean_flags();
263 max_time *= HZ; /* workaround for parameter issue */
264
265 return 0;
266 }
267
268 /* Cleanup routine */
269 static void __exit cleanup(void)
270 {
271 nf_unregister_hook(&nfho);
272 }
273
274 module_init(init);
275 module_exit(cleanup);