校园网802.1X的Linux联网客户端是Linuxidc转载自天空得博文,自己也看不懂,只为以后学习之用做摘记。
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>
#include<stdint.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<linux/if.h>
#include<linux/if_ether.h>
#include<linux/sockios.h>
#include<netdb.h>
#include<arpa/inet.h>
#include<pcap.h>
#include<signal.h>
#include<fcntl.h>
#include<time.h>
//#include<ncurses.h>//注意此头文件位置
#include<termios.h>
#include”md5.c”
#define Eap_Pack_Len 67
#define EAP_REQUEST_LEN 60
#define EAP_PWD_ERR_LEN 146
#define EAP_NAME_ERR_LEN 63
#define USER_NAME_LEN 40
#define USER_PWD_LEN 50
//#define EAP_MAC_ERR_LEN
const char *lock_file=”/tmp/802.1x_EAP_Client_lock-file_DW”;//锁文件
u_char mac_client[6],base[32],EAP_Pack[Eap_Pack_Len];
char user_name[USER_NAME_LEN],user_pwd[USER_PWD_LEN];//EAP_Pack为要发送给服务噐的数据
u_char mac_server[6]={0x01,0x80,0xc2,0x00,0x00,0x03};
struct pcap_pkthdr *header;
u_char *pkt_data;
unsigned int ID;
struct termios old_set;
const char *version=”Version:1.3_20090815″;
const char VERSION_802_1X[16]=”CH V2.40-F0316″;
const char KEY[]=”HuaWei3COM1X”;
struct ifreq req;
char network_name[30];
pcap_t *fp;
struct sigaction act;
int sockfd;
int file_desc,flag_sig_pro=1;
int get_user_name()
{
int j=0;
char swap;
printf(“input name:”);
fflush(stdout);
while((swap=user_name[j]=getchar())!=’\n’&&j<USER_NAME_LEN-1)
j++;
while(swap!=’\n’)
swap=getchar();
if(j==0)
return 1;
user_name[j]=’\0′;
return 0;
}
int get_user_pwd()
{
int j=0;
char swap;
printf(“input password:”);
fflush(stdout);
while((swap=user_pwd[j]=getchar())!=’\n’&&j<USER_PWD_LEN-1)
j++;
while(swap!=’\n’)
swap=getchar();
if(j==0)
return 1;
user_pwd[j]=’\0′;
return 0;
}
int get_mac_client()
{
sockfd=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
if(sockfd<0)
return 1;
if(-1==ioctl(sockfd,SIOCGIFHWADDR,&req))
{
close(sockfd);
return 1;
}
memcpy(mac_client,req.ifr_hwaddr.sa_data,6);
//close(sockfd);
return 0;
}
//md5
int get_md5_digest(u_char EAP_Pack[],const char *str,unsigned int len)//对字符串进行16位md5加密
{
md5_state_t state;
md5_byte_t digest[16];
md5_init(&state);
md5_append(&state,(const md5_byte_t *)str,len);
md5_finish(&state,digest);
memcpy(EAP_Pack,digest,16);
return 0;
}
int Fill_Eapol_Start(u_char EAP_Pack[])
{
EAP_Pack[15]=0x01;
return 0;
}
int Fill_EAP_Name(u_char EAP_Pack[])
{
EAP_Pack[15]=0x00;EAP_Pack[16]=0x00;EAP_Pack[17]=0x15;EAP_Pack[18]=0x02;
EAP_Pack[19]=ID;EAP_Pack[20]=0x00;EAP_Pack[21]=0x15;EAP_Pack[22]=0x01;
EAP_Pack[23]=0x15;EAP_Pack[24]=0x04;
memset(EAP_Pack+25,0×00,Eap_Pack_Len-25);
memcpy(EAP_Pack+29,user_name,strlen(user_name));
return 0;
}
int Fill_Eap_PWD(u_char EAP_Pack[],u_char pkt_data[])
{
int pwdlen=strlen(user_pwd);
u_char *pwdstring=(u_char *)malloc(1+pwdlen+16);
pwdstring[0]=*(pkt_data+0x13);
memcpy(pwdstring+1,user_pwd,pwdlen);
memcpy(pwdstring+1+pwdlen,pkt_data+0x18,16);
get_md5_digest(EAP_Pack+24,pwdstring,1+pwdlen+16);
EAP_Pack[15]=0x00;EAP_Pack[16]=0x00;EAP_Pack[17]=0x20;EAP_Pack[18]=0x02;
EAP_Pack[19]=ID;EAP_Pack[20]=0x00;EAP_Pack[21]=0x20;EAP_Pack[22]=0x04;
EAP_Pack[23]=0x10;
memcpy(EAP_Pack+40,user_name,strlen(user_name));
free(pwdstring);
return 0;
}
void XOR(uint8_t data[], unsigned dlen, const char key[], unsigned klen)
{
unsigned int i,j;
//先按正序处理一遍//
for (i=0; i<dlen; i++)
data[i] ^= key[i%klen];
// 再按倒序处理第二遍
for (i=dlen-1,j=0; j<dlen; i–,j++)
data[i] ^= key[j%klen];
}
int Fill_Client_Version(int i)
{
uint32_t random;
char RandomKey[8+1];
random = (uint32_t) time(NULL); // 注:可以选任意32位整数
sprintf(RandomKey, “%08x”, random);// 生成RandomKey[]字符串第一轮异或运算,以RandomKey为密钥加密16字节
memcpy(EAP_Pack+i, VERSION_802_1X, sizeof(VERSION_802_1X));
XOR(EAP_Pack+i, 16, RandomKey, strlen(RandomKey));
// 此16字节加上4字节的random,组成总计20字节
random = htonl(random); // (需调整为网络字节序)
memcpy(EAP_Pack+i+16, &random, 4);
// 第二轮异或运算,以KEY为密钥加密前面生成的20字节
XOR(EAP_Pack+i, 20, KEY, strlen(KEY));
return 0;
}
int Fill_EAP_Notification()
{
EAP_Pack[15]=0x00;EAP_Pack[16]=0x00;EAP_Pack[17]=0x31;EAP_Pack[18]=0x02;
EAP_Pack[19]=ID;EAP_Pack[20]=0x00;EAP_Pack[21]=0x31;EAP_Pack[22]=0x02;
EAP_Pack[23]=0x01;EAP_Pack[24]=0x16;
Fill_Client_Version(25);
EAP_Pack[45]=0x02;EAP_Pack[46]=0x16;EAP_Pack[47]=0x3a;EAP_Pack[48]=0x71;
EAP_Pack[49]=0x38;EAP_Pack[50]=0x01;EAP_Pack[51]=0x0b;EAP_Pack[52]=0x3b;
EAP_Pack[53]=0x7e;EAP_Pack[54]=0x3d;EAP_Pack[55]=0x26;EAP_Pack[56]=0x7c;
EAP_Pack[57]=0x7c;EAP_Pack[58]=0x17;EAP_Pack[59]=0x0b;EAP_Pack[60]=0x46;
EAP_Pack[61]=0x08;EAP_Pack[62]=0x32;EAP_Pack[63]=0x32;EAP_Pack[64]=0x08;
EAP_Pack[65]=0x46;EAP_Pack[66]=0x0b;
return 0;
}
int Fill_EAP_After_Suc()
{
char ip[20];
int swap=0,k=0,j=0,len;
EAP_Pack[19]=ID;
ioctl(sockfd,SIOCGIFADDR,&req);
sprintf(ip,”%s”,inet_ntoa(((struct sockaddr_in *)&req.ifr_ifru.ifru_addr)->sin_addr));
len=strlen(ip);
for(j=0;j<len;j++)
{
if(‘.’==ip[j])
{
EAP_Pack[25+k]=swap;
swap=0;
k++;
continue;
}
swap=swap*10+ip[j]-‘0’;
}
EAP_Pack[28]=swap;
return 0;
}
int Send_Eapol_Logoff()
{
EAP_Pack[15]=0x02;
memset(EAP_Pack+16,0,44);
if(pcap_sendpacket(fp,EAP_Pack,60) != 0)
return 1;
return 0;
}
int Send_Eapol_Start(pcap_t *fp,u_char EAP_Pack[])
{
Fill_Eapol_Start(EAP_Pack);
if(pcap_sendpacket(fp,EAP_Pack,60) != 0)
return 1;
return 0;
}
int Send_Eap_Name(pcap_t *fp,u_char EAP_Pack[])
{
Fill_EAP_Name(EAP_Pack);
if(pcap_sendpacket(fp,EAP_Pack,60) != 0)
return 1;
return 0;
}
int Send_Eap_PWD(pcap_t *fp,u_char EAP_Pack[],u_char pkt_data[])
{
Fill_Eap_PWD(EAP_Pack,pkt_data);
if(pcap_sendpacket(fp,EAP_Pack,60)!=0)
return 1;
return 0;
}
int Send_Eap_Noti(pcap_t *fp,u_char Eap_Pack[])
{
Fill_EAP_Notification();
if(pcap_sendpacket(fp,EAP_Pack,Eap_Pack_Len)!=0)
return 1;
return 0;
}
//compare to request eap
int EAP_CMP_NOTI(u_char pkt_data[])
{
int j;
base[15]=0x00;base[16]=0x00;base[17]=0x05;base[18]=0x01;
ID=base[19]=pkt_data[19];base[20]=0x00;base[21]=0x05;base[22]=0x02;
for(j=0;j<6;j++)
if(base[j]!=pkt_data[j])
return 1;
for(j=12;j<23;j++)
if(base[j]!=pkt_data[j])
return 1;
return 0;
}
int EAP_CMP_MAC_Error(u_char pkt_data[])
{
int j;
//base[16]=pkt_data[16];base[17]=pkt_data[17];base[18]=0x04;base[19]=pkt_data[19];base[20]=pkt_data[20];
//base[21]=pkt_data[21];base[22]=pkt_data[22];base[23]=pkt_data[23];
base[24]=0x45;base[25]=0x32;
base[26]=0x35;base[27]=0x34;base[28]=0x35;base[29]=0x3a;
base[30]=0x20;base[31]=0x4d;
for(j=0;j<6;j++)
if(base[j]!=pkt_data[j])
return 1;
//for(j=12;j<32;j++)
// if(base[j]=pkt_data[j])
// return 1;
for(j=24;j<32;j++)
if(base[j]!=pkt_data[j])
return 1;
return 0;
}
int EAP_CMP_Name(u_char pkt_data[])
{
int j;
base[17]=0x05;base[18]=0x01;
ID=pkt_data[19];base[20]=0x00;base[21]=0x05;base[22]=0x01;
//for(j=23;j<64;j++)
// base[j]=0x00;
for(j=0;j<6;j++)
if(base[j]!=pkt_data[j])
return 1;
for(j=12;j<23;j++)
if(base[j]!=pkt_data[j])
return 1;
return 0;
}
int EAP_CMP_PWD(u_char pkt_data[])
{
int j;
base[17]=0x16;base[18]=0x01;ID=base[19]=pkt_data[19];
base[20]=0x00;base[21]=0x16;base[22]=0x04;base[23]=0x10;
for(j=0;j<6;j++)
if(base[j]!=pkt_data[j])
return 1;
for(j=12;j<18;j++)
if(base[j]!=pkt_data[j])
return 1;
return 0;
}
int EAP_CMP_Success(u_char pkt_data[])
{
int j;
base[17]=0x04;base[18]=0x03;
//for(j=22;j<64;j++)
// base[j]=0xa5;
for(j=0;j<6;j++)
if(base[j]!=pkt_data[j])
return 1;
for(j=12;j<19;j++)
if(base[j]!=pkt_data[j])
return 1;
return 0;
}
int EAP_CMP_Name_Error(u_char pkt_data[])
{
int j;
base[17]=0x2d;base[18]=0x04;base[19]=pkt_data[19];
base[20]=pkt_data[20];base[21]=pkt_data[21];base[22]=pkt_data[22];base[23]=pkt_data[23];base[24]=0x45;
base[25]=0x32;base[26]=0x35;base[27]=0x33;base[28]=0x31;
base[29]=0x3a;base[30]=0x20;base[31]=0xd3;//base[31]=0xc3;
for(j=0;j<6;j++)
if(base[j]!=pkt_data[j])
return 1;
//for(j=12;j<16;j++)
// if(base[j]!=pkt_data[j])
// return 1;
if(base[18]!=0x04)
return 1;
for(j=24;j<32;j++)
if(base[j]!=pkt_data[j])
return 1;
return 0;
}
int EAP_CMP_PWD_Error(u_char pkt_data[])
{
int j;
//base[17]=pkt_data[17];base[18]=0x04;base[19]=pkt_data[19];
//base[20]=pkt_data[20];base[21]=pkt_data[21];base[22]=pkt_data[22];base[23]=pkt_data[23];
base[24]=0x45;
base[25]=0x32;base[26]=0x35;base[27]=0x35;base[28]=0x33;
base[29]=0x3a;base[30]=0x20;base[31]=0xd3;//base[31]=0xc3;
for(j=0;j<6;j++)
if(base[j]!=pkt_data[j])
return 1;
for(j=12;j<16;j++)
if(base[j]!=pkt_data[j])
return 1;
if(pkt_data[18]!=0x04)
return 1;
for(j=24;j<32;j++)
if(base[j]!=pkt_data[j])
return 1;
return 0;
}
int EAP_CMP_ERR(u_char pkt_data[])
{
int j;
for(j=0;j<6;j++)
if(base[j]!=pkt_data[j])
return 1;
for(j=12;j<16;j++)
if(base[j]!=pkt_data[j])
return 1;
if(pkt_data[18]!=0x04)
return 1;
return 0;
}
void shut_reb_signal_pro(int sig)//后台进程的重启、关机信号处理
{
flag_sig_pro=0;
close(sockfd);
(void)unlink(lock_file);
Send_Eapol_Logoff();
(void)close(file_desc);
pcap_close(fp);
return;
}
void free_lock_file()
{
(void)close(file_desc);
(void)unlink(lock_file);
if(tcsetattr(fileno(stdin),TCSANOW,&old_set))
printf(“restore terminal’s mode failed!\n”);
}
int main(int argc, char *argv[])
{
int flag,flag_fail=1;
pcap_if_t *alldevs,*d;
char c,c1;
int j=0,OvertimeCount=0,sendcount=1;
char errbuf[PCAP_ERRBUF_SIZE];
char shell[100]={“/sbin/ifconfig “};
int rtrnval;
pid_t pid;
struct termios new_set;
tcgetattr(fileno(stdin),&old_set);
new_set=old_set;
new_set.c_lflag&=~ISIG;
//system(“clear;history -c;exit 0”);
if(tcsetattr(fileno(stdin),TCSANOW,&new_set))
{
printf(“set terminal’s mode failed!\n”);
return 1;
}
new_set.c_lflag&=~ECHO;
act.sa_handler=shut_reb_signal_pro;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
file_desc=open(lock_file,O_RDWR|O_CREAT|O_EXCL,0444);//建立锁文件
if(file_desc==-1)
{
printf(“%s_Lock already present\n”,argv[0]);
printf(“If running %s is the first time after starting the PC,or you once ended it by closing the terminal,please remove %s!\nElse may you are running %s.\n”,argv[0],lock_file,argv[0]);
return 1;
}
printf(“\nAttention:\n\nif the twisted-pair is pulled out of network_card,this programme may will hold on.If so, you will have to close this terminal to end it!\n\nPress ENTER to continue……\n”);
c1=getchar();
while(c1!=’\n’)
c1=getchar();
memset(EAP_Pack,0X00,Eap_Pack_Len);
j=0;
if(pcap_findalldevs(&alldevs,errbuf)==0)
{
d=alldevs;
if(!alldevs)
{
printf(“try \”sudo %s \”\n”,argv[0]);
free_lock_file();
return 1;
}
while(get_user_name());
//noecho();
if(tcsetattr(fileno(stdin),TCSAFLUSH,&new_set))
{
printf(“set terminal’s mode failed!\n”);
free_lock_file();
return 1;
}
while(get_user_pwd());
new_set.c_lflag|=ECHO;
if(tcsetattr(fileno(stdin),TCSANOW,&new_set))
{
printf(“set terminal’s mode failed!\n”);
free_lock_file();
return 1;
}
//echo();
printf(“\n\n”);
for(;d;d=d->next)
{
j++;
printf(“%d:network_card_%d’s name:%s\n”,j,j,d->name);
//if(d->description)
// printf(“device %d’s description:%s\n”,j,d->description);
// else
// printf(“No description available for device %d.\n”,j);
}
printf(“Enter the interface number:\n”);
scanf(“%c”,&c);
c1=c;
while(c1!=’\n’)
c1=getchar();
c=c-‘0’;
if(c>j||c<1)
{
printf(“out of range!\n”);
pcap_freealldevs(alldevs);
free_lock_file();
return 1;
}
d=alldevs;
for(j=1;j<c;j++,d=d->next);
}
else
{
printf(“not found network_card!\n”);
free_lock_file();
return 1;
}
strcpy(req.ifr_name,d->name);
if(get_mac_client())
{
printf(“got network_card’s mac error!\n”);
pcap_freealldevs(alldevs);
free_lock_file();
return 1;
}
// Open the adapter
if((fp=pcap_open_live(d->name,150,1,200,errbuf))==NULL)
{
printf(“Unable to open the adapter. %s is not supported by WinPcap\n”,d->name);
pcap_freealldevs(alldevs);
free_lock_file();
return 1;
}
strcat(shell,d->name);
strcat(shell,” down;/sbin/ifconfig “);
strcat(shell,d->name);
strcat(shell,” up;exit 0″);
pcap_freealldevs(alldevs);
memcpy(EAP_Pack,mac_server,6);
memcpy(EAP_Pack+6,mac_client,6);
//strcpy(user_name,argv[1]);
EAP_Pack[12]=0x88;EAP_Pack[13]=0x8e;EAP_Pack[14]=0x01;
memcpy(base,mac_client,6);
base[12]=0x88;base[13]=0x8e;base[14]=0x01;base[15]=0x00;base[16]=0x00;
if(!Send_Eapol_Start(fp,EAP_Pack))
printf(“start logging on!\n”);
else
{
printf(“start logging on error!\n”);
pcap_close(fp);
free_lock_file();
return 1;
}
j=1;
while(j)
{
flag=0;
if(OvertimeCount>10)//超时次数
{
printf(“OvertimeCount>10!Please check PC’s network_card!\n”);
break;
}
rtrnval=pcap_next_ex(fp,&header,(const u_char **)&pkt_data);
if(rtrnval)
{
j++;
if(EAP_REQUEST_LEN==header->len||EAP_PWD_ERR_LEN==header->len||EAP_NAME_ERR_LEN==header->len)
{
if(!EAP_CMP_Name_Error(pkt_data))
{
printf(“name does’n exist or is wrong!\n”);
break;
}
if(!EAP_CMP_PWD_Error(pkt_data))
{
printf(“password is wrong!check your password!\n”);
break;
}
if(!EAP_CMP_MAC_Error(pkt_data))
{
printf(“MAC error!Did you change your network_card?\n”);
break;
}
if(!EAP_CMP_NOTI(pkt_data))
{
flag=1;
j=1;
if(Send_Eap_Noti(fp,EAP_Pack))
{
printf(“sent Eap_Notification_Packet error!\n”);
break;
}
printf(“sent eap_notification packet successfully!\n”);
continue;
}
if(!EAP_CMP_Name(pkt_data))
{
flag=1;
j=1;
if(Send_Eap_Name(fp,EAP_Pack))
{
printf(“sent Eap_Name_Packet error!\n”);
break;
}
printf(“sent eap_name_packet successfully!\n”);
continue;
}
if(!EAP_CMP_PWD(pkt_data))
{
flag=1;
j=1;
if(Send_Eap_PWD(fp,EAP_Pack,pkt_data))
{
printf(“sent Eap_PWD_Packet error!\n”);
break;
}
printf(“sent eap_pwd_packet successfully!\n”);
continue;
}
if(!EAP_CMP_Success(pkt_data))
{
flag_fail=0;
printf(“Logged on successfully!\n”);
system((const char *)shell);//执行shell获取IP
printf(“\nIF failed to automatically get IP,please manually operate to get IP.\n”);
printf(“for example:\n”);
printf(” run the following commands in terminal:\n\tsudo ifdown <network_card’s name> \n”);
printf(“\tsudo ifup <network_card’s name>\n”);
printf(“\n\t\t–good luck!\n”);
printf(“\t\t%s by DW\n”,version);
break;
}
if(!EAP_CMP_ERR(pkt_data))
{
printf(“receive log-off packet!Please check your name,password and network_card!\n”);
break;
}
}
}
else if(rtrnval==0)
{
OvertimeCount++;
printf(“received package over time!\n”);
if(OvertimeCount%2==0)
{
if(!Send_Eapol_Start(fp,EAP_Pack))
printf(“Restart logging on!\n”);
else
{
printf(“restart logging on error!\n”);
break;
}
}
continue;
}
else
{
printf(“received package error!will exit!\n”);
break;
}
if(flag==0&&j%50==0)//如果连续50个数据包都不是认证所需的,重新发起认证
{
if(!Send_Eapol_Start(fp,EAP_Pack))
{
printf(“Restart logging on!\n”);
sendcount++;
continue;
}
else
{
printf(“Restart logging on error!\n”);
break;
}
}
if(sendcount>5)//重新发起认证次数
{
printf(“logged on failed!\n”);
break;
}
}
if(flag_fail)
{
close(sockfd);
pcap_close(fp);
free_lock_file();
printf(“exit……\n”);
}
else//进入后台处理
{
free_lock_file();
if((pid=fork())>0)
exit(0);
else if(pid<0)
{
pcap_close(fp);
exit(1);
}
file_desc=open(lock_file,O_RDWR|O_CREAT|O_EXCL,0444);
if(file_desc==-1)
return 1;
setsid();
chdir(“/”);
umask(0);
sigaction(SIGTERM,&act,0);
//处理认证成功后服务器持续发送确认包的情况
sockfd=socket(AF_INET,SOCK_STREAM,0);//用来获取指定网卡IP
Fill_EAP_Name(EAP_Pack);
base[15]=0x00;base[16]=0x00;base[17]=0x05;base[18]=0x01;
base[20]=0x00;base[21]=0x05;base[22]=0x01;
while(flag_sig_pro)
{
rtrnval=pcap_next_ex(fp,&header,(const u_char **)&pkt_data);
if(rtrnval&&EAP_REQUEST_LEN==header->len)
{
ID=base[19]=pkt_data[19];
if(!strncmp(base,pkt_data,22))
{
Fill_EAP_After_Suc();
if(pcap_sendpacket(fp,EAP_Pack,60))
shut_reb_signal_pro(-1);
}
}
}
//pause();
}
return 0;
}//end
版本1.3_20090815的主要改动:
(1)不像1.3以前的版本那样直接给程序传递参数用户名与密码,而是在程序中输入用户名与密码。
输入密码的时候,禁用了终端的回显功能。这样可以防止别人看到你的密码
(2)1.3以前的版本在程序开始运行一直到认证成功,如果按下CTRL+C,那么程序就会终止。1.3中禁止终端向认证程序发送信号,按下CTRL+C的话,程序不会终止。如果想终止程序,需要直接关闭终端。
(3)1.3与1.2相比,增加了处理另一种失败包的代码。这种失败包在你连续输错10次密码的过程中会出现。
目前己知问题:
如果网线被拔下的话,认证时程序会挂起。需要关闭终端强制程序退出。如果有网友知道如何检测网线是否拔出的话,请留言或发邮件到418331922@qq.com。
程序在DEBIAN(KDE)环境下运行成功,不知道在其它LINUX发行版下情况如何。
有关认证客户端的其它情况,请参阅其它日志。
如有代码错误、内存泄露等问题或更好的建议,请发邮件到418331922@qq.com。