您的位置:首页 > 运维架构 > Shell

MyMiniBash之su命令

2019-04-07 00:39 232 查看

su 命令用来切换用户
su—》表示默认切换到root用户
su uer --》表示切换到user用户

主体框架
通过mybash–fork–》su–fork–》bash

1/切换到那个用户
2、输入密码
3、密码匹配
4、fork子进程调用bash,父进程等待子进程结束

实现代码:

int main(int argc, char *argv[])
{

char *user="root";

if(argc>1)
{
user=argv[1];
}

printf("Password:");
fflush(stdout);

char passwd[128]={0};

struct termios old, new;
tcgetattr(0,&old);
new=old;
new.c_lflag&=~ECHO;//设置不回显
new.c_lflag&=ICANON;
tcsetattr(0,TCSANOW,&new);

fgets(passwd,128,stdin);
tcsetattr(0,TCSANOW,&old);

passwd[strlen(passwd)-1]=0;

printf("\n");

//获取密文
struct spwd *sp=getspnam(user);
assert(sp!=NULL);

//sp->sp_pwdp    $id$salt$.....第3个$结束

char *p=sp->sp_pwdp;
char salt[128]={0};
int i=0,count=0;

while(*p!=0)//获取密文
{
salt[i++]=*p;
if(*p=='$')
{
count++;
}
if(count==3)
{
break;
}
p++;

}

char *pwd=crypt(passwd, salt);//加密
assert(pwd!=NULL);

if(strcmp(pwd,sp->sp_pwdp)!=0)
{
printf("passwd error");
exit(0);
}

pid_t pid=fork();
assert(pid!=-1);

if(pid==0)
{
struct passwd *UserInfo=getpwnam(user);

setuid(UserInfo->pw_uid);//切换用户
setenv("HOME",UserInfo->pw_dir,1);

execl(UserInfo->pw_shell,UserInfo->pw_shell,(char *)0);

printf("su exec fail\n");
exit(0);

}
else
{
wait(NULL);
}
}

知识点总结:
1、密码的回显控制
struct termios
{
tcflag_t c_iflag //输入标志,通过终端设备驱动程序控制字符的输入
tcflag_t c_oflag //输出标志,控制驱动程序输出
tcflag_t c_cflag //控制标志,影响RS-232串行线
tcflag_t c_lflag //本地标志,影响驱动程序和用户之间的接口(回显的开关ECHO)
cc_t c_cc[NCCS]

}

我们使用的本地标志,来控制密码的回显,通过new.c_lflag &=~ECHO来控制不回显
设置两个termios变量 old 和new 通过old获得当前的终端控制,在赋值给new,在让new设置为不回显,在通过new来设置终端,输入完密码后再,在将终端设置为old,还原终端。

2、获取密文

struct spwd {
char sp_namp; / user login name */
char sp_pwdp; / encrypted password /
long int sp_lstchg; / last password change /
long int sp_min; / days until change allowed. /
long int sp_max; / days before change required /
long int sp_warn; / days warning for expiration /
long int sp_inact; / days before account inactive /
long int sp_expire; / date when account expires /
unsigned long int sp_flag; / reserved for future use */
}
用来描述用户的密码的结构体
每个字段的含义是:

·   sp_namp - 指向以 null 结束的用户名的指针

·   sp_pwdp - 指向 null 结束的密码的指针

·   sp_lstchg - 最近更改密码的日期(日期计算方法是从1970年1月1日开始的天数)

·   sp_min - days before which password may not be changed

·   sp_max - days after which password must be changed

·   sp_warn - days before password is to expire that user is warned of pending password expiration

·   sp_inact - days after password expires that account is considered inactive and disabled

·   sp_expire - days since Jan 1, 1970 when account will be disabled

·   sp_flag - reserved for future use

通过该结构体的getspnam(const char * username);来获取该结构体变量指针。从而得到该用户的密文,密文由$id$salt$encode组成,在得到crypt函参数中的salt变量$id$salt$,对密码进行加密。

3、判断获得的密文是否正确
strcmp(pwd,sp->sp_pwdp);

4、切换用户
通过struct passwd
{
char *pw_name 用户登录名
uid_t pw_uid UID号
gid_t pw_gid GID 号
char *pw_dir 用户家目录
char *pw_gecos 用户全名
char *pw_shell 用户默认shell
};
来获取用户信息,setuid是用来切换用户的,而setenv是来改变当前的环境变量,参数1,是设置是立即改变的。
5、开辟进程
execl来进入用户的环境。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: