转载:使用c语言验证linux用户密码

时间:2022-11-04 13:00:10

转载来源:http://blog.163.com/lixiangqiu_9202/blog/static/5357503720131120115144149/

linux下如何验证用户的密码呢?我想很多人都知道有两个重要的文件
/etc/passwd
/etc/shadow
这两个文件,前者任何人都可以读,而后者则只有root权限能够读写或者shadow组用户可读(不同发行版可能不同),对于linux的登陆验证我想大家可能都知道是将输入的密码进行相应编码加密后与/etc/shadow文件中对应的密码部分进行比较,如果相同则验证通过,如果不同则表明密码错误,但是问题是我们要如何将用户输入的密码加密然后进行比较。


为了提高安全性,Linux引入了salt,所谓salt即为一个随机数,引入的时候为一个12bit的数值,当用户设置密码时,会随机生成一个salt与用户的密码一起加密,得到一个加密的字符串(salt以明文形式包含在该字符中),存储到密码文件中。crypt函数可以将用户输入的密码和salt一起适应某种算法进行加密,该加密后的字符串就是我们需要的与密码文件中密码对比的加密字符串。
crypt为支持不同的方式将salt格式化为
$id$salt$encode
其中id代表不同的算法
              1   | MD5
              2a  | Blowfish (not in mainline glibc; added in some
                  | Linux distributions)
              5   | SHA-256 (since glibc 2.7)
              6   | SHA-512 (since glibc 2.7)


这样我们就可以利用crypt函数将用户输入的字符加密,然后与密码文件中的密码进行对比了,有一个函数getspnam可以根据用户名从/etc/shadow中得到密码,函数返回的数据结构为

           struct spwd {
               char *sp_namp;     /* Login name */
               char *sp_pwdp;     /* Encrypted password */
               long  sp_lstchg;   /* Date of last change (measured
                                     in days since 1970-01-01 00:00:00 +0000 (UTC)) */
               long  sp_min;      /* Min # of days between changes */
               long  sp_max;      /* Max # of days between changes */
               long  sp_warn;     /* # of days before password expires
                                     to warn user to change it */
               long  sp_inact;    /* # of days after password expires
                                     until account is disabled */
               long  sp_expire;   /* Date when account expires (measured
                                     in days since 1970-01-01 00:00:00 +0000 (UTC)) */
               unsigned long sp_flag;  /* Reserved */
           };


我们现在创建一个test用户,并将密码设置为1234来做个测试看一下
这是创建之后从/etc/shadow中取出来的密码部分
$6$qOrvsN41$gGlv8z7P1sAKuyaTAY03AvCn9/Z6Ygc4DJ9Uwe0RVzNAI6TQsTfsdihPnIh3lSZV2C02HjW.9bvJNdep3k.ER.
可以看到这里的salt为$6$qOrvsN41


最后写个程序做下验证


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <shadow.h>
#include <errno.h>


void help(void)
{
    printf("用户密码验证程序\n第一个参数为用户名!\n");
    exit(-1);
}


void error_quit(char *msg)
{
    perror(msg);
    exit(-2);
}


void get_salt(char *salt,char *passwd)
{
    int i,j;


    //取出salt,i记录密码字符下标,j记录$出现次数
    for(i=0,j=0;passwd[i] && j != 3;++i)
    {
        if(passwd[i] == '$')
            ++j;
    }


    strncpy(salt,passwd,i-1);
}


int main(int argc,char **argv)
{
    struct spwd *sp;
    char *passwd;
    char salt[512]={0};


    if(argc != 2)
        help();


    //输入用户名密码
    passwd=getpass("请输入密码:");
    //得到用户名以及密码,命令行参数的第一个参数为用户名
    if((sp=getspnam(argv[1])) == NULL)
        error_quit("获取用户名和密码");
    //得到salt,用得到的密码作参数
    get_salt(salt,sp->sp_pwdp);


    //进行密码验证
    if(strcmp(sp->sp_pwdp,crypt(passwd,salt)) == 0)
        printf("验证通过!\n");
    else
        printf("验证失败!\n");


    return 0;
}