useradd在Linux中是怎样完成添加用户的

在上篇文字《Linux 单用户模式patch解析》(传送门:https://blog.csdn.net/cui841923894/article/details/82261386)的最后有个疑问,为什么在单用户模式下useradd操作仍然可以执行成功(su操作却失败),单用户模式不应该屏蔽所有用户操作吗?难道是因为patch漏了对内核中useradd函数的禁用?
带着上面疑问,展开了我的探索之旅。

查找linux内核代码中useradd接口

开始想的是只要找到内核useradd函数,然后仿照patch的操作,使用#ifdef CONFIG_MULTIUSER屏蔽掉函数,然后添加到sys_in.c中,不就可以禁止useradd操作了吗?
然后linux代码下执行

#grep "useradd" -rn *
#grep "adduser" -rn *

竟然没找到相关函数。

#vim include/linux/syscalls.h

也没找到添加用户的接口,难道用户添加接口名称不是这些?

strace和ftrace查看用户态->内核态调用流程

既然无法直接搜到,那么我ftrace抓一下关键函数总可以了吧。

#cd /sys/kernel/debug/tracing
#echo 0 > tracing_on; echo "" > trace
#echo 1 > tracing_on ; useradd cbx; echo 0 > tracing_on
#cat trace > ftrace.log
#strace useradd cbx1 > strace.log

抓到ftrace.log和strace.log,仔细查看调用流程(篇幅有限,截取关键流程):
ftrace.log

passwd-850   [000] ....   923.403649: do_sys_open <-do_syscall_64
          passwd-850   [000] ....   923.403653: getname <-do_sys_open
          passwd-850   [000] ....   923.403656: getname_flags <-do_sys_open
          passwd-850   [000] ....   923.403676: get_unused_fd_flags <-do_sys_open
          passwd-850   [000] ....   923.403680: __alloc_fd <-do_sys_open
          passwd-850   [000] ....   923.403692: do_filp_open <-do_sys_open
          passwd-850   [000] ....   923.404027: put_unused_fd <-do_sys_open
          passwd-850   [000] ....   923.404967: putname <-do_sys_open
          passwd-850   [000] ....   923.404971: kmem_cache_free <-do_sys_open
          passwd-850   [000] ....   923.404995: __ia32_sys_vfork <-do_syscall_64
        addgroup-851   [001] ....   923.409951: __x64_sys_execve <-do_syscall_64
        addgroup-851   [001] ....   923.409954: getname <-__x64_sys_execve
        addgroup-851   [001] ....   923.409957: getname_flags <-__x64_sys_execve
        addgroup-851   [001] ....   923.409981: __do_execve_file.isra.54 <-__x64_sys_execve
        addgroup-851   [001] ....   923.411965: __x64_sys_execve <-do_syscall_64
        addgroup-851   [001] ....   923.411968: getname <-__x64_sys_execve
        addgroup-851   [001] ....   923.411971: getname_flags <-__x64_sys_execve
        addgroup-851   [001] ....   923.411988: __do_execve_file.isra.54 <-__x64_sys_execve
          passwd-850   [000] ....   923.425624: __x64_sys_wait4 <-do_syscall_64
          passwd-850   [000] ....   923.425627: kernel_wait4 <-__do_sys_wait4
        addgroup-851   [001] ....   923.430820: __x64_sys_newuname <-do_syscall_64

中间主要是do_sys_open,getname,up_write,down_write_killable,__ia32_sys_getuid,__x64_sys_setpgid等等。唯一相关的sys_getuid和sys_setpgid,但也不是add user相关的函数,其他都是打开文件,getname等。

strace.log

…
utime("/etc/passwd-", [2018/08/30-16:02:14, 2018/08/30-16:02:09]) = 0
close(5)                                = 0
munmap(0x7fcece621000, 4096)            = 0
umask(0777)                             = 022
open("/etc/passwd+", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 5
umask(022)                              = 0777
fchown(5, 0, 0)                         = 0
fchmod(5, 0644)                         = 0
fstat(5, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcece621000
write(5, "at:x:25:25:Batch jobs daemon:/va"..., 3317) = 3317
fsync(5)                                = 0
close(5)                                = 0
munmap(0x7fcece621000, 4096)            = 0
lstat("/etc/passwd", {st_mode=S_IFREG|0644, st_size=3285, ...}) = 0
rename("/etc/passwd+", "/etc/passwd")   = 0
…
munmap(0x7fcece621000, 4096)            = 0
utime("/etc/shadow-", [2018/08/30-16:02:14, 2018/08/30-16:02:09]) = 0
close(7)                                = 0
munmap(0x7fcece61f000, 4096)            = 0
umask(0777)                             = 022
open("/etc/shadow+", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 5

也没有类似添加用户的系统调用发现。这时候隐隐感觉,内核可能真的没有添加用户的操作,那到底谁来管理用户呢。

从useradd源码入手

#which useradd
/usr/sbin/useradd
#rpm -qf /usr/sbin/useradd
shadow-4.1.5.1-14.4.x86_64

useradd命令来源于shadow命令包,下载,解压

#wget https://ftp.osuosl.org/pub/blfs/conglomeration/shadow/shadow-4.1.5.1.tar.bz2
#tar –xvf shadow-4.1.5.1.tar.bz2
#cd shadow-4.1.5.1/src/
# vim useradd.c

// 找到main函数

/*
 * main - useradd command
 */
int main (int argc, char **argv)
{
    …
    #ifdef ACCT_TOOLS_SETUID
    #ifdef USE_PAM
    {
         struct passwd *pampw;
        pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
        if (pampw == NULL) {
        fprintf (stderr,
               _("%s: Cannot determine your user name.\n"),
               Prog);
        fail_exit (1);
    }
    //
    retval = pam_start ("useradd", pampw->pw_name, &conv, &pamh);
}

这里
pam_start (“useradd”, pampw->pw_name, &conv, &pamh);
看来就是添加用户的操作函数,然后在shadow源码中竟然无法搜到pam_start()函数实现,难道调用其他库了。果断google发现,竟然发现了大家伙:PAM。

PAM介绍

参考:
https://wiki.archlinux.org/index.php/PAM_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)
http://blog.51cto.com/essun/1391133
https://www.ibm.com/developerworks/cn/linux/l-pam/index.html
https://www.ibm.com/developerworks/cn/linux/l-cn-pam/index.html
简单来说,使用PAM之前,login(telnet,ssh)之类操作应用程序都是在/etc/passwd中查找用户名,但是这种方式非常不安全,并且验证复杂。

Linux PAM( Pluggable Authentication Modules ) 提供了一个框架,用于进行系统级的用户认证。
PAM 可以使程序开发与认证方式细节分离,而是在程序运行时调用“认证”模型完成工作。认证模型可以由本地系统管理员通过配置进行选择。

PAM提供了一系列的模块(库)以及调用接口,支持认证管理(auth),账号管理(account),会话管理(session)和密码(password)管理。(PAM介绍、接口和配置可以自行查看上面链接介绍)
所以Linux对于用户管理任务交给了Linux-PAM,自己不参与实现,所以也就不会存在adduser接口。

后记

useradd原来通过shadow包最终调用到PAM接口,由PAM服务添加用户,实现用户、用户组管理。所以即使单用户模式下(CONFIG_MULTIUSER=n),无法通过修改内核代码阻止用户态的PAM管理(想修改也力不从心),所以仍然可以添加用户。
这里写图片描述

猜你喜欢

转载自blog.csdn.net/cui841923894/article/details/82261432