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-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
35 #include "config-win32.h"
44 #include "packet_id.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)
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",
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;
79 packet_id_free (struct packet_id
*p
)
83 dmsg (D_PID_DEBUG_LOW
, "PID packet_id_free");
85 free (p
->rec
.seq_list
);
91 packet_id_add (struct packet_id_rec
*p
, const struct packet_id_net
*pin
)
93 const time_t local_now
= now
;
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
))
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
);
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
;
133 * Expire sequence numbers which can no longer
134 * be accepted because they would violate
138 packet_id_reap (struct packet_id_rec
*p
)
140 const time_t local_now
= now
;
141 if (p
->time_backtrack
)
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
)
150 if (!expire
&& t
&& t
+ p
->time_backtrack
< local_now
)
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
164 packet_id_test (const struct packet_id_rec
*p
,
165 const struct packet_id_net
*pin
)
167 static int max_backtrack_stat
;
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
);
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? */
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
))
207 return CIRC_LIST_ITEM (p
->seq_list
, diff
) == 0;
209 else if (pin
->time
< p
->time
) /* if time goes back, reject */
211 else /* time moved forward */
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 */
226 else /* time moved forward */
232 * Read/write a packet ID to/from the buffer. Short form is sequence number
233 * only. Long form is sequence number and timestamp.
237 packet_id_read (struct packet_id_net
*pin
, struct buffer
*buf
, bool long_form
)
239 packet_id_type net_id
;
245 if (!buf_read (buf
, &net_id
, sizeof (net_id
)))
247 pin
->id
= ntohpid (net_id
);
250 if (!buf_read (buf
, &net_time
, sizeof (net_time
)))
252 pin
->time
= ntohtime (net_time
);
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
);
267 if (!buf_write_prepend (buf
, &net_time
, sizeof (net_time
)))
270 if (!buf_write_prepend (buf
, &net_id
, sizeof (net_id
)))
275 if (!buf_write (buf
, &net_id
, sizeof (net_id
)))
279 if (!buf_write (buf
, &net_time
, sizeof (net_time
)))
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
, " ]");
301 /* initialize the packet_id_persist structure in a disabled state */
303 packet_id_persist_init (struct packet_id_persist
*p
)
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 */
313 packet_id_persist_close (struct packet_id_persist
*p
)
315 if (packet_id_persist_enabled (p
))
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 */
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
,
336 msg (D_PID_PERSIST
| M_ERRNO
,
337 "Cannot open --replay-persist file %s for read/write",
342 struct packet_id_persist_file_image image
;
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
);
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
));
361 msg (D_PID_PERSIST
| M_ERRNO
,
362 "Read error on --replay-persist file %s",
370 /* save persisted rec packet_id (time and id) to file (only if enabled state) */
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
;
380 struct gc_arena gc
= gc_new ();
382 image
.time
= p
->time
;
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
));
397 msg (D_PID_PERSIST
| M_ERRNO
,
398 "Cannot write to --replay-persist file %s",
404 msg (D_PID_PERSIST
| M_ERRNO
,
405 "Cannot seek to beginning of --replay-persist file %s",
412 /* transfer packet_id_persist -> packet_id */
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
;
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
);
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
;
446 packet_id_interactive_test ()
448 struct packet_id pid
;
449 struct packet_id_net pin
;
454 const int seq_backtrack
= 10;
455 const int time_backtrack
= 10;
457 packet_id_init (&pid
, seq_backtrack
, time_backtrack
);
461 if (!fgets(buf
, sizeof(buf
), stdin
))
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",
470 (packet_id_print_type
)pin
.id
,
473 packet_id_add (&pid
.rec
, &pin
);
477 long_form
= (count
< 20);
478 packet_id_alloc_outgoing (&pid
.send
, &pin
, long_form
);
479 printf ("(" time_format
"(" packet_id_format
"), %d)\n",
481 (packet_id_print_type
)pin
.id
,
483 if (pid
.send
.id
== 10)
484 pid
.send
.id
= 0xFFFFFFF8;
488 packet_id_free (&pid
);
492 #endif /* USE_CRYPTO */