]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/ppp/thw.c
ppp: fix buffer overflow, set correct state after chap negotiation (thanks k0ga)
[plan9front.git] / sys / src / cmd / ip / ppp / thw.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include <auth.h>
5 #include "ppp.h"
6 #include "thwack.h"
7
8 typedef struct Cstate Cstate;
9 struct Cstate
10 {
11         ulong           seq;
12         Thwack          th;
13         ulong           stats[ThwStats];
14 };
15
16 typedef struct Uncstate Uncstate;
17 struct Uncstate
18 {
19         QLock           ackl;                   /* lock for acks sent back to compressor */
20         int             doack;                  /* send an ack? */
21         int             badpacks;               /* bad packets seen in a row */
22         ulong           ackseq;                 /* packets to ack */
23         int             ackmask;
24
25         int             active;                 /* 0 => waiting for resetack */
26         int             resetid;                /* id of most recent reset */
27         Unthwack        ut;
28 };
29
30 enum
31 {
32         ThwAcked        = 1UL << 23,
33         ThwCompMask     = 3UL << 21,
34         ThwCompressed   = 0UL << 21,
35         ThwUncomp       = 1UL << 21,
36         ThwUncompAdd    = 2UL << 21,            /* uncompressed, but add to decompression buffer */
37         ThwSeqMask      = 0x0fffff,
38         ThwSmallPack    = 96,
39 };
40
41 static  void            *compinit(PPP*);
42 static  Block*          comp(PPP*, ushort, Block*, int*);
43 static  Block           *compresetreq(void*, Block*);
44 static  void            compcompack(void*, Block*);
45 static  void            compfini(void*);
46
47 static  void            *uncinit(PPP*);
48 static  Block*          uncomp(PPP*, Block*, int *protop, Block**);
49 static  void            uncfini(void*);
50 static  void            uncresetack(void*, Block*);
51
52 Comptype cthwack = {
53         compinit,
54         comp,
55         compresetreq,
56         compfini
57 };
58
59 Uncomptype uncthwack = {
60         uncinit,
61         uncomp,
62         uncresetack,
63         uncfini
64 };
65
66 static void *
67 compinit(PPP *)
68 {
69         Cstate *cs;
70
71         cs = mallocz(sizeof(Cstate), 1);
72         thwackinit(&cs->th);
73         return cs;
74 }
75
76 static void
77 compfini(void *as)
78 {
79         Cstate *cs;
80
81         cs = as;
82         thwackcleanup(&cs->th);
83         free(cs);
84 }
85
86
87 static Block *
88 compresetreq(void *as, Block *b)
89 {
90         Cstate *cs;
91         Lcpmsg *m;
92         int id;
93
94         cs = as;
95         m = (Lcpmsg*)b->rptr;
96         id = m->id;
97
98         thwackinit(&cs->th);
99
100         freeb(b);
101
102         netlog("thwack resetreq id=%d \n", id);
103
104         b = alloclcp(Lresetack, id, 4, &m);
105         hnputs(m->len, 4);
106
107         return b;
108 }
109
110 static Block*
111 comp(PPP *ppp, ushort proto, Block *b, int *protop)
112 {
113         Uncstate *uncs;
114         Cstate *cs;
115         Block *bb;
116         ulong seq, acked;
117         int n, nn, mustadd;
118
119         cs = ppp->cstate;
120         *protop = 0;
121
122         /* put ack and protocol into b */
123         n = BLEN(b);
124         if(b->rptr - (2+4) < b->base)
125                 sysfatal("thwack: not enough header in block");
126         acked = 0;
127         if(ppp->unctype == &uncthwack){
128                 uncs = ppp->uncstate;
129                 qlock(&uncs->ackl);
130                 if(uncs->doack){
131                         uncs->doack = 0;
132                         b->rptr -= 4;
133                         b->rptr[0] = uncs->ackseq >> 16;
134                         b->rptr[1] = uncs->ackseq >> 8;
135                         b->rptr[2] = uncs->ackseq;
136                         b->rptr[3] = uncs->ackmask;
137                         acked = ThwAcked;
138                 }
139                 qunlock(&uncs->ackl);
140         }
141         if(proto > 0xff){
142                 b->rptr -= 2;
143                 b->rptr[0] = proto >> 8;
144                 b->rptr[1] = proto;
145         }else{
146                 b->rptr--;
147                 b->rptr[0] = proto;
148         }
149
150         bb = allocb(BLEN(b) + 3);
151
152         seq = cs->seq;
153         if(n <= 3){
154                 mustadd = 0;
155                 nn = -1;
156         }else{
157                 mustadd = n < ThwSmallPack;
158                 nn = thwack(&cs->th, mustadd, bb->wptr + 3, n - 3, b, seq, cs->stats);
159         }
160         if(nn < 0 && !mustadd){
161                 if(!acked || BLEN(b) + 1 > ppp->mtu){
162                         freeb(bb);
163                         if(acked)
164                                 b->rptr += 4;
165                         if(proto > 0xff)
166                                 b->rptr += 2;
167                         else
168                                 b->rptr++;
169                         *protop = proto;
170                         return b;
171                 }
172                 bb->wptr[0] = (ThwUncomp | ThwAcked) >> 16;
173
174                 memmove(bb->wptr + 1, b->rptr, BLEN(b));
175
176                 bb->wptr += BLEN(b) + 1;
177                 freeb(b);
178         }else{
179                 cs->seq = (seq + 1) & ThwSeqMask;
180                 if(nn < 0){
181                         nn = BLEN(b);
182                         memmove(bb->wptr + 3, b->rptr, nn);
183                         seq |= ThwUncompAdd;
184                 }else
185                         seq |= ThwCompressed;
186                 seq |= acked;
187                 bb->wptr[0] = seq>>16;
188                 bb->wptr[1] = seq>>8;
189                 bb->wptr[2] = seq;
190
191                 bb->wptr += nn + 3;
192         }
193
194         *protop = Pcdata;
195         return bb;
196 }
197
198 static  void *
199 uncinit(PPP *)
200 {
201         Uncstate *s;
202
203         s = mallocz(sizeof(Uncstate), 1);
204
205         s->active = 1;
206
207         unthwackinit(&s->ut);
208
209         return s;
210 }
211
212 static  void
213 uncfini(void *as)
214 {
215         free(as);
216 }
217
218 static  void
219 uncresetack(void *as, Block *b)
220 {
221         Uncstate *s;
222         Lcpmsg *m;
223
224         s = as;
225         m = (Lcpmsg*)b->rptr;
226
227         /*
228          * rfc 1962 says we must reset every message
229          * we don't since we may have acked some messages
230          * which the compressor will use in the future.
231          */
232         netlog("unthwack resetack id=%d resetid=%d active=%d\n", m->id, s->resetid, s->active);
233         if(m->id == (uchar)s->resetid && !s->active){
234                 s->active = 1;
235                 unthwackinit(&s->ut);
236         }
237 }
238
239 static  Block*
240 uncomp(PPP *ppp, Block *bb, int *protop, Block **reply)
241 {
242         Lcpmsg *m;
243         Cstate *cs;
244         Uncstate *uncs;
245         Block *b, *r;
246         ulong seq, mseq;
247         ushort proto;
248         uchar mask;
249         int n;
250
251         *reply = nil;
252         *protop = 0;
253         uncs = ppp->uncstate;
254
255         if(BLEN(bb) < 4){
256                 syslog(0, "ppp", ": thwack: short packet");
257                 freeb(bb);
258                 return nil;
259         }
260
261         if(!uncs->active){
262                 netlog("unthwack: inactive, killing packet\n");
263                 freeb(bb);
264                 r = alloclcp(Lresetreq, uncs->resetid, 4, &m);
265                 hnputs(m->len, 4);
266                 *reply = r;
267                 return nil;
268         }
269
270         seq = bb->rptr[0] << 16;
271         if((seq & ThwCompMask) == ThwUncomp){
272                 bb->rptr++;
273                 b = bb;
274         }else{
275                 seq |= (bb->rptr[1]<<8) | bb->rptr[2];
276                 bb->rptr += 3;
277                 if((seq & ThwCompMask) == ThwCompressed){
278                         b = allocb(ThwMaxBlock);
279                         n = unthwack(&uncs->ut, b->wptr, ThwMaxBlock, bb->rptr, BLEN(bb), seq & ThwSeqMask);
280                         freeb(bb);
281                         if(n < 2){
282                                 syslog(0, "ppp", ": unthwack: short or corrupted packet %d seq=%ld", n, seq);
283                                 netlog("unthwack: short or corrupted packet n=%d seq=%ld: %s\n", n, seq, uncs->ut.err);
284                                 freeb(b);
285
286                                 r = alloclcp(Lresetreq, ++uncs->resetid, 4, &m);
287                                 hnputs(m->len, 4);
288                                 *reply = r;
289                                 uncs->active = 0;
290                                 return nil;
291                         }
292                         b->wptr += n;
293                 }else{
294                         unthwackadd(&uncs->ut, bb->rptr, BLEN(bb), seq & ThwSeqMask);
295                         b = bb;
296                 }
297
298                 /*
299                  * update ack state
300                  */
301                 mseq = unthwackstate(&uncs->ut, &mask);
302                 qlock(&uncs->ackl);
303                 uncs->ackseq = mseq;
304                 uncs->ackmask = mask;
305                 uncs->doack = 1;
306                 qunlock(&uncs->ackl);
307         }
308
309         /*
310          * grab the compressed protocol field
311          */
312         proto = *b->rptr++;
313         if((proto & 1) == 0)
314                 proto = (proto << 8) | *b->rptr++;
315         *protop = proto;
316
317         /*
318          * decode the ack, and forward to compressor
319          */
320         if(seq & ThwAcked){
321                 if(ppp->ctype == &cthwack){
322                         cs = ppp->cstate;
323                         mseq = (b->rptr[0]<<16) | (b->rptr[1]<<8) | b->rptr[2];
324                         mask = b->rptr[3];
325                         thwackack(&cs->th, mseq, mask);
326                 }
327                 b->rptr += 4;
328         }
329         return b;
330 }