2

原始问题

我正在尝试创建一个 Lisp 库,除其他外,它可以编辑我的系统/etc/hosts文件和 nginx 配置。我面临的问题是,因为我的 Lisp 图像作为非特权用户运行,所以我的库不能做这些事情。理想情况下,当需要 root 权限时,我能够为我的库提供密码,以便它可以暂时提高其访问权限以完成任务。唉,我还没有找到任何与sudo. 有吗?我是以错误的方式接近这个吗?我该如何解决这个问题?

在代码中,我想要做的基本上是这样的:

(with-sudo (:username "root" :password (securely-read-line))
  (with-open-file (f "/etc/hosts" :direction :output :if-exists :append)
    (format f "127.0.0.1 mywebsite.local~%")))

澄清

我在 OS X 上使用 SBCL。我正在尝试创建一个库,它基本上是网站快速项目的专业化。目前,每次我在本地计算机上设置一个新的 Web 项目时,我都必须编辑分布在我系统中的配置文件。我希望尽可能多地自动化,并且我希望能够在我通常打开并连接到单个 SBCL 实例的 SLIME 会话中执行此操作。

以下是其他一些注意事项:

  1. 我不希望我的 SBCL 主要实例成为setuidroot
  2. 我不介意生成一个新进程,无论是小型 C 程序还是加载几行代码然后退出的 SBCL 裸实例。
  3. 我想使用保持 Lisp 与 C 的比率尽可能高。
  4. 某些文本编辑器(例如 TextMate)在访问需要更高权限才能查看的文件时会提示输入用户名和密码,从而提供相应的访问权限。我想知道如何让我的 Lisp 库做同样的事情?
  5. 起初我无法sudo(实际程序本身)在 SBCL 中工作:

    (inferior-shell:run/ss '("sudo" "ls" ".")) ;; how can I pass sudo a password?

    现在看起来可以使用sudo's-S选项(谢谢 JustAnotherCurious)。我想我可能会走这条路;这绝对是我现在所倾向的。

不管怎样,谢谢大家!我从你们所有人那里学到了很多东西。

4

5 回答 5

5

如果您需要在您的进程中获得 root 访问权限,您首先需要以 root 身份启动您的 Lisp 进程。回想起来,通常不可能使非根进程运行。

幸运的是,Unix 有一种机制允许进程在运行时在 root 和非 root 权限之间切换。该机制称为有效用户 ID。以 root 身份运行的进程可以使用系统调用切换到非 root 有效 uid seteuid,也可以通过这种方式切换回“成为”root。

当然,如果您以 root 身份启动 Lisp 进程,则该进程可以完全控制机器,并且根据您正在处理的数据和机器,您需要考虑由此打开的可能的安全漏洞。幸运的是,在 Lisp 中很难产生缓冲区溢出,所以从这个角度来看,你处于更安全的一边:)

对系统调用接口的访问在 Common Lisp 中没有标准化,但大多数实现都有系统的本机接口,如果你计划让你的程序在基于 Linux/Unix 的 Lisps 之间移植,你也可以使用 CFFI

这是以 root 身份运行的 SBCL 的成绩单,演示了seteuid的使用:

CL-USER> (defun write-file-in-filesystem-root ()
           (handler-case
               (with-open-file (f "/only-root-may-write-to-root"
                                  :direction :output
                                  :if-exists :supersede)
                 (write "hello" :stream f))
             (error (e) (format t "error: ~A~%" e))))
WRITE-FILE-IN-FILESYSTEM-ROOT
CL-USER> (sb-posix:seteuid 0)
0
CL-USER> (write-file-in-filesystem-root)
"hello"
CL-USER> (sb-posix:seteuid 1000)
0
CL-USER> (write-file-in-filesystem-root)
error: error opening #P"/only-root-may-write-to-root": Permission denied
NIL
CL-USER> (sb-posix:seteuid 0)
0
CL-USER> (write-file-in-filesystem-root)
"hello"
CL-USER> (delete-file "/only-root-may-write-to-root")
T

如果您只需要访问受保护的文件,如果保持特定于 OSX 是可以接受的,并且如果您希望用户使用标准身份验证请求程序进行身份验证,则可以使用特定于 OSX的authopen命令。

于 2013-03-29T10:58:15.067 回答
3

首先,您不能也不应该能够违反 Linux 权限系统。如果有某种方法可以滥用权限和身份验证系统,那将是一个巨大的安全漏洞:实际上,每个人都将拥有 root 访问权限。如果您想要这种不安全感,请返回 MSDOS!

