CVSで手に入るpppdの最新ベータではMS-CHAPv2とMPPEが取り込まれているので、 これを使います。 詳しい手順は Nobさんのページ を参照してください。
pppdの2.4.2beta(最新版)のソースをcvsで持ってきておきます。
Nobさんのページ参照。
# apt-get install cvs Where are your repositories: (削除) Should the CVS pserver be enabled: No % cvs -z5 -d :pserver:cvs@pserver.samba.org:/cvsroot co pppカーネルソースを持ってきます。自分の使っているカーネルか、より新しいのを。 カーネルの再コンパイルの仕方はあちこちで解説があると思います。
# apt-cache search kernel-source ... # apt-get install kernel-source-2.4.18 # apt-get install kernel-package # apt-get install libncurses5-dev % tar xjf /usr/src/kernel-source-2.4.18.tar.bz2 % cd kernel-source-2.4.18 % make menuconfig 最初にLoad an Alternate Configuration File(下のほうにあります)で 使用中のカーネルのconfig (/boot/config-2.4.xx など) を読み込みます。 保存して(.configに書かれます)、終了。pppに含まれるカーネルパッチを当てて、コンパイル。
% cd ~/ppp/linux/mppe % sh mppeinstall.sh ~/kernel-source-2.4.18 Is this a 2.2 kernel or 2.4 kernel: 2.4 ... % cd ~/kernel-source-2.4.18 % make menuconfig Network DeviceにあるPPP MPPEを有効にします。 % make-kpkg clean % make-kpkg kernel_image % cd .. # mv /lib/modules/2.4.18 /lib/modules/2.4.18.orig …使用中のカーネルが同じ名前の場合 # dpkg -i kernel-image-2.4.18_xx.deb # reboot
% cd ~/ppp/pppd % vi chap.c ... (関数ChapReceiveResponse 86行目あたり) /* We do not want to leak info about the chap result. */ code = CHAP_FAILURE; /* XXX exit value will be "wrong" */ warn("calling number %q is not authorized", remote_number); } } } else if(ChapMSLdap(explicit_remote? remote_name: rhostname, remmd, (int)remmd_len, cstate)) { code= CHAP_SUCCESS; } else { if (!get_secret(cstate->unit, (explicit_remote? remote_name: rhostname), cstate->chal_name, secret, &secret_len, 1)) { warn("No CHAP secret found for authenticating %q", rhostname); } else { % vi chap_ms.c ... #include <ldap.h> int ChapMSLdap(user, remmd, remmd_len, cstate) char *user; u_char *remmd; int remmd_len; chap_state *cstate; { const char b64[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; char pass[6+44+1]; int i; char *p; u_char *c; char* msg; FILE *fp; char buf[MAX_NT_PASSWORD]; char host[64]; int port; char bind[200]; LDAP *ld; char *attr[]= { "userPassword", 0}; LDAPMessage* lm; char *addr= "*"; struct wordlist *addrs; if(cstate->chal_type!=CHAP_MICROSOFT&&cstate->chal_type!=CHAP_MICROSOFT_V2) return 0; if (remmd_len != MS_CHAP_RESPONSE_LEN) return 0; memcpy(pass, "{NTLM}", 6); p= pass+6; c= remmd+24; if(cstate->chal_type==CHAP_MICROSOFT&&!((MS_ChapResponse*)remmd)->UseNT[0]) c= remmd; for(i= 0; i<32; i+=3) { int e0, e1, e2; if(i==24) { if(cstate->chal_type==CHAP_MICROSOFT_V2) { ChallengeHash(((MS_Chap2Response*)remmd)->PeerChallenge ,cstate->challenge, user, buf); c= buf; } else { c= cstate->challenge; }; }; e0= c[0]; e1= i+1<32?c[1]:0; e2= i+2<32?c[2]:0; *p++= b64[e0>>2]; *p++= b64[e0<<4&0x30|e1>>4]; *p++= i+1<32?b64[e1<<2&0x3c|e2>>6]:'='; *p++= i+2<32?b64[e2&0x3f]:'='; c+= 3; }; *p= 0; if(plogin(user, pass, &msg)!=2) return 0; /*login complete*/ port= LDAP_PORT; strcpy(host, "127.0.0.1"); strcpy(bind, ""); fp= fopen("/etc/pam_ldap.conf", "r"); if(fp) { while(fgets(buf, sizeof(buf), fp)) { char *p, *pk, *pv; p= buf; while(*p>0&&*p<=' ') p++; pk= p; while(!(*p>=0&&*p<=' ')) p++; while(*p>0&&*p<=' ') *p++= 0; pv= p; while(!(*p>=0&&*p<' '&&*p!='\t')) p++; *p= 0; if(strcasecmp(pk, "host")==0) { strncpy(host, pv, sizeof(host)-1); host[sizeof(host)-1]= 0; } else if(strcasecmp(pk, "port")==0) { port= atoi(pv); } else if(strcasecmp(pk, "bindpppuser")==0) { snprintf(bind, sizeof(bind), pv, user); } else if(strcasecmp(pk, "base")==0&&bind[0]==0) { snprintf(bind, sizeof(bind), "cn=%s,ou=People,%s", user, pv); }; }; fclose(fp); }; buf[0]= 0; if(ld= ldap_init(host, port)) { if(ldap_simple_bind_s(ld, bind, pass)==LDAP_SUCCESS && ldap_search_s(ld, bind, LDAP_SCOPE_BASE, 0, attr, 0, &lm)==LDAP_SUCCESS) { LDAPMessage* le= ldap_first_entry(ld, lm); if(le) { char** val= ldap_get_values(ld, le, attr[0]); if(val) strncpy(buf, val[0], sizeof(buf)), buf[sizeof(buf)-1]= 0; }; ldap_msgfree(lm); }; ldap_unbind(ld); }; if(buf[0]==0) warn("Cannot get userPassword from LDAP for %s", bind); addrs= (struct wordlist*)malloc(sizeof(struct wordlist)+strlen(addr)+1); addrs->next= 0; addrs->word= (char*)(addrs+1); strcpy(addrs->word, addr); set_allowed_addrs(cstate->unit, addrs, 0); free(addrs); if(cstate->chal_type==CHAP_MICROSOFT_V2) { GenerateAuthenticatorResponse(buf, strlen(buf) , ((MS_Chap2Response*)remmd)->NTResp , ((MS_Chap2Response*)remmd)->PeerChallenge , cstate->challenge, user, cstate->saresponse); #ifdef MPPE SetMasterKeys(buf, strlen(buf), ((MS_Chap2Response*)remmd)->NTResp , MS_CHAP2_AUTHENTICATOR); mppe_keys_set = 1; #endif } else { #ifdef MPPE Set_Start_Key(cstate->challenge, buf, strlen(buf)); mppe_keys_set = 1; #endif }; return 1; //success }; ... % vi auth.c ... /*static*/ int plogin __P((char *, char *, char **)); /*static*/ void set_allowed_addrs __P((int, struct wordlist *, struct wordlist *)); ... /*static*/ int plogin(user, passwd, msg) char *user; char *passwd; char **msg; { ... static void plogout() { char *tty; #ifdef USE_PAM int pam_error; if (pamh != NULL) { pam_error = pam_close_session (pamh, PAM_SILENT); pam_end (pamh, pam_error); pamh = NULL; } /* Apparently the pam stuff does closelog(). */ reopen_log(); #endif // char *tty; tty = devnam; if (strncmp(tty, "/dev/", 5) == 0) tty += 5; logwtmp(tty, "", ""); /* Wipe out utmp logout entry */ //#endif /* ! USE_PAM */ logged_in = 0; } ... /*static*/ void set_allowed_addrs(unit, addrs, opts) int unit; struct wordlist *addrs; struct wordlist *opts; { ...chap.cの認証でChapMSLdapを呼び出すようにして、 ChapMSLdap内部でplogin経由でpam-ldapにNTLMチャレンジ・レスポンスを渡して 認証させます。 MPPEの暗号化やMS-CHAPv2では、生パスワードが必要となるため、 ログインに成功した場合さらにパスワードを取得します。 この時のldapへの接続方法は/etc/pam_ldap.confの内容に従い、 base が dc=hoehoe,dc=japan の場合は cn=moke,ou=People,dc=hoehoe,dc=japan になります。もし変更したい場合は /etc/pam_ldap.conf に
bindpppuser cn=%s,ou=People,dc=soumu,dc=hoehoe,dc=japanのように設定してください。
pamを有効にしてコンパイルし、/usr/sbin/pppdを入れ替えます。 別の名前でpptpd専用なpppdを置いてpptpdからそれを指定できればいいんですが、 pptpdが起動するpppdは/usr/sbin/pppd固定なようですので、 apt-get upgradeでpppdが元に戻らないようにholdしておく必要もあります。
% vi pppd/Makefile.linux ... HAS_SHADOW=y USE_PAM=y ... INSTALL= install -o root LIBS += -lldap ... # apt-get install libldap2-dev % cd .. % ./configure % make % cd pppd % strip pppd # mv /usr/sbin/pppd /usr/sbin/pppd.orig # cp pppd /usr/sbin # chmod ... で元と同じに。 # dselect ...でpppをhold-stateにしておきます。
# apt-get install pptpd # vi /etc/ppp/pptpd-options ... auth require-mschap-v2 #require-mschap 両方指定してもv2のみ有効です。 require-mppe mppe-stateful ... # vi /etc/pam.d/ppp auth sufficient pam_ldap.so account sufficient pam_ldap.so session sufficient pam_ldap.so auth required pam_nologin.so use_first_pass auth required pam_unix.so nullok use_first_pass account required pam_unix.so session required pam_unix.so # vi /etc/ppp/chap-secrets (最低1行の有効なダミー項目) # vi /etc/pam_ldap.conf (必要ならbindpppuserを設定)/etc/ppp/chap-secretsファイルに最低でも1行の項目がなぜか必要です。
12.34.56.0 : ネットワークの場合、
12.34.56.1 : Linuxボックスのアドレス
12.34.56.7 : ブロードキャスト
iptables -t nat -A PREROUTING -d 12.34.56.1 -p tcp --dport 1723 -j DROP iptables -t nat -A PREROUTING -d 12.34.56.7 -p tcp --dport 1723 -j DNAT --to 12.34.56.1 iptables -t nat -A POSTROUTING -s 12.34.56.1 -p gre -j SNAT --to 12.34.56.7のように設定すると、pptpサーバを12.34.56.7で公開できます。 (必要に応じてインターフェースの指定やその他のフィルタリングも忘れずに。) GREプロトコルはサーバ側から張るので上記のSNATの指定だけが必要となります。 また、カーネルに対するpptp natパッチがありますが、 これは内側から発信された複数のpptpコネクションをmasquaradeするためのもので、 当てても当てなくても上記の指定が必要となります。 conntrack_ftpみたいに対応するgreを自動で開けたりしてくれるといいんですが、 現段階(2.4.19_rev1)では動作しませんでした。