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
8 * Copyright (C) 2002-2009 OpenVPN Technologies, Inc. <sales@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
38 #include "packet_id.h"
45 * Special time_t value that indicates that
46 * sequence number has expired.
48 #define SEQ_UNSEEN ((time_t)0)
49 #define SEQ_EXPIRED ((time_t)1)
52 packet_id_init (struct packet_id
*p
, int seq_backtrack
, int time_backtrack
)
54 dmsg (D_PID_DEBUG_LOW
, "PID packet_id_init seq_backtrack=%d time_backtrack=%d",
63 ASSERT (MIN_SEQ_BACKTRACK
<= seq_backtrack
&& seq_backtrack
<= MAX_SEQ_BACKTRACK
);
64 ASSERT (MIN_TIME_BACKTRACK
<= time_backtrack
&& time_backtrack
<= MAX_TIME_BACKTRACK
);
65 CIRC_LIST_ALLOC (p
->rec
.seq_list
, struct seq_list
, seq_backtrack
);
66 p
->rec
.seq_backtrack
= seq_backtrack
;
67 p
->rec
.time_backtrack
= time_backtrack
;
69 p
->rec
.initialized
= true;
73 packet_id_free (struct packet_id
*p
)
77 dmsg (D_PID_DEBUG_LOW
, "PID packet_id_free");
79 free (p
->rec
.seq_list
);
85 packet_id_add (struct packet_id_rec
*p
, const struct packet_id_net
*pin
)
87 const time_t local_now
= now
;
93 * If time value increases, start a new
94 * sequence number sequence.
96 if (!CIRC_LIST_SIZE (p
->seq_list
)
97 || pin
->time
> p
->time
98 || (pin
->id
>= (packet_id_type
)p
->seq_backtrack
99 && pin
->id
- (packet_id_type
)p
->seq_backtrack
> p
->id
))
103 if (pin
->id
> (packet_id_type
)p
->seq_backtrack
)
104 p
->id
= pin
->id
- (packet_id_type
)p
->seq_backtrack
;
105 CIRC_LIST_RESET (p
->seq_list
);
108 while (p
->id
< pin
->id
)
110 CIRC_LIST_PUSH (p
->seq_list
, SEQ_UNSEEN
);
114 diff
= p
->id
- pin
->id
;
115 if (diff
< (packet_id_type
) CIRC_LIST_SIZE (p
->seq_list
)
116 && local_now
> SEQ_EXPIRED
)
117 CIRC_LIST_ITEM (p
->seq_list
, diff
) = local_now
;
127 * Expire sequence numbers which can no longer
128 * be accepted because they would violate
132 packet_id_reap (struct packet_id_rec
*p
)
134 const time_t local_now
= now
;
135 if (p
->time_backtrack
)
139 for (i
= 0; i
< CIRC_LIST_SIZE (p
->seq_list
); ++i
)
141 const time_t t
= CIRC_LIST_ITEM (p
->seq_list
, i
);
142 if (t
== SEQ_EXPIRED
)
144 if (!expire
&& t
&& t
+ p
->time_backtrack
< local_now
)
147 CIRC_LIST_ITEM (p
->seq_list
, i
) = SEQ_EXPIRED
;
150 p
->last_reap
= local_now
;
154 * Return true if packet id is ok, or false if
158 packet_id_test (const struct packet_id_rec
*p
,
159 const struct packet_id_net
*pin
)
161 static int max_backtrack_stat
;
165 "PID TEST " time_format
":" packet_id_format
" " time_format
":" packet_id_format
"",
166 (time_type
)p
->time
, (packet_id_print_type
)p
->id
, (time_type
)pin
->time
,
167 (packet_id_print_type
)pin
->id
);
169 ASSERT (p
->initialized
);
174 if (p
->seq_backtrack
)
177 * In backtrack mode, we allow packet reordering subject
178 * to the seq_backtrack and time_backtrack constraints.
180 * This mode is used with UDP.
182 if (pin
->time
== p
->time
)
184 /* is packet-id greater than any one we've seen yet? */
188 /* check packet-id sliding window for original/replay status */
189 diff
= p
->id
- pin
->id
;
191 /* keep track of maximum backtrack seen for debugging purposes */
192 if ((int)diff
> max_backtrack_stat
)
194 max_backtrack_stat
= (int)diff
;
195 msg (D_BACKTRACK
, "Replay-window backtrack occurred [%d]", max_backtrack_stat
);
198 if (diff
>= (packet_id_type
) CIRC_LIST_SIZE (p
->seq_list
))
201 return CIRC_LIST_ITEM (p
->seq_list
, diff
) == 0;
203 else if (pin
->time
< p
->time
) /* if time goes back, reject */
205 else /* time moved forward */
211 * In non-backtrack mode, all sequence number series must
212 * begin at some number n > 0 and must increment linearly without gaps.
214 * This mode is used with TCP.
216 if (pin
->time
== p
->time
)
217 return !p
->id
|| pin
->id
== p
->id
+ 1;
218 else if (pin
->time
< p
->time
) /* if time goes back, reject */
220 else /* time moved forward */
226 * Read/write a packet ID to/from the buffer. Short form is sequence number
227 * only. Long form is sequence number and timestamp.
231 packet_id_read (struct packet_id_net
*pin
, struct buffer
*buf
, bool long_form
)
233 packet_id_type net_id
;
239 if (!buf_read (buf
, &net_id
, sizeof (net_id
)))
241 pin
->id
= ntohpid (net_id
);
244 if (!buf_read (buf
, &net_time
, sizeof (net_time
)))
246 pin
->time
= ntohtime (net_time
);
252 packet_id_write (const struct packet_id_net
*pin
, struct buffer
*buf
, bool long_form
, bool prepend
)
254 packet_id_type net_id
= htonpid (pin
->id
);
255 net_time_t net_time
= htontime (pin
->time
);
261 if (!buf_write_prepend (buf
, &net_time
, sizeof (net_time
)))
264 if (!buf_write_prepend (buf
, &net_id
, sizeof (net_id
)))
269 if (!buf_write (buf
, &net_id
, sizeof (net_id
)))
273 if (!buf_write (buf
, &net_time
, sizeof (net_time
)))
281 packet_id_net_print (const struct packet_id_net
*pin
, bool print_timestamp
, struct gc_arena
*gc
)
283 struct buffer out
= alloc_buf_gc (256, gc
);
285 buf_printf (&out
, "[ #" packet_id_format
, (packet_id_print_type
)pin
->id
);
286 if (print_timestamp
&& pin
->time
)
287 buf_printf (&out
, " / time = (" packet_id_format
") %s",
288 (packet_id_print_type
)pin
->time
,
289 time_string (pin
->time
, 0, false, gc
));
291 buf_printf (&out
, " ]");
295 /* initialize the packet_id_persist structure in a disabled state */
297 packet_id_persist_init (struct packet_id_persist
*p
)
301 p
->time
= p
->time_last_written
= 0;
302 p
->id
= p
->id_last_written
= 0;
305 /* close the file descriptor if it is open, and switch to disabled state */
307 packet_id_persist_close (struct packet_id_persist
*p
)
309 if (packet_id_persist_enabled (p
))
312 msg (D_PID_PERSIST
| M_ERRNO
, "Close error on --replay-persist file %s", p
->filename
);
313 packet_id_persist_init (p
);
317 /* load persisted rec packet_id (time and id) only once from file, and set state to enabled */
319 packet_id_persist_load (struct packet_id_persist
*p
, const char *filename
)
321 struct gc_arena gc
= gc_new ();
322 if (!packet_id_persist_enabled (p
))
324 /* open packet-id persist file for both read and write */
325 p
->fd
= open (filename
,
326 O_CREAT
| O_RDWR
| O_BINARY
,
330 msg (D_PID_PERSIST
| M_ERRNO
,
331 "Cannot open --replay-persist file %s for read/write",
336 struct packet_id_persist_file_image image
;
339 #if defined(HAVE_FLOCK) && defined(LOCK_EX) && defined(LOCK_NB)
340 if (flock (p
->fd
, LOCK_EX
| LOCK_NB
))
341 msg (M_ERR
, "Cannot obtain exclusive lock on --replay-persist file %s", filename
);
344 p
->filename
= filename
;
345 n
= read (p
->fd
, &image
, sizeof(image
));
346 if (n
== sizeof(image
))
348 p
->time
= p
->time_last_written
= image
.time
;
349 p
->id
= p
->id_last_written
= image
.id
;
350 dmsg (D_PID_PERSIST_DEBUG
, "PID Persist Read from %s: %s",
351 p
->filename
, packet_id_persist_print (p
, &gc
));
355 msg (D_PID_PERSIST
| M_ERRNO
,
356 "Read error on --replay-persist file %s",
364 /* save persisted rec packet_id (time and id) to file (only if enabled state) */
366 packet_id_persist_save (struct packet_id_persist
*p
)
368 if (packet_id_persist_enabled (p
) && p
->time
&& (p
->time
!= p
->time_last_written
||
369 p
->id
!= p
->id_last_written
))
371 struct packet_id_persist_file_image image
;
374 struct gc_arena gc
= gc_new ();
376 image
.time
= p
->time
;
378 seek_ret
= lseek(p
->fd
, (off_t
)0, SEEK_SET
);
379 if (seek_ret
== (off_t
)0)
381 n
= write(p
->fd
, &image
, sizeof(image
));
382 if (n
== sizeof(image
))
384 p
->time_last_written
= p
->time
;
385 p
->id_last_written
= p
->id
;
386 dmsg (D_PID_PERSIST_DEBUG
, "PID Persist Write to %s: %s",
387 p
->filename
, packet_id_persist_print (p
, &gc
));
391 msg (D_PID_PERSIST
| M_ERRNO
,
392 "Cannot write to --replay-persist file %s",
398 msg (D_PID_PERSIST
| M_ERRNO
,
399 "Cannot seek to beginning of --replay-persist file %s",
406 /* transfer packet_id_persist -> packet_id */
408 packet_id_persist_load_obj (const struct packet_id_persist
*p
, struct packet_id
*pid
)
410 if (p
&& pid
&& packet_id_persist_enabled (p
) && p
->time
)
412 pid
->rec
.time
= p
->time
;
418 packet_id_persist_print (const struct packet_id_persist
*p
, struct gc_arena
*gc
)
420 struct buffer out
= alloc_buf_gc (256, gc
);
422 buf_printf (&out
, "[");
424 if (packet_id_persist_enabled (p
))
426 buf_printf (&out
, " #" packet_id_format
, (packet_id_print_type
)p
->id
);
428 buf_printf (&out
, " / time = (" packet_id_format
") %s",
429 (packet_id_print_type
)p
->time
,
430 time_string (p
->time
, 0, false, gc
));
433 buf_printf (&out
, " ]");
434 return (char *)out
.data
;
440 packet_id_interactive_test ()
442 struct packet_id pid
;
443 struct packet_id_net pin
;
448 const int seq_backtrack
= 10;
449 const int time_backtrack
= 10;
451 packet_id_init (&pid
, seq_backtrack
, time_backtrack
);
455 if (!fgets(buf
, sizeof(buf
), stdin
))
458 if (sscanf (buf
, "%lu,%u", &pin
.time
, &pin
.id
) == 2)
460 packet_id_reap_test (&pid
.rec
);
461 test
= packet_id_test (&pid
.rec
, &pin
);
462 printf ("packet_id_test (" time_format
", " packet_id_format
") returned %d\n",
464 (packet_id_print_type
)pin
.id
,
467 packet_id_add (&pid
.rec
, &pin
);
471 long_form
= (count
< 20);
472 packet_id_alloc_outgoing (&pid
.send
, &pin
, long_form
);
473 printf ("(" time_format
"(" packet_id_format
"), %d)\n",
475 (packet_id_print_type
)pin
.id
,
477 if (pid
.send
.id
== 10)
478 pid
.send
.id
= 0xFFFFFFF8;
482 packet_id_free (&pid
);
486 #endif /* USE_CRYPTO */