然后,您可以配置您的系统以授予对几乎所有内容的 root 访问权限。您可以sudo将命令或super命令配置为不询问任何密码(我实际上是在某些机器上故意这样做的,但我确实知道这是 - 或可能被滥用为 - 一个安全漏洞)。这意味着信任运行该  sudo命令的实体(人或程序)。

此外,某些程序(例如/bin/login, /usr/bin/sudo, /usr/bin/super....)可以是setuid(另请阅读credentials(7)execve(2)手册页),您的 Common Lisp 实现可能为setuid(2)setreuid( 2)系统调用(或者你可以自己编写调用C系统调用的包装器)。

但是,您似乎只是想将127.0.0.1 mywebsite.local条目添加到/etc/hosts; 为什么不能将这样的条目作为先决条件(因此,在安装 Common Lisp 软件的人工过程中,您需要 - 对人工系统管理员 - 应该添加这样的条目)。然后,您所要做的就是以某种方式将mywebsite.local名称传递给您的 Common Lisp 程序,这应该很容易。

我会警告不要使用用 Common Lisp 编写的setuid二进制程序。因为,除非您采取特殊预防措施,否则大多数 Common Lisp 二进制文件都包含(间接通过eval)整个 Common Lisp 编译器。并且授予对像 Common Lisp(例如 SBCL)这样的动态且强大的编译器的 root 访问权限是一个安全漏洞。

如果您坚持以mywebsite.local编程方式/etc/hosts从您的 Common Lisp 库中添加,您可以编写一个小型C帮助程序,其可执行文件例如/usr/local/libexec/helperbinary是root- setuid(即您chown root /usr/local/libexec/helperbinary安装chmod u+s /usr/local/libexec/helperbinary时显然需要 root 才能运行这些和),其中正是这样做的,并且只能通过适当的技巧从您的 Common Lisp 二进制文件中启动。在用C编写那个微小的帮助程序时要非常小心,例如阅读Advanced Linux Programming ,并多次校对你的C代码(你很容易在setuid程序中制造安全漏洞)。chownchmod

于 2013-03-29T09:44:45.650 回答
2

问题是您没有“提高”访问权限:您从超级用户权限开始,当您不再需要它们时将其删除(sudo您所指的程序被标记为setuid root来实现这一点)。

因此,您想要实现的目标需要以 root 身份启动您的 LISP 解释器,然后让它派生另一个解释器进程并让该进程立即放弃其超级用户权限。从那里开始,非特权进程可以像往常一样运行您的代码,并使用IPC到特权进程来拥有它,例如,写入/etc/hosts.

不幸的是,这不会是微不足道的。

于 2013-03-29T09:13:31.800 回答
2

如果您具有 root 访问权限的操作仅限于一些小而简单的操作,并且您可以编写一些特定于平台的代码,那么,我认为它会起作用,您可以尝试使用 sudo 从代码中执行 bash(或其他 shell)命令-S。

来自须藤人

The -S (stdin) option causes sudo to read the password from 
the standard input instead of the terminal device. 
The password must be followed by a newline character.

但我认为最好不要做这样的事情。

于 2013-03-29T09:52:06.477 回答
0

如果您只是想以 root 身份使用命令执行某些任务,则无需以 root 身份启动 Lisp 解释器,就像您的 bash/zsh 未以 root 身份启动但仍然可以sudo正常运行一样。

如何传递 sudo 密码?

如果您正在使用inferior-shell

(inferior-shell:run/interactive '("sudo" "ls" "."))

否则,使用普通 UIOP:

(uiop:run-program '("sudo" "whoami")
                  :input :interactive
                  :output :interactive)

如果你确实想运行 Lisp 代码而不仅仅是一个外部命令,你将不得不做一些像这样丑陋的事情:

(string-trim
 '(#\space #\return #\linefeed)
 (uiop:run-program
  ;; Notice how "sbcl" is hardcoded. Making this portable is also non-trivial.
  '("sudo" "sbcl" "--noinform" "--non-interactive" "--eval" "(print (+ 1 2))")
  :input :interactive
  :output :interactive))

通过足够的努力(以某种方式捕获当前环境并在新进程中重新建立它,也许处理输出值以便您可以例如列出特权目录并将其作为路径列表返回......),可能有可能实现一个with-sudo宏,透明地要求用户获得更多权限以以 root 身份运行更多 Lisp 代码。但这不是微不足道的。

于 2021-12-08T21:42:38.760 回答