C语言实现su命令

来源:互联网 发布:固定资产管理 源码 编辑:程序博客网 时间:2024/06/08 10:07

su命令的功能为切换用户,首先看一下系统su命令的效果:


su命令为:su+用户名,没有输入参数时默认为root用户。由普通用户切换到root用户时,需要输入密码,由root用户切换到普通用户时不需要输入密码,而且密码输入时在屏幕上是不显示的。根据这些特点逐步实现速命令。


1、密码不显示设置

通过设置termios类型的数据结构中的值和使用一小组函数调用,就可以对终端接口进行控制。termios数据结构和相关的函数调用都定义在头文件termios.h中。

可以被调整来影响终端的值按照不同的模式被分成:

输入模式、输出模式、控制模式、本地模式和特殊控制字符。

最小的termios结构的定义如下,结构体成员的名称与上面列出的5种参数类型相对应:

#include<termios.h>struct termios{tcflag_t c_iflag;tcflag_t c_oflag;tcflag_t c_cflag;tcflag_t c_lflag;cc_t       c_cc[NCCS];};
函数tcgetattr初始化与一个终端对应的termios结构,把当前的终端接口变量的值写入p指向的结构:

int tcgetattr(int fd,struct termios *p);

函数tcsetattr来重新配置终端接口:int tcsetattr(int fd,int actions,const struct termios *p);

参数actions控制修改方式,共有3种修改方式:

TCSANOW:立刻对值进行修改;

TCSADRAIN:等当前的输出完成后再对值进行修改;

TCSAFLUSH:等当前的输出完成后再对值进行修改,但丢弃还未从read调用返回的当前可用的任何输入。


本地模式控制终端的各种特性,c_lflag成员中最重要的两个宏是ECHO和ICANON。ECHO 启用输入字符的本地回显功能,ICANON 启用标准输入处理。

struct termios old, new;tcgetattr(0, &old);new = old;new.c_lflag &= ~ECHO;//设置成不显示tcsetattr(0, TCSANOW, &new);fgets(passwd, 128, stdin);tcsetattr(0, TCSANOW, &old);//密码输入完成后要立即设回回显模式passwd[strlen(passwd) - 1] = 0;printf("\n");

这样我们就解决了密码不显示的问题。


那么,密码输入进去以后首先要判断密码是否正确,这里要用到crypt()函数。其原型为char *crypt(const char*key,const char*salt). key是我们传入的明文,salt是我们指定用来加密的密钥,返回值为加密后的密文。那这里关键是要知道salt,那就要用到getspnam函数。


getspnam()函数可以访问shadow口令,其原型为struct spwd *getspnam(const char*name),其返回值为spwd结构体指针。spwd结构部分如下:

struct spwd{      char *sp_namp;//用户登录名      char *sp_pwdp;//加密口令}
要注意,这里只有root用户才可以调用该函数,所以要用chmod a+s su命令加上s权限,使得不管谁执行起来都具有所有者权限。

接下来就可以比较输入的密码是否正确,如果不正确直接退出,如果正确又会执行什么操作呢?先看一下系统是怎么做的。


通过上图可以发现,我们实现su命令就是如下过程:



整体代码如下:

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <assert.h>#include <sys/types.h>#include <pwd.h>#include <shadow.h>#include <termios.h>int main(int argc, char *argv[]){char *user = "root";if(argv[1] != NULL){user = argv[1];}//input  passwordprintf("Passwd: ");fflush(stdout);char passwd[128] = {0};struct termios old, new;tcgetattr(0, &old);new = old;new.c_lflag &= ~ECHO;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);char salt[128] = {0};int i = 0, count = 0;for(; sp->sp_pwdp[i] != 0; ++i){salt[i] = sp->sp_pwdp[i];if(salt[i] == '$'){count++;if(count == 3){break;}}}char *p = crypt(passwd, salt);assert(p != NULL);if(strcmp(p, sp->sp_pwdp) != 0){printf("passwd error\n");exit(0);}pid_t pid = fork();assert(pid != -1);if(pid == 0){struct passwd *pw = getpwnam(user);assert(pw != NULL);setuid(pw->pw_uid);setenv("HOME", pw->pw_dir, 1);execl(pw->pw_shell, pw->pw_shell, (char*)0);printf("error\n");exit(0);}else{wait(NULL);}}





原创粉丝点击