命令解释器:http://blog.csdn.net/qq_35256722/article/details/53316066
ls的实现: http://blog.csdn.net/qq_35256722/article/details/53323238
pwd和clear 的实现:http://blog.csdn.net/qq_35256722/article/details/53322604
su,切换目录的,首先切换目录 ,通过切换uid来实现的,切换uid后,我们就得到了一个表面看起来是切换的名字,但是你的gid还没切换过来,所以你还没有全部是那个用户。切换过的用户,一定是新的用户完全替换old用户,那么exec又来了。
1. struct passwd *p = getpwnam(s); 根据用户名得到更多信息,例如:id
2. struct spwd * sp = getspnam(s); //从shadow里面读出加密的密码
3.int tcgetattr(int fd, struct termios *termios_p); 把当前终端的变量信息,写到termios_p这个结构体中。
4.char *crypt(const char *key, const char *salt);加密函数:key加密的密钥是 salt,
函数分析完了,那么大体思路,
先从根据用户名获得当前用户的详细信息,从shadow里面读取该用户的密文,和用相同的密钥加密用户输入的密码后,作比较,相同开辟子进程,否则退出。
1.比较密码:
那么从shadow里面读取密文要分割用$分割,分割前两个$,一个加密方式,一个是加密的密钥,第三个那$是密文,用户输入的密码,在crypt函数的作用下,利用从shadow里面读取的密钥和加密方式,对用户所输入的密码加密,屏和系统的作比较。
2.设置密码不可见
在我们每次输入密码时,系统的密码总是什么都没有,所以我们在这里对用户输入的密码加以处理,nts.c_lflag 宏nts.c_lflag &= ~(ECHO|ICANON),将nts 的设置成不回显隐藏起来。
3.输入完成后,设置回显
在输入完成后,提取密码时,记得回显,这样子才能把密码读到,后续 可以正常工作。
代码实现:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include<sys/stat.h>
#include<pwd.h>
#include<shadow.h>
#include<termios.h>
int main(int argc,char *argv[])
{
char *s = "root";
if(argc == 2)
{
s = argv[1];
}
struct passwd *p = getpwnam(s);
if(p == NULL)
{
perror("username error");
exit(0);
}
struct spwd * sp = getspnam(s); //从shadow里面读出加密的密码
if(sp == NULL)
{
printf("sp is error\n");
exit(0);
}
printf("input passwd:");
fflush(stdout);
struct termios ts,nts;
tcgetattr(0,&ts); //把当前终端端口变量的值,写入ts这个结构体中,如果这些值其后被修改,你可能通过调用函数tcsetattr 来重新配置终端接口
nts = ts;
// tcflag_t c_iflag; /* input modes */
nts.c_lflag &= ~(ECHO|ICANON);
c_iflag 的宏
// ECHO 输入字符串的本地回显功能
// ICANON 启用标准输入处理
/*TCSANOW 立即对值进行修改。
TCSADRAIN 等当前的输出完成后再对值进行后,修改
TCSAFLUSH 等当前的输出完成后再对值进行修改,但丢弃还未从read调用返回的当前可用的任何输入。*/
tcsetattr(0,TCSANOW,&nts); //密码隐藏
char buff[128] = {0};
int j = 0;
char ch;
while((ch=getchar()) !='\n')
{
buff[j++] = ch;
}
//fgets(buff,128,stdin);
//buff[strlen(buff)-1] = 0;
char lt[128]= {0};
int i = 0;
int num = 0;
tcsetattr(0,TCSANOW,&ts); //密码回显
for(;i < strlen(sp->sp_pwdp);i ++)
{
if(sp->sp_pwdp[i] == '$')
{
num ++;
if(num ==3)
{
break;
}
}
lt[i] = sp->sp_pwdp[i];
}
char *fg = crypt(buff,lt); //加密函数
if(strcmp(fg,sp->sp_pwdp) != 0) //采用相同的密钥来加密,最后来比较密文就可以了
{
printf("fg error\n");
exit(0);
}
printf("succes!\n");
pid_t pid = fork();
assert(pid != -1);
if(pid == 0)
{
setuid(p->pw_uid);
setgid(p->pw_gid);
setenv("HOME",p->pw_dir,1);//加上u+s 无论谁调起来,调起来就变成来root
execl(p->pw_shell,p->pw_shell,(char*)0);//启动当前的终端
perror("exec error");
exit(0);
}
wait(NULL);
}