如何编写登录守护进程?

如何编写登录守护进程?

关于正确启动守护进程的步骤有很多指南,例如 Stevens 的章节UNIX 环境中的高级编程。对于守护进程后应采取哪些步骤,人们还没有达成一致,而是采取另一种方式:从根进程启动用户进程。

从历史上看,登录或 telnetd 中的调用细节因平台而异。 SSH 服务器是登录守护进程的现代示例。但是,在 SSH 服务器的不同实现中,对正确步骤有不同的解释,这通常严重依赖于每个平台的步骤。

人们应该如何编写一个现代守护进程来提供登录、设置权限和上下文来运行用户的命令(也许基于远程输入)?

答案1

介绍

提供登录的守护程序的基本任务是在正确的上下文中为该系统的用户执行一个或多个命令。考虑到终端登录的历史要求以及平台之间进程属性和凭据的差异,这比看起来更难。必须精确地排序不同的步骤以确保正确的设置。

实际例子

github 项目 netlogind是一个简单的测试应用程序,演示了此类守护程序的操作。它说明了下面列出的步骤。

创建以给定用户身份运行的进程的基本步骤

struct passwd pw; //< the user
setgid(pw.pw_gid);
initgroups(pw.pw_name, pw.pw_gid);
setuid(pw.pw_uid);

(应该进行错误检查。)此外,对于高度安全关键的调用(例如 setuid),请调用getuidgeteuid随后断言已设置正确的凭据。继续在错误的 uid 下执行代码是最严重的灾难。setuid在所有支持此功能的平台上重置保存的设置用户ID。

聚丙烯酰胺

PAM 是一个 API,允许系统管理员配置应用程序如何执行身份验证和启动用户进程。 PAM 得到广泛部署。

PAM 用于通过pam_setcredpam_open_session函数建立会话环境。可移植地调用这些函数存在许多问题,并且对顺序有限制。

  • 它们必须从同一执行线程调用,就像pam_authenticate用于执行身份验证一样。一些模块的工作方式是在身份验证会话期间收集凭据,并在会话阶段使用它们执行操作(例如pam_mount)。特别是,如果从不同的进程调用内部使用的 PAM 模块(例如,某些版本或“pam_krb5”),则pam_set_data内部使用的 PAM 模块将无法工作。pam_setcred/open_sessionpam_authenticatepam_afs
  • 它们必须被称为 root。
  • 它们必须在 后调用initgroups,因为它们可用于设置额外的组成员身份。
  • pam_setcred对于应该pam_open_session调用哪个顺序存在争议。在大多数现代平台上,似乎最好先调用pam_setcredbefore pam_open_session,因为某些模块合理地要求这样做。[*]然而,有理由希望以其他方式订购。[*]无论如何,最严格的限制是 Solaris 和 HP-UX PAM 将因某些模块而失败,除非pam_setcred是第二个模块,因此在这些平台上没有太多选择(也就是说,您实际上必须遵循这些平台上记录的顺序)。 LinuxPAM 的文档说“pam_setcred 应该放在第一位”,这与 OpenPAM 的文档相反。
  • 相对于 forking的排序pam_open_session/setcred:不要在调用两个 PAM 函数 和 之间进行 fork setuidpam_limits对调用进程应用资源限制,root 很可能运行比目标用户允许运行的进程更多的进程,在这种情况下,分叉总是会失败。
  • 需要注意的 PAM 错误:某些供应商提供的模块(例如在 HP-UX 上)不会将 appdata 参数传递给对话函数。为了可移植性,请使用静态变量以避免依赖 appdata 参数。其他值得注意的现实兼容性问题:红帽#126985,红帽#127054PAM_TTYSun 上的问题(例如OpenSSH #687)、ruid限制pam_chauthtok(AIX要求ruid为0,Solaris要求ruid非零)。

设置用户进程的执行环境

closefrom

在执行用户命令之前关闭所有 fd。是否应该这样做是有争议的,因为它杀死了posix_trace(例如) 的许多实现。尽管有时被列为守护进程的步骤之一,但这是一件非常偏执的事情。不过,在创建用户会话时这样做更合理。

平台:本机运行在 Solaris、FreeBSD 上。否则,请使用 中列出的 fd(proc如果可用)进行模拟。决不要天真地尝试接近getrlimit(RLIMIT_NOFILE)或类似,因为这个数字可能太大而无法循环。

请致电:任何时候

也可以看看: Austin Group Defect Tracker,“添加 fdwalk 系统接口”

setlogin

调用setlogin(pw.pw_name)以确保会话具有与其关联的正确名称。

称呼:就在setsid;之后绝对不是来自守护进程正在运行的同一会话。以 root 身份调用。

平台:FreeBSD、Mac OSX。由于一个 uid 在密码数据库中可能有多个具有不同名称的条目,因此getpwuid(getuid())可能无法告诉您用于登录的用户名,因此getlogin必须提供另一个函数 来执行此操作。实现可以根据 utmp(不可靠)或$LOGNAME(不安全)来完成。 BSD 派生系统通过将用户名存储在每会话内核数据结构中,以理想的方式解决了该问题。 AIX 使用usrinfo(如下)解决了这个问题

usrinfo,setpcred

在 AIX 上,调用usrinfo(SETUINFO, "LOGIN=<name>\0LOGNAME=<name>\0NAME=name\0", ...).这在功能上与setloginBSD 派生系统类似。以 root 身份调用。

用于setpcred(pw.pw_name, NULL)根据用户数据库中的凭据正确设置进程限制。

环境变量

$USER, $HOME, $PATH, $LOGNAME, $LOGIN(旧版,AIX)

选修的:$MAIL$TZ

默认值可能位于/etc/environment.

需要有关环境变量的更多详细信息!编辑我!

SELinux

在 Linux 系统上设置子进程的执行上下文最好通过 PAM 完成。但是,可以手动设置它,以保证守护程序的任何权限都不会应用于用户进程,无论 PAM 配置如何。在这种情况下,它将设置打电话initgroups

审核用户id

在某些 Linux 内核上,进程会维护一个 auid,这是一个附加的用户 ID,当用户使用 切换用户 ID 时会保留该用户 ID su(1)。这允许记录所采取的操作并追踪执行该操作的用户。

auid 通常使用设置pam_loginid。为了保证即使在 PAM 未正确配置时也能设置它,请将用户的 uid 写入/proc/self/loginuid

进一步查看: “Linux 审计系统,或者谁更改了该文件?”, 赖纳·维希曼

索拉里斯contract(4)

为从守护进程启动的进程创建一个新的合约。

参见示例: “在 Solaris 10 上的新合同中创建子进程”, 弗洛里斯·布鲁诺格

setusercontext

其中许多任务都被分解到login(1)BSD 系统上的 libutil 中。请参阅setusercontext文档。

相关内容