Linux 用户认证(crypt方式)

来源:互联网 发布:部落冲突石头升级数据 编辑:程序博客网 时间:2024/04/26 20:30

最近做的一些开发和Linux用户有密切的关系,以前没有做过相关的学习,仅仅停留在使用shell下的useradd, passwd命令,但对用户的认证及密码的管理基本没有了解。在这里做个小结。

1. 第一个重要文件/etc/passwd

例:/etc/passwd中的一行

rwan:x:1000:1000:Robin:/home/rwan:/bin/bash

包含七个字段,各个字段间用冒号隔开(用户名:密码:用户id:组id:用户描述:用户家目录:用户的登录shell

POSIX.1 定义了两个接口获取用户口令文件

#include <pwd.h>struct passwd *getpwuid(uid_t uid); //通过uid获取密码项struct passwd *getpwnam(const char *name); //通过用户名获取密码项struct passwd{     char *pw_name; //用户名     char *pw_passwd; //用户密码     uid_t pw_uid; //用户id     uid_t pw_gid; //用户组id     char *pw_gecos; //用户描述     char *pw_dir; //用户家目录     char *pw_shell; //用户登录shell};


要查看整个口令文件,POSIX.1接口则不能满足要求,需使用下列接口

#include <pwd.h>struct passwd *getpwent(); //返回口令记录的下一项void setpwent(); //定位到开头,即从第一项开始读void endpwent(); //读取结束,关闭

问题:为什么/etc/passwd中的所有密码都是个x?

在最早的Unix系统中,用户密码确实存放在/etc/passwd,但为了安全,后来把密码存放在/etc/shadow中,

因为/etc/paasswd对所有用户都是可读的,如果密码放在这里,会被暴力破解,而/etc/shadow只有root用户可读写和

shadow组用户可读(这里不同发行版可能不同,Fedora/etc/shadow只有root可只读,此处测试在Ubuntu下)。

root@localhost:/home/rwan# ls -l /etc/passwd-rw-r--r-- 1 root root 1371 2009-08-29 05:05 /etc/passwdroot@localhost:/home/rwan# ls -l /etc/shadow-rw-r----- 1 root shadow 878 2009-08-29 05:05 /etc/shadow


 2.第二个重要文件/etc/shadow

例:/etc/shadow中的一行

rwan:$1$TA.EyKcB$GMnqWwkKY9cr8667xIwXE0:14574:0:99999:7:::

包含九项内容(用户登录名:加密口令:之后的几项用于控制口令改动频率及账户活动状态情况)。

而在/etc/shadow中大致有三种情形,密码项为*!,或一个字符串,分别表示禁止账户登录,密码未设置,及加密后的密码。

使用useradd添加一个新用户,就会发现该用户的的密码项为!

例:添加一个无密码用户

root@localhost:/home/rwan# useradd testroot@localhost:/home/rwan# cat /etc/passwd |grep testtest:x:1001:1002::/home/test:/bin/shroot@localhost:/home/rwan# cat /etc/shadow | grep testtest:!:14574:0:99999:7:::Linux中提供了类似读取/etc/passwd的接口#include <shadow.h>struct spwd* getspnam(const char *name);struct spwd* getspent();void setspent();void endspent();struct spwd{      char *sp_namp; //用户名      char *sp_pwdp; //密码     ……}


3. 登录过程中如何进行密码比对

为了提高安全性,Linux引入了salt,所谓的salt,即为一个随机数,引入的时候为一个12-bit的数值,

当用户设置密码时,会随机生成一个salt,与用户的密码一起加密,得到一个加密的字符串(salt以明文形式包含在该字符串中),

存储到密码文件中,这样就将攻击的难度扩大了2124096倍。crypt将用户的keysalt一起适应某种算法进行加密(散列)
#inclue <stdlib.h>
char *crypt(const char *key, const char *salt);

crypt中可以使用多种加密(散列)机制,包括最初的DES,还有后来为提高安全性引入的md5blowfishsha-256sha-512.

crypt为支持不同的方式,将salt进行格式化,格式为:

$id$salt$encoded (这也是保存在密码文件中的格式)

这里不同id代表不同的算法,不同算法salt的长度也不同。

Id

Method

实际加密后的密码长度

1

         MD512salt字符)

