big svn cleanup
[anytun.git] / src / openvpn / packet_id.c
blob6bc5b6bd3946d0c29e436f913c12f91365ae4711
1 /*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
8 * Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program (see the file COPYING included with this
21 * distribution); if not, write to the Free Software Foundation, Inc.,
22 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * These routines are designed to catch replay attacks,
27 * where a man-in-the-middle captures packets and then
28 * attempts to replay them back later.
30 * We use the "sliding-window" algorithm, similar
31 * to IPSec.
34 #ifdef WIN32
35 #include "config-win32.h"
36 #else
37 #include "config.h"
38 #endif
40 #ifdef USE_CRYPTO
42 #include "syshead.h"
44 #include "packet_id.h"
45 #include "misc.h"
46 #include "integer.h"
48 #include "memdbg.h"
51 * Special time_t value that indicates that
52 * sequence number has expired.
54 #define SEQ_UNSEEN ((time_t)0)
55 #define SEQ_EXPIRED ((time_t)1)
57 void
58 packet_id_init (struct packet_id *p, int seq_backtrack, int time_backtrack)
60 dmsg (D_PID_DEBUG_LOW, "PID packet_id_init seq_backtrack=%d time_backtrack=%d",
61 seq_backtrack,
62 time_backtrack);
64 ASSERT (p);
65 CLEAR (*p);
67 if (seq_backtrack)
69 ASSERT (MIN_SEQ_BACKTRACK <= seq_backtrack && seq_backtrack <= MAX_SEQ_BACKTRACK);
70 ASSERT (MIN_TIME_BACKTRACK <= time_backtrack && time_backtrack <= MAX_TIME_BACKTRACK);
71 CIRC_LIST_ALLOC (p->rec.seq_list, struct seq_list, seq_backtrack);
72 p->rec.seq_backtrack = seq_backtrack;
73 p->rec.time_backtrack = time_backtrack;
75 p->rec.initialized = true;
78 void
79 packet_id_free (struct packet_id *p)
81 if (p)
83 dmsg (D_PID_DEBUG_LOW, "PID packet_id_free");
84 if (p->rec.seq_list)
85 free (p->rec.seq_list);
86 CLEAR (*p);
90 void
91 packet_id_add (struct packet_id_rec *p, const struct packet_id_net *pin)
93 const time_t local_now = now;
94 if (p->seq_list)
96 packet_id_type diff;
99 * If time value increases, start a new
100 * sequence number sequence.
102 if (!CIRC_LIST_SIZE (p->seq_list)
103 || pin->time > p->time
104 || (pin->id >= (packet_id_type)p->seq_backtrack
105 && pin->id - (packet_id_type)p->seq_backtrack > p->id))
107 p->time = pin->time;
108 p->id = 0;
109 if (pin->id > (packet_id_type)p->seq_backtrack)
110 p->id = pin->id - (packet_id_type)p->seq_backtrack;
111 CIRC_LIST_RESET (p->seq_list);
114 while (p->id < pin->id)
116 CIRC_LIST_PUSH (p->seq_list, SEQ_UNSEEN);
117 ++p->id;
120 diff = p->id - pin->id;
121 if (diff < (packet_id_type) CIRC_LIST_SIZE (p->seq_list)
122 && local_now > SEQ_EXPIRED)
123 CIRC_LIST_ITEM (p->seq_list, diff) = local_now;
125 else
127 p->time = pin->time;
128 p->id = pin->id;
133 * Expire sequence numbers which can no longer
134 * be accepted because they would violate
135 * time_backtrack.
137 void
138 packet_id_reap (struct packet_id_rec *p)
140 const time_t local_now = now;
141 if (p->time_backtrack)
143 int i;
144 bool expire = false;
145 for (i = 0; i < CIRC_LIST_SIZE (p->seq_list); ++i)
147 const time_t t = CIRC_LIST_ITEM (p->seq_list, i);
148 if (t == SEQ_EXPIRED)
149 break;
150 if (!expire && t && t + p->time_backtrack < local_now)
151 expire = true;
152 if (expire)
153 CIRC_LIST_ITEM (p->seq_list, i) = SEQ_EXPIRED;
156 p->last_reap = local_now;
160 * Return true if packet id is ok, or false if
161 * it is a replay.
163 bool
164 packet_id_test (const struct packet_id_rec *p,
165 const struct packet_id_net *pin)
167 static int max_backtrack_stat;
168 packet_id_type diff;
170 dmsg (D_PID_DEBUG,
171 "PID TEST " time_format ":" packet_id_format " " time_format ":" packet_id_format "",
172 (time_type)p->time, (packet_id_print_type)p->id, (time_type)pin->time,
173 (packet_id_print_type)pin->id);
175 ASSERT (p->initialized);
177 if (!pin->id)
178 return false;
180 if (p->seq_backtrack)
183 * In backtrack mode, we allow packet reordering subject
184 * to the seq_backtrack and time_backtrack constraints.
186 * This mode is used with UDP.
188 if (pin->time == p->time)
190 /* is packet-id greater than any one we've seen yet? */
191 if (pin->id > p->id)
192 return true;
194 /* check packet-id sliding window for original/replay status */
195 diff = p->id - pin->id;
197 /* keep track of maximum backtrack seen for debugging purposes */
198 if ((int)diff > max_backtrack_stat)
200 max_backtrack_stat = (int)diff;
201 msg (D_BACKTRACK, "Replay-window backtrack occurred [%d]", max_backtrack_stat);
204 if (diff >= (packet_id_type) CIRC_LIST_SIZE (p->seq_list))
205 return false;
207 return CIRC_LIST_ITEM (p->seq_list, diff) == 0;
209 else if (pin->time < p->time) /* if time goes back, reject */
210 return false;
211 else /* time moved forward */
212 return true;
214 else
217 * In non-backtrack mode, all sequence number series must
218 * begin at 1 and must increment linearly without gaps.
220 * This mode is used with TCP.
222 if (pin->time == p->time)
223 return pin->id == p->id + 1;
224 else if (pin->time < p->time) /* if time goes back, reject */
225 return false;
226 else /* time moved forward */
227 return pin->id == 1;
232 * Read/write a packet ID to/from the buffer. Short form is sequence number
233 * only. Long form is sequence number and timestamp.
236 bool
237 packet_id_read (struct packet_id_net *pin, struct buffer *buf, bool long_form)
239 packet_id_type net_id;
240 net_time_t net_time;
242 pin->id = 0;
243 pin->time = 0;
245 if (!buf_read (buf, &net_id, sizeof (net_id)))
246 return false;
247 pin->id = ntohpid (net_id);
248 if (long_form)
250 if (!buf_read (buf, &net_time, sizeof (net_time)))
251 return false;
252 pin->time = ntohtime (net_time);
254 return true;
257 bool
258 packet_id_write (const struct packet_id_net *pin, struct buffer *buf, bool long_form, bool prepend)
260 packet_id_type net_id = htonpid (pin->id);
261 net_time_t net_time = htontime (pin->time);
263 if (prepend)
265 if (long_form)
267 if (!buf_write_prepend (buf, &net_time, sizeof (net_time)))
268 return false;
270 if (!buf_write_prepend (buf, &net_id, sizeof (net_id)))
271 return false;
273 else
275 if (!buf_write (buf, &net_id, sizeof (net_id)))
276 return false;
277 if (long_form)
279 if (!buf_write (buf, &net_time, sizeof (net_time)))
280 return false;
283 return true;
286 const char *
287 packet_id_net_print (const struct packet_id_net *pin, bool print_timestamp, struct gc_arena *gc)
289 struct buffer out = alloc_buf_gc (256, gc);
291 buf_printf (&out, "[ #" packet_id_format, (packet_id_print_type)pin->id);
292 if (print_timestamp && pin->time)
293 buf_printf (&out, " / time = (" packet_id_format ") %s",
294 (packet_id_print_type)pin->time,
295 time_string (pin->time, 0, false, gc));
297 buf_printf (&out, " ]");
298 return BSTR (&out);
301 /* initialize the packet_id_persist structure in a disabled state */
302 void
303 packet_id_persist_init (struct packet_id_persist *p)
305 p->filename = NULL;
306 p->fd = -1;
307 p->time = p->time_last_written = 0;
308 p->id = p->id_last_written = 0;
311 /* close the file descriptor if it is open, and switch to disabled state */
312 void
313 packet_id_persist_close (struct packet_id_persist *p)
315 if (packet_id_persist_enabled (p))
317 if (close (p->fd))
318 msg (D_PID_PERSIST | M_ERRNO, "Close error on --replay-persist file %s", p->filename);
319 packet_id_persist_init (p);
323 /* load persisted rec packet_id (time and id) only once from file, and set state to enabled */
324 void
325 packet_id_persist_load (struct packet_id_persist *p, const char *filename)
327 struct gc_arena gc = gc_new ();
328 if (!packet_id_persist_enabled (p))
330 /* open packet-id persist file for both read and write */
331 p->fd = open (filename,
332 O_CREAT | O_RDWR | O_BINARY,
333 S_IRUSR | S_IWUSR);
334 if (p->fd == -1)
336 msg (D_PID_PERSIST | M_ERRNO,
337 "Cannot open --replay-persist file %s for read/write",
338 filename);
340 else
342 struct packet_id_persist_file_image image;
343 ssize_t n;
345 #if defined(HAVE_FLOCK) && defined(LOCK_EX) && defined(LOCK_NB)
346 if (flock (p->fd, LOCK_EX | LOCK_NB))
347 msg (M_ERR, "Cannot obtain exclusive lock on --replay-persist file %s", filename);
348 #endif
350 p->filename = filename;
351 n = read (p->fd, &image, sizeof(image));
352 if (n == sizeof(image))
354 p->time = p->time_last_written = image.time;
355 p->id = p->id_last_written = image.id;
356 dmsg (D_PID_PERSIST_DEBUG, "PID Persist Read from %s: %s",
357 p->filename, packet_id_persist_print (p, &gc));
359 else if (n == -1)
361 msg (D_PID_PERSIST | M_ERRNO,
362 "Read error on --replay-persist file %s",
363 p->filename);
367 gc_free (&gc);
370 /* save persisted rec packet_id (time and id) to file (only if enabled state) */
371 void
372 packet_id_persist_save (struct packet_id_persist *p)
374 if (packet_id_persist_enabled (p) && p->time && (p->time != p->time_last_written ||
375 p->id != p->id_last_written))
377 struct packet_id_persist_file_image image;
378 ssize_t n;
379 off_t seek_ret;
380 struct gc_arena gc = gc_new ();
382 image.time = p->time;
383 image.id = p->id;
384 seek_ret = lseek(p->fd, (off_t)0, SEEK_SET);
385 if (seek_ret == (off_t)0)
387 n = write(p->fd, &image, sizeof(image));
388 if (n == sizeof(image))
390 p->time_last_written = p->time;
391 p->id_last_written = p->id;
392 dmsg (D_PID_PERSIST_DEBUG, "PID Persist Write to %s: %s",
393 p->filename, packet_id_persist_print (p, &gc));
395 else
397 msg (D_PID_PERSIST | M_ERRNO,
398 "Cannot write to --replay-persist file %s",
399 p->filename);
402 else
404 msg (D_PID_PERSIST | M_ERRNO,
405 "Cannot seek to beginning of --replay-persist file %s",
406 p->filename);
408 gc_free (&gc);
412 /* transfer packet_id_persist -> packet_id */
413 void
414 packet_id_persist_load_obj (const struct packet_id_persist *p, struct packet_id *pid)
416 if (p && pid && packet_id_persist_enabled (p) && p->time)
418 pid->rec.time = p->time;
419 pid->rec.id = p->id;
423 const char *
424 packet_id_persist_print (const struct packet_id_persist *p, struct gc_arena *gc)
426 struct buffer out = alloc_buf_gc (256, gc);
428 buf_printf (&out, "[");
430 if (packet_id_persist_enabled (p))
432 buf_printf (&out, " #" packet_id_format, (packet_id_print_type)p->id);
433 if (p->time)
434 buf_printf (&out, " / time = (" packet_id_format ") %s",
435 (packet_id_print_type)p->time,
436 time_string (p->time, 0, false, gc));
439 buf_printf (&out, " ]");
440 return (char *)out.data;
443 #ifdef PID_TEST
445 void
446 packet_id_interactive_test ()
448 struct packet_id pid;
449 struct packet_id_net pin;
450 bool long_form;
451 bool count = 0;
452 bool test;
454 const int seq_backtrack = 10;
455 const int time_backtrack = 10;
457 packet_id_init (&pid, seq_backtrack, time_backtrack);
459 while (true) {
460 char buf[80];
461 if (!fgets(buf, sizeof(buf), stdin))
462 break;
463 update_time ();
464 if (sscanf (buf, "%lu,%u", &pin.time, &pin.id) == 2)
466 packet_id_reap_test (&pid.rec);
467 test = packet_id_test (&pid.rec, &pin);
468 printf ("packet_id_test (" time_format ", " packet_id_format ") returned %d\n",
469 (time_type)pin.time,
470 (packet_id_print_type)pin.id,
471 test);
472 if (test)
473 packet_id_add (&pid.rec, &pin);
475 else
477 long_form = (count < 20);
478 packet_id_alloc_outgoing (&pid.send, &pin, long_form);
479 printf ("(" time_format "(" packet_id_format "), %d)\n",
480 (time_type)pin.time,
481 (packet_id_print_type)pin.id,
482 long_form);
483 if (pid.send.id == 10)
484 pid.send.id = 0xFFFFFFF8;
485 ++count;
488 packet_id_free (&pid);
490 #endif
492 #endif /* USE_CRYPTO */