一、笔记
符号: ~ 表示按位取反(看C语言设计)
二、实现命令(可能不全)
//File Name: su.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include <pwd.h>
#include <sys/types.h>
#include <shadow.h>
#include <termios.h>
// su --> argv[0] argv[1] == NULL
// su --> argv[0] argv[1] == "stu"
int main(int argc, char *argv[])
{
char *user = "root";
if(argv[1] != NULL)
{
user = argv[1];
}
printf("Password: ");
fflush(stdout);
char passwd[128] = {0};
int index = 0;
// 1、获取终端的属性
struct termios old, new;
tcgetattr(0, &old);
new = old;
new.c_lflag &= ~ECHO;
new.c_lflag &= ~ICANON;
//2、设置终端的新属性
tcsetattr(0, TCSANOW, &new);
char c = 0;
while((c = getchar()) != '\n')
{
if(c == 127)
{
if(index > 0)
{
passwd[--index] = 0;
printf("\033[1D\033[K");
}
continue;
}
passwd[index++] = c;
printf("*");
fflush(stdout);
}
// 3、恢复终端的属性
tcsetattr(0, TCSANOW, &old);
printf("\n");
//passwd[strlen(passwd) - 1] = 0; // fgets 获取的字符串最后会有一个\n存在
// 密码匹配
struct spwd *sp = getspnam(user); // sp->sp_pwdp;
assert(NULL != sp);
//对用户输入的明文进行加密
//1、提取密钥
char salt[128] = {0};
index = 0;
char *p = sp->sp_pwdp;
int count = 0; // 记录遇到的$的个数
while(*p)
{
salt[index++] = *p;
if(*p == '$')
{
count++;
if(count == 3)
{
break;
}
}
p++;
}
char *q = crypt(passwd, salt);
assert(NULL != q);
if(0 != strcmp(sp->sp_pwdp, q)) // 条件不成立, 密码匹配失败
{
printf("my_su: incorrect password\n");
return 0;
}
pid_t n = fork();
assert(-1 != n);
if(0 == n)
{
struct passwd *pw = getpwnam(user); // passwd结构体指针指向的是新用户的信息
assert(pw != NULL);
setuid(pw->pw_uid); // 切换到新用户
setenv("HOME", pw->pw_dir, 1); // 在程序中修改环境变量
execl(pw->pw_shell, pw->pw_shell, (char*)0); // main函数的参数至少有一个(执行进程的命令)
perror("execl error: ");
}
else
{
wait(NULL); // 等创建的子进程(新启动bash)退出
}
exit(0);
}