linux用户和组

linux上每个用户都拥有一个唯一的用户名和一个相对应的用户id(uid),用户可以隶属于一个或多个组。每个组也拥有一个唯一的组名和组id(gid)。用户和组主要是用来控制资源访问权限的。

记录用户相关信息的文件:/etc/passwd

每个用户都会在此文件里有一条记录,每条记录包含7个字段。

root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin

登录名,密码占位符(通常为x,密码加密后实际存在/etc/shadow文件里),uid,gid,注释,登录后的主目录,登录的shell(登录后便交由这个程序控制/sbin/nologin代表此账号不能登录)

在密码文件中,允许(但不常见)同一用户id拥有多条记录,从而使得同一用户id可以有多个登录名。

记录密码文件/etc/shadow

记录组信息的文件:/etc/group

root:x:0:
bin:x:1:

组名,组密码占位符(一般为x),组id,组下的用户列表。

假设u_1,u_2都属于root组,那么上面这条记录应该是这个样子

root:x:0:u_1,u_2


获取相关用户信息

从/etc/passwd获取信息
#include <pwd.h>

struct passwd
{
  char *pw_name;		/* Username.  */
  char *pw_passwd;		/* Password.  */
  uid_t pw_uid;		        /* User ID.  */
  gid_t pw_gid;		        /* Group ID.  */
  char *pw_gecos;		/* Real name.  */
  char *pw_dir;			/* Home directory.  */
  char *pw_shell;		/* Shell program.  */
};

struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);
/*
成功返回struct passwd指针,返回NULL代表错误或找不到
*/
getpwnam()根据用户名获取而getpwuid根据uid获取信息,返回的指针指向静态分配的内存,任何一次调用都会覆盖之前的内容。
#include <stdio.h>
#include <pwd.h>
#include <errno.h>


int main(int argc, char **argv){
    struct passwd *pwd;
    errno = 0;
    pwd = getpwnam("root");
    if(pwd == NULL){
        if(errno == 0)
            printf("not found\n");
        else
            printf("error\n");
        return 1;
    }
    
    printf("name: %s\n", pwd->pw_name);
}


从/etc/group获取信息

#include <grp.h>

struct group{
    char *gr_name;		/* Group name.	*/
    char *gr_passwd;		/* Password.	*/
    gid_t gr_gid;		/* Group ID.	*/
    char **gr_mem;		/* Member list.	*/
};

struct group *getgrnam(const char *name);
struct group *getgrgid(gid_t gid);
//失败返回NULL
用法行为跟上两个函数相同。


从/etc/shadow文件获取信息

#include <shadow.h>

struct spwd{
    char *sp_namp;		/* Login name.  */
    char *sp_pwdp;		/* Encrypted password.  */
    long int sp_lstchg;		/* Date of last change.  */
    long int sp_min;		/* Minimum number of days between changes.  */
    long int sp_max;		/* Maximum number of days between changes.  */
    long int sp_warn;		/* Number of days to warn user to change
				   the password.  */
    long int sp_inact;		/* Number of days the account may be
				   inactive.  */
    long int sp_expire;		/* Number of days since 1970-01-01 until
				   account expires.  */
    unsigned long int sp_flag;	/* Reserved.  */
  };

struct spwd *getspnam(const char *name);


逐行读取/etc/passwd和/etc/group和/etc/shadow文件

#include <pwd.h>

struct passwd *getpwent(void);  //会打开/etc/passwd文件

void setpwent(void);    //光标移动到文件开始处
void endpwent(void);    //关闭/etc/passwd文件


#include <grp.h>

struct group *getgrent(void);  //会打开/etc/group文件
void setgrent(void);    //光标移动到文件开始处
void endgrent(void);    //会关闭/etc/group文件

#include <shadow.h>

struct spwd *getspent(void);
void setspent(void);
void endspent(void);
#include <stdio.h>
#include <pwd.h>
#include <errno.h>

int main(int argc, char **argv){
    struct passwd *pwd;
    while((pwd = getpwent()) != NULL)
        printf("%-8s %5d\n", pwd->pw_name, pwd->pw_uid);
    
}
/*
root         0
bin          1
daemon       2
adm          3
lp           4
sync         5
shutdown     6
...
*/


校验用户密码

#define _XOPEN_SOURCE
#include <unistd.h>

char *crypt(const char *key, const char *salt);
/etc/shadow的密码是用过单项加密的,所以验证密码的唯一方法是使用同一算法对用户提供的明文密码进行加密,然后跟/etc/shadow中的密码进行匹配。salt参数指向一个2字符的字符串用来改变des算法,为了让密码更难以破解。
由crypt()所返回的经过加密的密码中,头2个字符是对原始salt值得拷贝,也就是说加密密码时,salt参数可以从/etc/shadow内获得。
编译程序需要带上-lcrypt选项


#define _BSD_SOURCE     /* Get getpass() declaration from <unistd.h> */
#define _XOPEN_SOURCE   /* Get crypt() declaration from <unistd.h> */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <unistd.h>
#include <limits.h>
#include <pwd.h>
#include <shadow.h>

typedef unsigned char u_char;

int main(int argc, char *argv[]){
    char *username, *password, *encrypted, *p;
    struct passwd *pwd;
    struct spwd *spwd;
    u_char authOk;
    size_t len;
    long lnmax;

    lnmax = sysconf(_SC_LOGIN_NAME_MAX);
    if (lnmax == -1)                    /* If limit is indeterminate */
        lnmax = 256;                    /* make a guess */

    username = malloc(lnmax);
    if (username == NULL)
        return 1;

    
    printf("Username: ");
    fflush(stdout);
    if (fgets(username, lnmax, stdin) == NULL)
        return 1;            /* Exit on EOF */

    len = strlen(username);
    if (username[len - 1] == '\n'){
        username[len - 1] = '\0';
    }else{
        username[len] = '\0';
    }
    
    //获取/etc/passwd记录
    pwd = getpwnam(username);
    if (pwd == NULL){
        printf("couldn't get password record\n");
        return 1;
    }
    
    //获取/etc/shadow记录
    spwd = getspnam(username);
    if (spwd == NULL && errno == EACCES){
        printf("no permission to read shadow password file\n");
        return 1;
    }

    if (spwd != NULL)           /* If there is a shadow password record */
        pwd->pw_passwd = spwd->sp_pwdp;     /* Use the shadow password */

    password = getpass("Password: ");

    encrypted = crypt(password, pwd->pw_passwd);
    for (p = password; *p != '\0'; )
        *p++ = '\0';

    if (encrypted == NULL)
        return 1;

    authOk = strcmp(encrypted, pwd->pw_passwd) == 0;
    if (!authOk) {
        printf("Incorrect password\n");
        return 1;
    }

    printf("Successfully authenticated: UID=%ld\n", (long) pwd->pw_uid);


    return 0;
}

//gcc main.c -lcrypt


上一篇: linux动态内存分配
下一篇: linux进程凭证(权限)
作者邮箱: 203328517@qq.com