1、密码文件/etc/passwd

在这里插入图片描述
上述7个区段

【登录名】:登录系统的用户名;
【密码】:包含加密处理的密码,长度为13,使用x代替,实际密码存储再shadow中;
【用户ID】:用户的数值型ID,root为0;
【组ID】:用户属组中首选属组的数值型ID;
【注释】:存放关于用户的描述性文字;
【主目录】:用户登录后的初始路径;

2、密码文件/etc/shadow

该文件包括加密处理的密码;

3、组文件/etc/group

该文件下一条记录的各个字段:
- 【组名】:与密码文件中的登录名类似;
- 【密码】:组密码,该密码经由加密处理;
- 【组ID】:该组的数值型ID;
- 【用户列表】:属于该组的用户名列表;

4、获取用户和组的信息

从密码文件获取记录

#include <pwd.h>

struct passwd *getpwnam(const char *name);

struct passwd *getpwuid(uid_t uid);

struct passwd{
	char *pw_name;		// 登录名
	char *pw_passwd;	// 密码
	uid_t pw_uid;		// 用户ID
	gid_t pw_gid;		// 组ID	
	char *pw_gecos;		// 用户信息
	char *pw_dir;		// 工作目录
	char *pw_shell;		// 登录shell
};
/**
上述两个函数返回一致,只是通过查询的方式不同;
*/

从组文件获取记录

#include <grp.h>

struct group *getgrnam(const char *name);
struct group *getrgid(git_t gid);

struct group {
	char *gr_name;		// 组名 
	char *gr_passwd;	// 密码
	gid_t gr_gid;		// 组ID
	char **gr_mem;		
};

/**
上述函数分别通过组名和组ID来查找属主信息;
*/

扫描密码文件和组文件中的所有记录

#include<pwd.h>

struct passwd *getpwent(void);
/**
@func: 从密码文件中逐条返回记录,调用会自动打开密码文件;
*/
void setpwent(void);
/**
@func: 重返文件起始处;
*/
void endpwent(void);
/**
@func: 密码文件处理完,使用该函数进行关闭;
*/

从shadow密码文件中获取记录

#include<shadow.h>

struct spwd *getspnam(const char* name);
struct spwd *getspent(void);
void setspent(void);
void endspent(void);

struct spwd {
	char *sp_namp;	// 用户名
	char *sp_pwdp;	// 密码
	long sp_lstchg;	
	long sp_min;
	long sp_max;
	long sp_warn;
	long sp_inact;
	long sp_expire;
	unsigned long sp_flag;
};

读取用户密码

#include <unistd.h>

char *getpass(const char *prompt);
/**
@func: 屏蔽回显功能,读取用户密码;
return: 返回密码;
*/

5、密码加密和用户认证

由于安全方面的原因,UNIX系统采用单向加密算法对密码进行加密,故加密形式将无法还原出原始密码;
因此,验证候选密码的唯一方法是使用同一算法对其进行加密,并将加密结果与存储于/etc/shadow中的密码进行匹配;
加密算法封装于crypt()函数之中;
#include <unistd.h>

char *crypt(const char *key, const char *salt);

/**
@func: 接收一个最长可达8字符的密码,并对其加密;
@param salt: 指向一个两字符的字符串,用来扰动DES算法;
return: 指向长度未13个字符的字符串,该字符串为静态分配而成,即经过加密处理的密码;
*/
【注意】: 在编译程序时,指定-lcrypt选项,链接该库;

密码验证

#define _BSD_SOURCE
#define _XOPEN_SOURCE
#include <unistd.h>
#include <limits.h>
#include <pwd.h>
#include <shadow.h>
#include "../Jxiepc/tlpi_hdr.h"

int main() {
    char *username, *passwd, *encrypted, *p;
    struct passwd *pwd;
    struct spwd *spwd;
    bool authOk;
    size_t len;
    long lnmax;

    /* 获取主机上用户名字符串的最大长度 */
    lnmax = sysconf(_SC_LOGIN_NAME_MAX);
    if(lnmax == -1) lnmax = 256;

    /* 给用户名分配内存 */
    username = (char*)malloc(lnmax);
    if(username == NULL) errExit("malloc");

    /* 获取用户输入用户名 */
    printf("username:");
    fflush(stdout);
    if(fgets(username, lnmax, stdin) == NULL)
        exit(EXIT_FAILURE);
    /* 记录实际用户名长度 */
    len = strlen(username);
    if(username[len - 1] == '\n')   
        username[len-1] = '\0';

    /* 从密码文件中获取记录 */
    pwd = getpwnam(username);
    if(pwd == NULL)
        fatal("couldn't get password record");

    printf("%s\n", pwd->pw_passwd);
    /* 从组文件中获取记录 */
    spwd = getspnam(username);
    if(spwd == NULL && errno == EACCES)
        fatal("no permission to read shadow password file");
    if(spwd != NULL)
        pwd->pw_passwd = spwd->sp_pwdp;
    printf("%s\n", spwd->sp_pwdp);
    
    /* 从用户输入中获取 */
    passwd = getpass("Password: ");

    /* 密码加密验证 */
    encrypted = crypt(passwd, pwd->pw_passwd);
    printf("%s\n", encrypted);
    for(p = passwd; *p != '\0';)
        *p++ = '\0';
    if(encrypted == NULL)
        errExit("crypt");
    authOk = strcmp(encrypted, pwd->pw_passwd) == 0;
    if(!authOk){
        printf("Incorrect password\n");
        exit(EXIT_FAILURE);
    }
    printf("UID=%ld\n", (long )pwd->pw_uid);

    return 0;
}


Logo

更多推荐