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)では動作しませんでした。