linux用户和组

2018-05-20 20:15:26

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用户和组以及相关的做了简单的介绍,如果有疑问可以给我留言。


备注

1.编译器版本gcc4.8,运行环境centos7 64位
2.原文地址http://www.freecls.com/a/2712/2d

©著作权归作者所有
收藏
推荐阅读
  • err
    linux动态内存分配

    进程可以通过增加堆的大小来分配内存,堆就是一段长度可变的连续的虚拟内存,开始于未初始化数据段末尾,随着内存的分配和释放增减。通常堆的当前内存边界称为program bre...

  • err
    linux进程

    进程是一个可执行程序的实例。程序包含了一系列信息,这些信息描述了如何在运行时创建一个进程,所包含的内容如下。1.二进制格式标识,linux上用的是elf格式2.机器语言指...

  • err
    linux文件io(open、read、write、close)

    所有执行io操作的系统调用都是以文件描述符(大于0的整数)来指代打开的文件。文件描述符可以表示诸如管道(pipe)、fifo、socket、终端、设备和普通文件。对于每个...

  • err
    linux系统调用及错误处理

    系统调用是内核提供给外部程序的接口,进程可以通过系统调用来一自己的名义来执行某些动作。在深入了解系统调用之前,先关注以下几点。1.系统调用处理器会从用户态切换到核心态以便...

  • linux内核简介

    内核是用来管理和分配计算机资源的,它主要有进程调度、内存管理、提供文件系统、创建和终止进程、对设备的访问、联网、提供系统调用的接口等。内核还可以为内阁用户模拟出抽象的虚拟私有计算机,每个用户都可以登录...

  • nginx模块 ngx_http_headers_module

    ngx_http_headers_module 模块是用来增加 Expires 和 Cache-control,或者是任意的响应头。Syntax: add_header name value [alw...

  • nginx模块 ngx_http_gunzip_module、ngx_http_gzip_module、ngx_http_gzip_static_module

    ngx_http_gunzip_module 模块将文件解压缩后并在响应头加上 "Content-Encoding: gzip" 返回给客户端。为了解决客户端不支持gzip压缩。编译的时候带上 --w...

  • nginx模块 ngx_http_flv_module、ngx_http_mp4_module

    ngx_http_flv_module模块提供了对 flv 视频的伪流支持。编译的时候带上 --with-http_flv_module。它会根据指定的 start 参数来指定跳过多少字节,并在返回数...

  • nginx模块 ngx_http_fastcgi_module

    ngx_http_fastcgi_module 模块使得nginx可以与 fastcgi 服务器通信。比如目前要使得 nginx 支持 php 就得使用 fastcgi技术,在服务器上装上 nginx...

  • nginx模块 ngx_http_autoindex_module

    ngx_http_autoindex_module 模块可以将uri以 / 结尾时,列出里面的文件和目录。Syntax: autoindex on | off; Default: autoindex ...

简介
天降大任于斯人也,必先苦其心志。