xref: /openbsd-src/usr.sbin/npppd/npppd/ccp.c (revision bcb159dabaec0c8e55089ec3f1ffbd233e95b4b7)
1 /*	$OpenBSD: ccp.c,v 1.8 2019/02/27 04:52:19 denis Exp $ */
2 
3 /*-
4  * Copyright (c) 2009 Internet Initiative Japan Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 /**@file
29  * This file provides functions for CCP (Compression Control Protocol).
30  * MPPE is supported as a CCP option.
31  * $Id: ccp.c,v 1.8 2019/02/27 04:52:19 denis Exp $
32  */
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/time.h>
36 #include <net/if_dl.h>
37 #include <netinet/in.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <syslog.h>
41 #include <string.h>
42 #include <event.h>
43 
44 #include "npppd.h"
45 #include "fsm.h"
46 
47 #ifdef	CCP_DEBUG
48 #define	CCPDEBUG(x)	fsm_log(x)
49 #define	CCP_ASSERT(x)	ASSERT(x)
50 #else
51 #define	CCPDEBUG(x)
52 #define	CCP_ASSERT(x)
53 #endif
54 
55 static int   ccp_reqci (fsm *, u_char *, int *, int);
56 static void  ccp_open (fsm *);
57 static void  ccp_close (fsm *);
58 static void  ccp_start (fsm *);
59 static void  ccp_stop (fsm *);
60 static void  ccp_resetci (fsm *);
61 static int   ccp_cilen (fsm *);
62 static void  ccp_addci (fsm *, u_char *, int *);
63 static int   ccp_ackci (fsm *, u_char *, int);
64 static int   ccp_rejci (fsm *, u_char *, int);
65 static int   ccp_nakci (fsm *, u_char *, int);
66 static int   ccp_nackackci (fsm *, u_char *, int, int, int);
67 static int   ccp_ext (fsm *, int, int, u_char *, int);
68 
69 static struct fsm_callbacks ccp_callbacks = {
70 	.cilen		= ccp_cilen,
71 	.resetci	= ccp_resetci,
72 	.addci		= ccp_addci,
73 	.ackci		= ccp_ackci,
74 	.nakci		= ccp_nakci,
75 	.rejci		= ccp_rejci,
76 	.reqci		= ccp_reqci,
77 	.up		= ccp_open,
78 	.down		= ccp_close,
79 	.starting	= ccp_start,
80 	.finished	= ccp_stop,
81 	.extcode	= ccp_ext,
82 	.proto_name	= "ccp",
83 };
84 
85 /** Initialize the context for ccp */
86 void
ccp_init(ccp * _this,npppd_ppp * ppp)87 ccp_init(ccp *_this, npppd_ppp *ppp)
88 {
89 	struct tunnconf *conf;
90 
91 	memset(_this, 0, sizeof(ccp));
92 
93 	_this->ppp = ppp;
94 	_this->fsm.callbacks = &ccp_callbacks;
95 	_this->fsm.protocol = PPP_PROTO_NCP | NCP_CCP;
96 	_this->fsm.ppp = ppp;
97 
98 	fsm_init(&_this->fsm);
99 
100 	conf = ppp_get_tunnconf(ppp);
101 	PPP_FSM_CONFIG(&_this->fsm, timeouttime, conf->ccp_timeout);
102 	PPP_FSM_CONFIG(&_this->fsm, maxconfreqtransmits,
103 	    conf->ccp_max_configure);
104 	PPP_FSM_CONFIG(&_this->fsm, maxtermtransmits,
105 	    conf->ccp_max_terminate);
106 	PPP_FSM_CONFIG(&_this->fsm, maxnakloops,
107 	    conf->ccp_max_nak_loop);
108 }
109 
110 /** Request Command Interpreter */
111 static int
ccp_reqci(fsm * f,u_char * pktp,int * lpktp,int reject_if_disagree)112 ccp_reqci(fsm *f, u_char *pktp, int *lpktp, int reject_if_disagree)
113 {
114 	int type, len, rcode, lrej, lnak;
115 	u_char *rejbuf, *nakbuf, *nakbuf0, *pktp0;
116 #ifdef USE_NPPPD_MPPE
117 	uint32_t peer_bits, our_bits;
118 #endif
119 	npppd_ppp *ppp;
120 
121 	ppp = f->ppp;
122 
123 	rejbuf = NULL;
124 	rcode = CONFACK;
125 	pktp0 = pktp;
126 	lrej = 0;
127 	lnak = 0;
128 
129 	if ((rejbuf = malloc(*lpktp)) == NULL) {
130 		return rcode;
131 	}
132 	if ((nakbuf0 = malloc(*lpktp)) == NULL) {
133 		free(rejbuf);
134 		return rcode;
135 	}
136 	nakbuf = nakbuf0;
137 #define	remlen()	(*lpktp - (pktp - pktp0))
138 
139 	while (remlen() >= 2) {
140 		GETCHAR(type, pktp);
141 		GETCHAR(len, pktp);
142 		if (len <= 0 || remlen() + 2 < len)
143 			goto fail;
144 
145 		switch (type) {
146 #ifdef USE_NPPPD_MPPE
147 		case CCP_MPPE:
148 			if (len < 6)
149 				goto fail;
150 
151 			if (ppp->mppe.enabled == 0)
152 				goto reject;
153 			GETLONG(peer_bits, pktp);
154 			our_bits = mppe_create_our_bits(&ppp->mppe, peer_bits);
155 			if (our_bits != peer_bits) {
156 				if (reject_if_disagree) {
157 					pktp -= 4;
158 					goto reject;
159 				}
160 				if (lrej > 0) {
161 				/* don't nak because we are doing rej */
162 				} else {
163 					PUTCHAR(type, nakbuf);
164 					PUTCHAR(6, nakbuf);
165 					PUTLONG(our_bits, nakbuf);
166 					rcode = CONFNAK;
167 				}
168 			} else
169 				ppp->ccp.mppe_p_bits = our_bits;
170 			break;
171 reject:
172 #endif
173 		default:
174 			pktp -= 2;
175 			memcpy(rejbuf + lrej, pktp, len);
176 			lrej += len;
177 			pktp += len;
178 			rcode = CONFREJ;
179 		}
180 		continue;
181 	}
182 fail:
183 	switch (rcode) {
184 	case CONFREJ:
185 		memcpy(pktp0, rejbuf, lrej);
186 		*lpktp = lrej;
187 		break;
188 	case CONFNAK:
189 		len = nakbuf - nakbuf0;
190 		memcpy(pktp0, nakbuf0, len);
191 		*lpktp = len;
192 		break;
193 	}
194 	free(rejbuf);
195 	free(nakbuf0);
196 
197 	return rcode;
198 #undef	remlen
199 }
200 
201 static void
ccp_open(fsm * f)202 ccp_open(fsm *f)
203 {
204 	ppp_ccp_opened(f->ppp);
205 }
206 
207 static void
ccp_close(fsm * f)208 ccp_close(fsm *f)
209 {
210 }
211 
212 static void
ccp_start(fsm * f)213 ccp_start(fsm *f)
214 {
215 }
216 
217 static void
ccp_stop(fsm * f)218 ccp_stop(fsm *f)
219 {
220 #ifdef USE_NPPPD_MPPE
221 	fsm_log(f, LOG_INFO, "CCP is stopped");
222 	ppp_ccp_stopped(f->ppp);
223 #endif
224 }
225 
226 static void
ccp_resetci(fsm * f)227 ccp_resetci(fsm *f)
228 {
229 #ifdef	USE_NPPPD_MPPE
230 	if (f->ppp->mppe_started == 0)
231 		f->ppp->ccp.mppe_o_bits =
232 		    mppe_create_our_bits(&f->ppp->mppe, 0);
233 	/* don't reset if the ccp is started. */
234 #endif
235 }
236 
237 static int
ccp_cilen(fsm * f)238 ccp_cilen(fsm *f)
239 {
240 	return f->ppp->mru;
241 }
242 
243 /** Create a Confugre-Request */
244 static void
ccp_addci(fsm * f,u_char * pktp,int * lpktp)245 ccp_addci(fsm *f, u_char *pktp, int *lpktp)
246 {
247 	u_char *pktp0;
248 
249 	pktp0 = pktp;
250 
251 	if (f->ppp->ccp.mppe_rej == 0) {
252 		PUTCHAR(CCP_MPPE, pktp);
253 		PUTCHAR(6, pktp);
254 		PUTLONG(f->ppp->ccp.mppe_o_bits, pktp);
255 
256 		*lpktp = pktp - pktp0;
257 	} else
258 		*lpktp = 0;
259 }
260 
261 static int
ccp_ackci(fsm * f,u_char * pktp,int lpkt)262 ccp_ackci(fsm *f, u_char *pktp, int lpkt)
263 {
264 	return ccp_nackackci(f, pktp, lpkt, 0, 0);
265 }
266 
267 
268 static int
ccp_nakci(fsm * f,u_char * pktp,int lpkt)269 ccp_nakci(fsm *f, u_char *pktp, int lpkt)
270 {
271 	return ccp_nackackci(f, pktp, lpkt, 1, 0);
272 }
273 
274 static int
ccp_rejci(fsm * f,u_char * pktp,int lpkt)275 ccp_rejci(fsm *f, u_char *pktp, int lpkt)
276 {
277 	return ccp_nackackci(f, pktp, lpkt, 0, 1);
278 }
279 
280 static int
ccp_nackackci(fsm * f,u_char * pktp,int lpkt,int is_nak,int is_rej)281 ccp_nackackci(fsm *f, u_char *pktp, int lpkt, int is_nak, int is_rej)
282 {
283 	int type, len;
284 	u_char *pktp0;
285 #ifdef	USE_NPPPD_MPPE
286 	uint32_t peer_bits, our_bits;
287 #endif
288 	npppd_ppp *ppp;
289 
290 	ppp = f->ppp;
291 
292 	pktp0 = pktp;
293 
294 #define	remlen()	(lpkt - (pktp - pktp0))
295 	while (remlen() >= 2) {
296 		GETCHAR(type, pktp);
297 		GETCHAR(len, pktp);
298 		if (len <= 0 || remlen() + 2 < len)
299 			goto fail;
300 
301 		switch (type) {
302 #ifdef USE_NPPPD_MPPE
303 		case CCP_MPPE:
304 			if (len < 6)
305 				goto fail;
306 			if (is_rej) {
307 				f->ppp->ccp.mppe_rej = 1;
308 				return 1;
309 			}
310 			if (ppp->mppe_started != 0) {
311 				/* resend silently */
312 				return 1;
313 			}
314 			GETLONG(peer_bits, pktp);
315 			/*
316 			 * With Yamaha RTX-1000 that is configured as
317 			 * "ppp ccp mppe-any",
318 			 *
319 			 *	npppd ConfReq (40,56,128) => RTX 1000
320 			 *	npppd <= (40,128) ConfNAK    RTX 1000
321 			 *	npppd ConfReq (40,56,128) => RTX 1000
322 			 *	npppd <= (40,128) ConfNAK    RTX 1000
323 			 *
324 			 * both peers never decide the final bits.  We insist
325 			 * the longest bit if our request is nacked.
326 			 */
327 			our_bits = mppe_create_our_bits(&ppp->mppe, peer_bits);
328 			if (peer_bits == our_bits || is_nak)
329 				ppp->ccp.mppe_o_bits = our_bits;
330 
331 			break;
332 #endif
333 		default:
334 			goto fail;
335 		}
336 	}
337 	return 1;
338 fail:
339 	return 0;
340 }
341 
342 #define	RESET_REQ	0x0e
343 #define	RESET_ACK	0x0f
344 
345 static int
ccp_ext(fsm * f,int code,int id,u_char * pktp,int lpktp)346 ccp_ext(fsm *f, int code, int id, u_char *pktp, int lpktp)
347 {
348 	switch (code) {
349 	case RESET_REQ:
350 		fsm_log(f, LOG_DEBUG, "Received ResetReq %d", id);
351 #ifdef USE_NPPPD_MPPE
352 		mppe_recv_ccp_reset(&f->ppp->mppe);
353 #endif
354 		/*
355 		 * RFC 3078 says MPPE can be synchronized without Reset-Ack,
356 		 * but it doesn't tell about necessity of Reset-Ack.  But
357 		 * in fact, windows peer will complain Reset-Ack with
358 		 * Code-Reject.  So we don't send Reset-Ack.
359 		 */
360 		return 1;
361 	case RESET_ACK:
362 		fsm_log(f, LOG_DEBUG, "Received ResetAck %d", id);
363 		return 1;
364 	}
365 	return 0;
366 }
367