5 April 2009

VPN-сервер на Linux — решение проблемы с MPPE и клиентами, не поддерживающими шифрование данных

Configuring Linux
Так уж исторически сложилось, что связка pptpd + pppd — довольно популярное решение для раздачи интернета в локальных сетях, во многом благодаря наличию клиента pptp в windows начиная с 98 «из коробки». Более того, этот клиент поддерживает протокол шифрования MPPE который начиная с windows 2000 включен для новых соединений по умолчанию.
pppd радостно идет нам навстречу, также поддерживая этот протокол, но делает это весьма своеобразно:


цитата из README.MPPE:

if you turn it on, all other compression options are disabled and MPPE *must* be negotiated successfully in both directions (CCP is unidirectional) or the link will be disconnected. I think this is reasonable since, if you want encryption, you want encryption. That is, I am not convinced that optional encryption is useful

На практике сие означает, что включив поддержку mppe (опциями +mppe или require-mppe) вы не сможете обслужить подключения клиентов, не поддерживающих этого протокола. Разработчики pppd считают это вполне себе правильным поведением, заявляя что «опциональное шифрование народу не нужно»

Но что делать тем, у которого помимо виндовых клиентов есть еще и немало подключений дешевых аппаратных роутеров, которые попросту не поддерживают MPPE? Отключать шифрование для всех? Или убеждать клиентов что им нужен роутер в 5 раз дороже?

Прежде чем ударяться в такие крайности, было принято решение починить эту «фичу» и за пару часиков вдумчивого изучения исходников pppd был рожден такой вот патч:
(тестировался на ppp-2.4.4rel-10.1 из debian lenny)

diff -ruNp ppp-2.4.4.orig/pppd/auth.c ppp-2.4.4/pppd/auth.c
--- ppp-2.4.4.orig/pppd/auth.c 2006-06-18 14:26:00.000000000 +0300
+++ ppp-2.4.4/pppd/auth.c 2008-02-13 18:24:08.000000000 +0200
@@ -875,7 +875,7 @@ start_networks(unit)
*/
ecp_required = ecp_gotoptions[unit].required;
mppe_required = ccp_gotoptions[unit].mppe;
- if (!ecp_required && !mppe_required)
+ if (!ecp_required && (!mppe_required || allow_mppe_fallback))
continue_networks(unit);
}
diff -ruNp ppp-2.4.4.orig/pppd/ccp.c ppp-2.4.4/pppd/ccp.c
--- ppp-2.4.4.orig/pppd/ccp.c 2005-07-09 03:23:05.000000000 +0300
+++ ppp-2.4.4/pppd/ccp.c 2008-02-13 18:31:16.000000000 +0200
@@ -156,6 +156,11 @@ static option_t ccp_option_list[] = {
"allow MPPE stateful mode", OPT_PRIO },
{ "nomppe-stateful", o_bool, &refuse_mppe_stateful,
"disallow MPPE stateful mode", OPT_PRIO | 1 },
+
+ /* allow falling back to unencrypted connection mode */
+ { "mppe-optional", o_bool, &allow_mppe_fallback,
+ "allow falling back to unencryped connection mode", OPT_PRIO | 1 },
+
#endif /* MPPE */
{ NULL }
@@ -516,9 +521,14 @@ ccp_protrej(unit)
#ifdef MPPE
if (ccp_gotoptions[unit].mppe) {
- error("MPPE required but peer negotiation failed");
- lcp_close(unit, "MPPE required but peer negotiation failed");
- }
+ if(!allow_mppe_fallback) {
+ error("MPPE required but peer negotiation failed");
+ lcp_close(unit, "MPPE required but peer negotiation failed");
+ } else {
+ error("MPPE required but peer negotiation failed.");
+ error("Falling back and disabling MPPE");
+ }
+ }
#endif
}
@@ -1004,11 +1014,17 @@ ccp_rejci(f, p, len)
#ifdef MPPE
if (go->mppe && len >= CILEN_MPPE
&& p[0] == CI_MPPE && p[1] == CILEN_MPPE) {
- error("MPPE required but peer refused");
- lcp_close(f->unit, "MPPE required but peer refused");
- p += CILEN_MPPE;
- len -= CILEN_MPPE;
+ if(!allow_mppe_fallback) {
+ error("MPPE required but peer refused. Closing LCP");
+ lcp_close(f->unit, "MPPE required but peer refused");
+ } else {
+ try.mppe = 0;
+ error("MPPE required but peer refused.");
+ error("Falling back and disabling MPPE");
+ }
}
+ p += CILEN_MPPE;
+ len -= CILEN_MPPE;
#endif
if (go->deflate_correct && len >= CILEN_DEFLATE
&& p[0] == CI_DEFLATE && p[1] == CILEN_DEFLATE) {
diff -ruNp ppp-2.4.4.orig/pppd/ccp.h ppp-2.4.4/pppd/ccp.h
--- ppp-2.4.4.orig/pppd/ccp.h 2004-11-04 12:02:26.000000000 +0200
+++ ppp-2.4.4/pppd/ccp.h 2008-02-13 19:03:43.000000000 +0200
@@ -43,6 +43,8 @@ typedef struct ccp_options {
short method; /* code for chosen compression method */
} ccp_options;
+bool allow_mppe_fallback;
+
extern fsm ccp_fsm[];
extern ccp_options ccp_wantoptions[];
extern ccp_options ccp_gotoptions[];
@@ -50,3 +52,5 @@ extern ccp_options ccp_allowoptions[];
extern ccp_options ccp_hisoptions[];
extern struct protent ccp_protent;
+
+
diff -ruNp ppp-2.4.4.orig/pppd/pppd.8 ppp-2.4.4/pppd/pppd.8
--- ppp-2.4.4.orig/pppd/pppd.8 2006-06-16 03:01:23.000000000 +0300
+++ ppp-2.4.4/pppd/pppd.8 2008-02-13 18:41:31.000000000 +0200
@@ -770,6 +770,9 @@ available under Linux.
.B nomppe
Disables MPPE (Microsoft Point to Point Encryption). This is the default.
.TP
+.B mppe-optional
+Makes MPPE optional, allowing both MPPE and non-MPPE clients
+.TP
.B nomppe\-40
Disable 40-bit encryption with MPPE.
.TP


После наложения и пересборки — в конфиге pppd пишем

require-mppe
mppe-optional

и наблюдаем нормальную работу vpn как с включенным шифрованием на стороне клиента, так и без.

уже собранные пакеты для debian lenny (возможно, подойдут и для ubuntu) могу выложить по запросу
Tags:linuxpptppppdmpperequiredoptionalpatch
Hubs: Configuring Linux
+21
15k 37
Comments 19