Unix用户终端登录过程分析

login 之前

UNIX系统中,用户在终端登录过程大致为(Linux系统与之差异不大,主要是是配置文件组织形式有差异):

  1. 系统自举(系统启动初始化)时,内核创建进程 1 号进程init,init 进入多用户模式,读取 /etc/tty
  2. 对每一个允许登录的终端设备,init 依次调用 fork,子进程 exec getty
  3. getty 对终端设备调用 open 函数,以读写方式打开,也可能在某一等待连接线路的设备处等待
  4. 文件描述符 0 1 2 被设置指向该设备
  5. 输出 login: 字符串等待用户输入
  6. 输入成功后,按照以下方式调用login程序
1
execle("/bin/login", "login", "-p", username, (char *)0, envp);

getty 以终端名称和在 gettytab 中说明的环境变量字符串,为 login 创建一个环境参数 envp。-p 表示保留该环境参数。

上图中的所有进程目前都具有 root 权限,因为直接从 init 继承 fork,而继承了权限。并且 exec 并不改变进程 ID,下面三个进程的 ID 相同。

这部分说的终端都不是伪终端

login

接下来的 login 的工作是:

  1. getpwnam 取得用户口令文件
  2. getpass 读取用户输入密码
  3. crypt 对输入密码加密,并于 shadow 文件中的 pw_passwd 字段比较
  4. 若口令无效,exit(1)
  5. init 进程读取返回值后,再次 fork 执行 getty,重复上述过程。

以上是UNIX系统的用户登录验证过程,Linux 等后续较新的系统使用更灵活的 PAM 验证方案。

login 之后

完成 login 之后:

  1. chdir 到用户起始目录

  2. chown 改变当前终端所有权为当前登录用户

  3. 改变当前终端设备的访问权限为 “用户读写”

  4. setgid, initgroups 设置进程组 ID

  5. 用 login 得到的信息,初始化:HOME目录、shell、username、PATH 等

  6. setuid 设置进程用户 ID 为当前登录用户的 ID,并启动该用户设置的登录 shell

    1
    execl("/bin/sh", "-sh", (char *)0);

此处是由root 调用的 setuid,所以可以设置进程的:实际用户ID、有效用户ID和保存用户ID。

  1. shell 读取启动文件,.profile 之类(可能由其他名字)
  2. 在终端输出输入提示符,等待用户输入命令。

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!