22

2a

Blowfish

 

5

SHA-25612salt字符)

43

6

SHA-51212salt字符)

86

例:一个小程序

首先给刚才新建的用户test设置密码123456

root@localhost:/home/rwan# passwd testroot@localhost:/home/rwan# cat /etc/shadow | grep testtest:$1$svja5yi5$nGAXQLRtqM454THCcBv/50:14574:0:99999:7:::可以看到,Ubuntu采用的是Md5认证,salt = $1$svja5yi5$, 密钥 = nGAXQLRtqM454THCcBv/50可以用下面的程序来试验。#include <stdio.h>#include <stdlib.h>#include <string.h>void main(){    char *salt = "$1$svja5yi5";    printf("user test's secrect key = %s /n",crypt("123456", salt));}root@localhost:/home/rwan# gcc -o user.o user.c -lcryptroot@localhost:/home/rwan# ./user.ouser test's secrect key = $1$svja5yi5$nGAXQLRtqM454THCcBv/50 

 

呵呵。不错吧。和/etc/shadow中的一模一样吧,废话,不一样的话,出大问题了。

那么Linux是如何认证用户密码的,应该是用户输入密码后,login程序从/etc/shadow中获得该用户的salt

并计算密钥,和/etc/shadow中的比对,有了上面的struct spwd* getspnam(const char *name)函数,想模拟写个也不难。

4.更多思考

既然/etc/shadow只有root可写,那为什么普通用户可以修改自己的密码?

这里有一个suid的概念,什么意思呢?

root@localhost:/home/rwan# ls -l /usr/bin/passwd-rwsr-xr-x 1 root root 29104 2006-12-19 15:35 /usr/bin/passwd


所有用户对passwd命令均有可执行权限,注意里面的那个s,是说如果你有执行权限,那么运行该程序的时间,会以文件属主的权限执行。

5.别走开,我们可以走的更远

学习下怎么制造salt吧。

以下代码摘自busybox

#include <stdio.h>#include <unistd.h>static int i64c(int i){    i &= 0x3f;    if (i == 0)        return '.';    if (i == 1)        return '/';    if (i < 12)        return ('0' - 2 + i);    if (i < 38)        return ('A' - 12 + i);    return ('a' - 38 + i);}int crypt_make_salt(char *p, int cnt, int x){    x += getpid() + time(NULL);    do {        /* x = (x*1664525 + 1013904223) % 2^32 generator is lame         * (low-order bit is not "random", etc...),         * but for our purposes it is good enough */        x = x*1664525 + 1013904223;        /* BTW, Park and Miller's "minimal standard generator" is         * x = x*16807 % ((2^31)-1)         * It has no problem with visibly alternating lowest bit         * but is also weak in cryptographic sense + needs div,         * which needs more code (and slower) on many CPUs */        *p++ = i64c(x >> 16);        *p++ = i64c(x >> 22);    } while (--cnt);    *p = '/0';    return x;}void main() {    int rnd = rnd; //use uninitialized data to generate random digital    char salt[sizeof("$N$XXXXXXXX")];    strcpy(salt, "$1$");    rnd = crypt_make_salt(salt + 3, 4, rnd);    printf("slat = %s/n", salt);    printf("secret key = %s/n", crypt("123456", salt));}root@localhost:/home/rwan# gcc -o user2.o user2.c -lcryptroot@localhost:/home/rwan# ./user2.oslat = $1$rTWCKgPtsecret key = $1$rTWCKgPt$pIvnCsr3wAOw8XrJ9SQug1


思想:目的是生成8个随机字符作为salt。对着ASCII表,去理解吧。

因为ASCII码中有很多字符时不可打印字符,为了让加密过的密码看起来像普通的字符串,系统使用了一种叫base64的编码,

将散列后的结果转化为可打印字符。base64的基本思想,base64的字符集为A-Za-z0-9/.

原创粉丝点击