Skip to content

Commit 45c8a4a

Browse files
committed
Fix DoS in listen syscall over IPv6 socket. [EN-18:11.listen]
Reported by: Jakub Jirasek, Secunia Research at Flexera Approved by: so Security: FreeBSD-EN-18:11.listen Security: CVE-2018-6925
1 parent 14ad9a1 commit 45c8a4a

File tree

3 files changed

+63
-11
lines changed

3 files changed

+63
-11
lines changed

sys/netinet/tcp_usrreq.c

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
339339
struct inpcb *inp;
340340
struct tcpcb *tp = NULL;
341341
struct sockaddr_in6 *sin6p;
342+
u_char vflagsav;
342343

343344
sin6p = (struct sockaddr_in6 *)nam;
344345
if (nam->sa_len != sizeof (*sin6p))
@@ -355,6 +356,7 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
355356
inp = sotoinpcb(so);
356357
KASSERT(inp != NULL, ("tcp6_usr_bind: inp == NULL"));
357358
INP_WLOCK(inp);
359+
vflagsav = inp->inp_vflag;
358360
if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) {
359361
error = EINVAL;
360362
goto out;
@@ -384,6 +386,8 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
384386
error = in6_pcbbind(inp, nam, td->td_ucred);
385387
INP_HASH_WUNLOCK(&V_tcbinfo);
386388
out:
389+
if (error != 0)
390+
inp->inp_vflag = vflagsav;
387391
TCPDEBUG2(PRU_BIND);
388392
TCP_PROBE2(debug__user, tp, PRU_BIND);
389393
INP_WUNLOCK(inp);
@@ -447,6 +451,7 @@ tcp6_usr_listen(struct socket *so, int backlog, struct thread *td)
447451
int error = 0;
448452
struct inpcb *inp;
449453
struct tcpcb *tp = NULL;
454+
u_char vflagsav;
450455

451456
TCPDEBUG0;
452457
inp = sotoinpcb(so);
@@ -456,6 +461,7 @@ tcp6_usr_listen(struct socket *so, int backlog, struct thread *td)
456461
error = EINVAL;
457462
goto out;
458463
}
464+
vflagsav = inp->inp_vflag;
459465
tp = intotcpcb(inp);
460466
TCPDEBUG1();
461467
SOCK_LOCK(so);
@@ -482,6 +488,9 @@ tcp6_usr_listen(struct socket *so, int backlog, struct thread *td)
482488
if (tp->t_flags & TF_FASTOPEN)
483489
tp->t_tfo_pending = tcp_fastopen_alloc_counter();
484490
#endif
491+
if (error != 0)
492+
inp->inp_vflag = vflagsav;
493+
485494
out:
486495
TCPDEBUG2(PRU_LISTEN);
487496
TCP_PROBE2(debug__user, tp, PRU_LISTEN);
@@ -558,6 +567,8 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
558567
struct inpcb *inp;
559568
struct tcpcb *tp = NULL;
560569
struct sockaddr_in6 *sin6p;
570+
u_int8_t incflagsav;
571+
u_char vflagsav;
561572

562573
TCPDEBUG0;
563574

@@ -574,6 +585,8 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
574585
inp = sotoinpcb(so);
575586
KASSERT(inp != NULL, ("tcp6_usr_connect: inp == NULL"));
576587
INP_WLOCK(inp);
588+
vflagsav = inp->inp_vflag;
589+
incflagsav = inp->inp_inc.inc_flags;
577590
if (inp->inp_flags & INP_TIMEWAIT) {
578591
error = EADDRINUSE;
579592
goto out;
@@ -603,11 +616,11 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
603616
}
604617

605618
in6_sin6_2_sin(&sin, sin6p);
606-
inp->inp_vflag |= INP_IPV4;
607-
inp->inp_vflag &= ~INP_IPV6;
608619
if ((error = prison_remote_ip4(td->td_ucred,
609620
&sin.sin_addr)) != 0)
610621
goto out;
622+
inp->inp_vflag |= INP_IPV4;
623+
inp->inp_vflag &= ~INP_IPV6;
611624
if ((error = tcp_connect(tp, (struct sockaddr *)&sin, td)) != 0)
612625
goto out;
613626
#ifdef TCP_OFFLOAD
@@ -625,11 +638,11 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
625638
}
626639
}
627640
#endif
641+
if ((error = prison_remote_ip6(td->td_ucred, &sin6p->sin6_addr)) != 0)
642+
goto out;
628643
inp->inp_vflag &= ~INP_IPV4;
629644
inp->inp_vflag |= INP_IPV6;
630645
inp->inp_inc.inc_flags |= INC_ISIPV6;
631-
if ((error = prison_remote_ip6(td->td_ucred, &sin6p->sin6_addr)) != 0)
632-
goto out;
633646
if ((error = tcp6_connect(tp, nam, td)) != 0)
634647
goto out;
635648
#ifdef TCP_OFFLOAD
@@ -642,6 +655,15 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
642655
error = tp->t_fb->tfb_tcp_output(tp);
643656

644657
out:
658+
/*
659+
* If the implicit bind in the connect call fails, restore
660+
* the flags we modified.
661+
*/
662+
if (error != 0 && inp->inp_lport == 0) {
663+
inp->inp_vflag = vflagsav;
664+
inp->inp_inc.inc_flags = incflagsav;
665+
}
666+
645667
TCPDEBUG2(PRU_CONNECT);
646668
TCP_PROBE2(debug__user, tp, PRU_CONNECT);
647669
INP_WUNLOCK(inp);

sys/netinet6/sctp6_usrreq.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,7 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
561561
struct sctp_inpcb *inp;
562562
struct in6pcb *inp6;
563563
int error;
564+
u_char vflagsav;
564565

565566
inp = (struct sctp_inpcb *)so->so_pcb;
566567
if (inp == NULL) {
@@ -591,6 +592,7 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
591592
}
592593
}
593594
inp6 = (struct in6pcb *)inp;
595+
vflagsav = inp6->inp_vflag;
594596
inp6->inp_vflag &= ~INP_IPV4;
595597
inp6->inp_vflag |= INP_IPV6;
596598
if ((addr != NULL) && (SCTP_IPV6_V6ONLY(inp6) == 0)) {
@@ -620,7 +622,7 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
620622
inp6->inp_vflag |= INP_IPV4;
621623
inp6->inp_vflag &= ~INP_IPV6;
622624
error = sctp_inpcb_bind(so, (struct sockaddr *)&sin, NULL, p);
623-
return (error);
625+
goto out;
624626
}
625627
#endif
626628
break;
@@ -637,7 +639,8 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
637639
if (addr->sa_family == AF_INET) {
638640
/* can't bind v4 addr to v6 only socket! */
639641
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL);
640-
return (EINVAL);
642+
error = EINVAL;
643+
goto out;
641644
}
642645
#endif
643646
sin6_p = (struct sockaddr_in6 *)addr;
@@ -646,10 +649,14 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, struct thread *p)
646649
/* can't bind v4-mapped addrs either! */
647650
/* NOTE: we don't support SIIT */
648651
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL);
649-
return (EINVAL);
652+
error = EINVAL;
653+
goto out;
650654
}
651655
}
652656
error = sctp_inpcb_bind(so, addr, NULL, p);
657+
out:
658+
if (error != 0)
659+
inp6->inp_vflag = vflagsav;
653660
return (error);
654661
}
655662

sys/netinet6/udp6_usrreq.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,13 +1006,15 @@ udp6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
10061006
struct inpcb *inp;
10071007
struct inpcbinfo *pcbinfo;
10081008
int error;
1009+
u_char vflagsav;
10091010

10101011
pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
10111012
inp = sotoinpcb(so);
10121013
KASSERT(inp != NULL, ("udp6_bind: inp == NULL"));
10131014

10141015
INP_WLOCK(inp);
10151016
INP_HASH_WLOCK(pcbinfo);
1017+
vflagsav = inp->inp_vflag;
10161018
inp->inp_vflag &= ~INP_IPV4;
10171019
inp->inp_vflag |= INP_IPV6;
10181020
if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) {
@@ -1040,6 +1042,8 @@ udp6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
10401042
#ifdef INET
10411043
out:
10421044
#endif
1045+
if (error != 0)
1046+
inp->inp_vflag = vflagsav;
10431047
INP_HASH_WUNLOCK(pcbinfo);
10441048
INP_WUNLOCK(inp);
10451049
return (error);
@@ -1086,6 +1090,7 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
10861090
struct inpcbinfo *pcbinfo;
10871091
struct sockaddr_in6 *sin6;
10881092
int error;
1093+
u_char vflagsav;
10891094

10901095
pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
10911096
inp = sotoinpcb(so);
@@ -1113,17 +1118,26 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
11131118
goto out;
11141119
}
11151120
in6_sin6_2_sin(&sin, sin6);
1116-
inp->inp_vflag |= INP_IPV4;
1117-
inp->inp_vflag &= ~INP_IPV6;
11181121
error = prison_remote_ip4(td->td_ucred, &sin.sin_addr);
11191122
if (error != 0)
11201123
goto out;
1124+
vflagsav = inp->inp_vflag;
1125+
inp->inp_vflag |= INP_IPV4;
1126+
inp->inp_vflag &= ~INP_IPV6;
11211127
INP_HASH_WLOCK(pcbinfo);
11221128
error = in_pcbconnect(inp, (struct sockaddr *)&sin,
11231129
td->td_ucred);
11241130
INP_HASH_WUNLOCK(pcbinfo);
1131+
/*
1132+
* If connect succeeds, mark socket as connected. If
1133+
* connect fails and socket is unbound, reset inp_vflag
1134+
* field.
1135+
*/
11251136
if (error == 0)
11261137
soisconnected(so);
1138+
else if (inp->inp_laddr.s_addr == INADDR_ANY &&
1139+
inp->inp_lport == 0)
1140+
inp->inp_vflag = vflagsav;
11271141
goto out;
11281142
} else {
11291143
if ((inp->inp_vflag & INP_IPV6) == 0) {
@@ -1136,16 +1150,25 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
11361150
error = EISCONN;
11371151
goto out;
11381152
}
1139-
inp->inp_vflag &= ~INP_IPV4;
1140-
inp->inp_vflag |= INP_IPV6;
11411153
error = prison_remote_ip6(td->td_ucred, &sin6->sin6_addr);
11421154
if (error != 0)
11431155
goto out;
1156+
vflagsav = inp->inp_vflag;
1157+
inp->inp_vflag &= ~INP_IPV4;
1158+
inp->inp_vflag |= INP_IPV6;
11441159
INP_HASH_WLOCK(pcbinfo);
11451160
error = in6_pcbconnect(inp, nam, td->td_ucred);
11461161
INP_HASH_WUNLOCK(pcbinfo);
1162+
/*
1163+
* If connect succeeds, mark socket as connected. If
1164+
* connect fails and socket is unbound, reset inp_vflag
1165+
* field.
1166+
*/
11471167
if (error == 0)
11481168
soisconnected(so);
1169+
else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) &&
1170+
inp->inp_lport == 0)
1171+
inp->inp_vflag = vflagsav;
11491172
out:
11501173
INP_WUNLOCK(inp);
11511174
return (error);

0 commit comments

Comments
 (0)