7.5 Setting the Time(设置时间)
Unix machines depend on accurate timekeeping. The kernel maintains the system clock, which is the clock that is consulted when you run commands like date. You can also set the system clock using the date command, but it’s usually a bad idea to do so because you’ll never get the time exactly right. Your system clock should be as close to the correct time as possible.
Unix机器依赖准确的时间记录。内核维护着系统时钟,当你运行像date这样的命令时会查询该时钟。
你也可以使用date命令设置系统时钟,但通常这样做是不明智的,因为你永远无法完全准确地获得时间。你的系统时钟应尽可能接近正确的时间。
PC hardware has a battery-backed real-time clock (RTC). The RTC isn’t the best clock in the world, but it’s better than nothing. The kernel usually sets its time based on the RTC at boot time, and you can reset the system clock to the current hardware time with hwclock. Keep your hardware clock in Universal Coordinated Time (UTC) in order to avoid any trouble with time zone or daylight savings time corrections. You can set the RTC to your kernel’s UTC clock using this command:
PC硬件有一个带有电池备份的实时时钟(RTC)。
RTC并不是世界上最好的时钟,但总比没有好。
内核通常在启动时根据RTC设置时间,并且你可以使用hwclock将系统时钟重置为当前硬件时间。
为了避免与时区或夏令时校正引起的任何问题,你应将硬件时钟设置为协调世界时(UTC)。
你可以使用以下命令将RTC设置为内核的UTC时钟:
代码语言:javascript复制# hwclock --hctosys --utc
Unfortunately, the kernel is even worse at keeping time than the RTC, and because Unix machines often stay up for months or years on a single boot, they tend to develop time drift. Time drift is the current difference between the kernel time and the true time (as defined by an atomic clock or another very accurate clock).
不幸的是,内核在时间保持方面甚至比RTC更差,因为Unix机器通常在单次启动上运行数月或数年,它们往往会出现时间漂移。
时间漂移是内核时间与真实时间(由原子钟或其他非常准确的时钟定义)之间的差异。
You should not try to fix the drift with hwclock because time-based system events can get lost or mangled. You could run a utility like adjtimex to smoothly update the clock, but usually it’s best to keep your system time correct with a network time daemon (see 7.5.2 Network Time).
你不应该尝试使用hwclock来修复漂移,因为基于时间的系统事件可能会丢失或损坏。
补充(来自网络): 实际上,linux系统有两个时钟:一个是由主板电池驱动的“Real Time Clock”也叫做RTC或者叫CMOS时钟,硬件时钟。当操作系统关机的时候,用这个来记录时间,但是对于运行的系统是不用这个时间的。 另一个时间是 “System clock”也叫内核时钟或者软件时钟,是由软件根据时间中断来进行计数的,内核时钟在系统关机的情况下是不存在的,所以,当操作系统启动的时候,内核时 钟是要读取RTC时间来进行时间同步(有些情况下,内核时钟也可以通过ntp服务器来读取时间) 这两个时钟通常会有一些误差,所以长时间可以导致 这两个时钟偏离的比较多,最简单的保持两个时间同步的方法是用软件测出他们之间的误差率,然后用软件进行修正。在每次重新启动系统的时候,系统都会用 hwclock命令对时间进行同步。如果内核时钟在每一个时间中断都快或者慢的话,可以用adjtimex命令进行调整,使得RTC和内核时间走的快慢一 致。 linux的内核时间实际上是记录从1970年1月1日距离现在的秒数,并且以GMT(格林尼治时间)(或者叫UTC- Coordinated Universal Time)为标准,UTC是不随着DST(夏令时)变换,需要有变化的是由应用程序自身来完成时间的转换。 通常,本地时间=UTC时间 时区在安装linux系统的时候,可能正确设置了时区,但由于某些原因需要调整的时候,请参考以下方法利用tzselect命令可以修改系统的当前时区,配置文件储存在/etc/sysconfig/clock文件中(Redhat As3)
你可以运行像adjtimex这样的实用程序来平滑更新时钟,但通常最好使用网络时间守护程序来保持系统时间准确(参见7.5.2 网络时间)。
7.5.1 Kernel Time Representation and Time Zones(内核时间表示法和时区)
The kernel’s system clock represents the current time as the number of seconds since 12:00 midnight on January 1, 1970, UTC. To see this number at the moment, run:
内核的系统时钟将当前时间表示为自1970年1月1日午夜12:00以来的秒数,以协调世界时(UTC)为准。要立即查看此数字,请运行:
代码语言:javascript复制$ date %s
To convert this number into something that humans can read, user-space programs change it to local time and compensate for daylight savings time and any other strange circumstances (such as living in Indiana). The local time zone is controlled by the file /etc/localtime. (Don’t bother trying to look at it; it’s a binary file.)
为了将这个数字转换成人类可读的形式,用户空间程序将其转换为本地时间,并补偿夏令时和其他奇怪的情况(比如生活在印第安纳州)。
本地时区由文件/etc/localtime控制。(不要费心去查看它;它是一个二进制文件。)
The time zone files on your system are in /usr/share/zoneinfo. You’ll find that this directory contains a lot of time zones and a lot of aliases for time zones. To set your system’s time zone manually, either copy one of the files in /usr/share/zoneinfo to /etc/localtime (or make a symbolic link) or change it with your distribution’s time zone tool. (The command-line program tzselect may help you identify a time zone file.)
您系统上的时区文件位于/usr/share/zoneinfo。
您会发现这个目录包含许多时区和许多时区的别名。
要手动设置系统的时区,可以将/usr/share/zoneinfo中的一个文件复制到/etc/localtime(或创建一个符号链接),或者使用您发行版的时区工具进行更改。
(命令行程序tzselect可能会帮助您识别一个时区文件。)
To use a time zone other than the system default for just one shell session, set the TZ environment variable to the name of a file in /usr/share/ zoneinfo and test the change, like this:
要在仅对一个shell会话使用非系统默认时区,请将TZ环境变量设置为/usr/share/zoneinfo
中的文件名,并测试更改,如下所示:
$ export TZ=US/Central
$ date
As with other environment variables, you can also set the time zone for the duration of a single command like this:
与其他环境变量一样,您也可以像这样为单个命令的持续时间设置时区:
代码语言:javascript复制$ TZ=US/Central date
7.5.2 Network Time(网络时间)
If your machine is permanently connected to the Internet, you can run a Network Time Protocol (NTP) daemon to maintain the time using a remote server. Many distributions have built-in support for an NTP daemon, but it may not be enabled by default. You might need to install an ntpd package to get it to work.
如果您的机器长期连接到互联网,您可以运行一个网络时间协议(NTP)守护进程,使用远程服务器来维护时间。
许多发行版都内置了对NTP守护进程的支持,但默认情况下可能未启用。
您可能需要安装一个ntpd软件包才能使其正常工作。
If you need to do the configuration by hand, you’ll find help on the main NTP web page at http://www.ntp.org/, but if you’d rather not read through the mounds of documentation there, do this:
如果您需要手动进行配置,您可以在主NTP网页 上找到帮助,但如果您不想阅读那里的大量文档,可以按照以下步骤操作:
- Find the closest NTP time server from your ISP or from the ntp.org web page.
- Put that time server in /etc/ntpd.conf.
- Run ntpdate server at boot time.
- Run ntpd at boot time, after the ntpdate command.
- 从您的ISP或ntp.org网页上找到最近的NTP时间服务器。
- 将该时间服务器放入/etc/ntpd.conf文件中。
- 在启动时运行ntpdate服务器。
- 在启动时运行ntpd命令,该命令在ntpdate命令之后运行。
If your machine doesn’t have a permanent Internet connection, you can use a daemon like chronyd to maintain the time during disconnections.
如果您的机器没有长期的互联网连接,您可以使用像chronyd这样的守护进程来在断开连接期间维护时间。
You can also set your hardware clock based on the network time in order to help your system maintain time coherency when it reboots. (Many distributions do this automatically.) To do so, set your system time from the network with ntpdate (or ntpd), then run the command you saw back in Note:
您还可以根据网络时间设置您的硬件时钟,以帮助系统在重新启动时保持时间的一致性。
(许多发行版会自动执行此操作。)
要这样做,请使用ntpdate(或ntpd)从网络设置系统时间,然后运行您在注释中看到的命令。
代码语言:javascript复制# hwclock --systohc –-utc
7.6 Scheduling Recurring Tasks with cron(使用 cron 调度循环任务)
The Unix cron service runs programs repeatedly on a fixed schedule. Most experienced administrators consider cron to be vital to the system because it can perform automatic system maintenance. For example, cron runs log file rotation utilities to ensure that your hard drive doesn’t fill up with old log files. You should know how to use cron because it’s just plain useful.
Unix cron服务按固定的时间表重复运行程序。
大多数经验丰富的管理员认为cron对系统至关重要,因为它可以执行自动系统维护。
例如,cron运行日志文件轮转工具,以确保您的硬盘不会被旧日志文件填满。
您应该知道如何使用cron,因为它非常实用。
You can run any program with cron at whatever times suit you. The program running through cron is called a cron job. To install a cron job, you’ll create an entry line in your crontab file, usually by running the crontab command. For example, the crontab entry schedules the /home/juser/bin/spmake command daily at 9:15 AM:
您可以在cron中根据自己的时间安排运行任何程序。
通过cron运行的程序称为cron作业。
要安装cron作业,您需要在crontab文件中创建一个条目行,通常通过运行crontab命令来完成。
例如,以下crontab条目将在每天上午9:15运行/home/juser/bin/spmake命令:
代码语言:javascript复制15 09 * * * /home/juser/bin/spmake
The five fields at the beginning of this line, delimited by whitespace, specify the scheduled time (see also Figure 7-3). The fields are as follows, in order:
此行开头的五个字段由空格分隔,指定了计划的时间(另请参见图7-3)。
字段依次为:
o Minute (0 through 59). The cron job above is set for minute 15. o Hour (0 through 23). The job above is set for the ninth hour. o Day of month (1 through 31). o Month (1 through 12). o Day of week (0 through 7). The numbers 0 and 7 are Sunday
o 分钟(0到59)。上述cron作业设置为15分钟。 o 小时(0到23)。上述作业设置为第9小时。 o 日期(1到31)。 o 月份(1到12)。 o 星期几(0到7)。数字0和7表示星期天。
Figure 7-3. An entry in the crontab file
Figure 7-3. An entry in the crontab file
图7-3. crontab文件中的一个条目
A star (*) in any field means to match every value. The preceding example runs spmake daily because the day of month, month, and day of week fields are all filled with stars, which cron reads as “run this job every day, of every month, of every week.”
在任何字段中的星号(*)表示匹配每个值。
前面的示例中,spmake每天运行一次,因为月份、星期和日期字段都填满了星号,cron将其解读为“每天,每月,每周运行此作业”。
To run spmake only on the 14th day of each month, you would use this crontab line:
要仅在每月的第14天运行spmake,您可以使用以下crontab行:
代码语言:javascript复制15 09 14 * * /home/juser/bin/spmake
You can select more than one time for each field. For example, to run the program on the 5th and the 14th day of each month, you could enter 5,14 in the third field:
您可以为每个字段选择多个时间。例如,要在每月的 5 日和 14 日运行程序,您可以在第三个字段中输入 5、14:
代码语言:javascript复制15 09 5,14 * * /home/juser/bin/spmake
NOTE If the cron job generates standard output or an error or exits abnormally, cron should mail this information to you. Redirect the output to /dev/null or some other log file if you find the email annoying. 注意:如果cron作业生成标准输出、错误信息或异常退出,cron应该将这些信息发送给您。 如果您觉得邮件很烦人,请将输出重定向到/dev/null或其他日志文件。
The crontab(5) manual page provides complete information on the crontab format.
crontab(5)手册页面提供了关于crontab格式的完整信息。
7.6.1 Installing Crontab Files(安装 Crontab 文件)
Each user can have his or her own crontab file, which means that every system may have multiple crontabs, usually found in /var/spool/cron/crontabs. Normal users can’t write to this directory; the crontab command installs, lists, edits, and removes a user’s crontab. 、 每个用户都可以拥有自己的crontab文件,这意味着每个系统可能有多个crontab文件,通常存放在/var/spool/cron/crontabs目录中。
普通用户无法写入该目录;crontab命令用于安装、列出、编辑和删除用户的crontab。
The easiest way to install a crontab is to put your crontab entries into a file and then use crontab file to install file as your current crontab. The crontab command checks the file format to make sure that you haven’t made any mistakes. To list your cron jobs, run crontab -l. To remove the crontab, use crontab -r.
安装crontab的最简单方法是将crontab条目放入一个文件中,然后使用crontab file命令将该文件安装为当前的crontab。
crontab命令会检查文件格式,以确保没有发生任何错误。
要列出cron作业,请运行crontab -l命令。要删除crontab,请使用crontab -r命令。
However, after you’ve created your initial crontab, it can be a bit messy to use temporary files to make further edits. Instead, you can edit and install your crontab in one step with the crontab -e command. If you make a mistake, crontab should tell you where the mistake is and ask if you want to try editing again.
然而,在创建了初始的crontab之后,使用临时文件进行进一步编辑可能会有些混乱。
相反,您可以使用crontab -e命令一次性编辑和安装您的crontab。
如果出现错误,crontab会告诉您错误的位置,并询问是否要重新编辑。
7.6.2 System Crontab Files(系统 Crontab 文件)
Rather than use the superuser’s crontab to schedule recurring system tasks, Linux distributions normally have an /etc/crontab file. Don’t use crontab to edit this file, because this version has an additional field inserted before the command to run—the user that should run the job. For example, this cron job defined in /etc/crontab runs at 6:42 AM as the superuser (root, shown at ➊):
与其使用超级用户的crontab来安排定期系统任务,Linux发行版通常会有一个/etc/crontab文件。
不要使用crontab来编辑此文件,因为这个版本在要运行的命令之前插入了一个额外的字段——应该运行该作业的用户。
例如,在/etc/crontab中定义的这个cron作业将以超级用户(root,在➊处显示)身份在上午6:42运行:
代码语言:javascript复制42 6 * * * root➊ /usr/local/bin/cleansystem > /dev/null 2>&1
NOTE Some distributions store system crontab files in the /etc/cron.d directory. These files may have any name, but they have the same format as /etc/crontab. 注意:一些发行版将系统crontab文件存储在/etc/cron.d目录中。这些文件可以有任何名称,但格式与/etc/crontab相同。
7.6.3 The Future of cron(cron 的未来)
The cron utility is one of the oldest components of a Linux system; it’s been around for decades (predating Linux itself), and its configuration format hasn’t changed much for many years. When something gets to be this old, it becomes fodder for replacement, and there are efforts underway to do exactly that.
cron实用程序是Linux系统中最古老的组件之一;它已经存在了几十年(早于Linux本身),其配置格式多年来几乎没有改变。
当一样东西变得如此古老时,它就成为替代的素材,目前正在进行相关工作。
The proposed replacements are actually just parts of the newer versions of init: For systemd, there are timer units, and for Upstart, the idea is to be able to create recurring events to trigger jobs. After all, both versions of init can run tasks as any user, and they offer certain advantages, such as custom logging.
提议的替代方案实际上只是较新版本的init的一部分:对于systemd,有定时器单元;对于Upstart,想法是能够创建定期事件来触发作业。
毕竟,两个版本的init都可以以任何用户身份运行任务,并且它们提供了某些优势,如自定义日志记录。
However, the reality is that neither systemd nor Upstart currently has all of the capabilities of cron. Furthermore, when they do become capable, backward compatibility will be necessary to support everything that relies on cron. For these reasons, it’s unlikely that the cron format will go away anytime soon.
然而,事实是,无论是systemd还是Upstart目前都没有cron的所有功能。
此外,当它们确实具备这些功能时,向后兼容性将是必要的,以支持依赖于cron的所有内容。
出于这些原因,cron格式不太可能很快消失。
7.7 Scheduling One-Time Tasks with at(安排一次性任务)
To run a job once in the future without using cron, use the at service. For example, to run myjob at 10:30 PM, enter this command:
要在将来的某个时间运行一次作业而不使用cron,请使用at服务。
例如,要在晚上10:30运行myjob,请输入以下命令:
代码语言:javascript复制$ at 22:30
at> myjob
End the input with CTRL-D. (The at utility reads the commands from the standard input.)
使用CTRL-D结束输入。
(at实用程序从标准输入读取命令。)
To check that the job has been scheduled, use atq. To remove it, use atrm. You can also schedule jobs days into the future by adding the date in DD.MM.YY format, for example, at 22:30 30.09.15.
要检查作业是否已计划,请使用atq。
要删除作业,请使用atrm。
您还可以通过在DD.MM.YY格式中添加日期来安排未来几天的作业,例如at 22:30 30.09.15。
There isn’t much else to the at command. Though at isn’t used that often, it can be handy for that odd time when you need to tell the system to shut down in the future.
at命令没有太多其他用途。虽然at并不经常使用,但在需要告诉系统在将来关闭的奇怪时间时,它可能会很方便。
7.8 Understanding User IDs and User Switching(了解用户 ID 和用户切换)
We’ve discussed how setuid programs such as sudo and su allow you to change users, and we’ve mentioned system components like login that control user access. Perhaps you’re wondering how these pieces work and what role the kernel plays in user switching
我们已经讨论了像sudo和su这样的setuid程序如何允许您更改用户,并提到了控制用户访问的登录等系统组件。
也许您想知道这些部分是如何工作的,内核在用户切换中扮演了什么角色。
There are two ways to change a user ID, and the kernel handles both. The first is with a setuid executable, which is covered in 2.17 File Modes and Permissions. The second is through the setuid() family of system calls. There are a few different versions of this system call to accommodate the various user IDs associated with a process, as you’ll learn in 7.8.1 Process Ownership, Effective UID, Real UID, and Saved UID.
有两种方法可以更改用户ID,内核处理这两种方法。
第一种是使用setuid可执行文件,这在2.17文件模式和权限中有介绍。
第二种是通过setuid()系统调用系列。
有几个不同版本的这个系统调用来适应与进程关联的各种用户ID,您将在7.8.1进程所有权、有效UID、真实UID和保存UID中了解到。
The kernel has basic rules about what a process can or can’t do, but here are the three basics:
内核对进程能够做什么或不能做什么有基本规则,但以下是三个基本原则:
o A process running as root (userid 0) can use setuid() to become any other user. o A process not running as root has severe restrictions on how it may use setuid(); in most cases, it cannot. o Any process can execute a setuid program as long as it has adequate file permissions.
o 以root(用户ID为0)身份运行的进程可以使用setuid()成为任何其他用户。
o 非以root身份运行的进程在使用setuid()时有严格的限制;在大多数情况下,它是不允许的。
o 只要具有足够的文件权限,任何进程都可以执行setuid程序。
NOTE User switching has nothing to do with passwords or usernames. Those are strictly user-space concepts, as you first saw in the /etc/passwd file in 7.3.1 The /etc/passwd File. You’ll learn more details about how this works in 7.9.1 Using Libraries for User Information. 注意用户切换与密码或用户名无关。 这些严格来说是用户空间的概念,正如您在7.3.1 /etc/passwd文件中首次看到的那样。 您将在7.9.1使用库获取用户信息中了解更多详细信息。
7.8.1 Process Ownership, Effective UID, Real UID, and Saved UID
Our discussion of user IDs so far has been simplified. In reality, every process has more than one user ID. We’ve described the effective user ID (euid), which defines the access rights for a process. A second user ID, the real user ID (ruid), indicates who initiated a process. When you run a setuid program, Linux sets the effective user ID to the program’s owner during execution, but it keeps your original user ID in the real user ID.
我们之前对用户ID的讨论已经进行了简化。
实际上,每个进程都有多个用户ID。
我们已经描述了有效用户ID(euid),它定义了进程的访问权限。
第二个用户ID,真实用户ID(ruid),表示启动进程的用户。
当您运行一个setuid程序时,Linux在执行过程中将有效用户ID设置为程序的所有者,但保留您的原始用户ID作为真实用户ID。
On modern systems, the difference between the effective and real user IDs is confusing, so much so that a lot of documentation regarding process ownership is incorrect.
在现代系统中,有效用户ID和真实用户ID之间的区别很令人困惑,以至于关于进程所有权的很多文档都是错误的。
Think of the effective user ID as the actor and the real user ID as the owner. The real user ID defines the user that can interact with the running process—most significantly, which user can kill and send signals to a process. For example, if user A starts a new process that runs as user B (based on setuid permissions), user A still owns the process and can kill it.
将有效用户ID视为演员,将真实用户ID视为所有者。
真实用户ID定义了可以与运行中的进程交互的用户,最重要的是,哪个用户可以终止进程和发送信号给进程。
例如,如果用户A启动一个以用户B身份运行的新进程(基于setuid权限),用户A仍然拥有该进程并可以终止它。
On normal Linux systems, most processes have the same effective user ID and real user ID. By default, ps and other system diagnostic programs show the effective user ID. To view both the effective and real user IDs on your system, try this, but don’t be surprised if you find that the two user ID columns are identical for all processes on your system:
在正常的Linux系统中,大多数进程的有效用户ID和真实用户ID相同。
默认情况下,ps和其他系统诊断程序显示有效用户ID。
要在您的系统上查看有效用户ID和真实用户ID,请尝试以下操作,但如果您发现所有进程的两个用户ID列相同,也不要感到惊讶:
代码语言:javascript复制$ ps -eo pid,euser,ruser,comm
To create an exception just so that you can see different values in the columns, try experimenting by creating a setuid copy of the sleep command, running the copy for a few seconds, and then running the preceding ps command in another window before the copy terminates.
为了在列中看到不同的值,可以尝试创建一个setuid的sleep命令的副本,并在副本运行几秒钟后,在另一个窗口中运行前面的ps命令,然后再副本终止之前。
To add to the confusion, in addition to the real and effective user IDs, there is also a saved user ID (which is usually not abbreviated). A process can switch its effective user ID to the real or saved user ID during execution. (To make things even more complicated, Linux has yet another user ID: the file system user ID fsuid, which defines the user accessing the filesystem but is rarely used.)
为了增加混淆,除了真实用户ID和有效用户ID之外,还有一个保存的用户ID(通常不缩写)。
进程可以在执行过程中将其有效用户ID切换为真实用户ID或保存的用户ID。
(为了让事情更加复杂,Linux还有另一个用户ID:文件系统用户ID fsuid,它定义了访问文件系统的用户,但很少使用。)
Typical Setuid Program Behavior(典型的 Setuid 程序行为)
The idea of the real user ID might contradict your previous experience. Why don’t you have to deal with the other user IDs very frequently? For example, after starting a process with sudo, if you want to kill it, you still use sudo; you can’t kill it as your own regular user. Shouldn’t your regular user be the real user ID in this case, giving you the correct permissions?
真实用户ID的概念可能与您之前的经验相矛盾。为什么您不经常处理其他用户ID呢?
例如,在使用sudo启动进程后,如果您想要终止它,您仍然需要使用sudo;您不能使用您自己的普通用户身份终止它。
在这种情况下,您的普通用户不应该是真实用户ID吗?从而给您正确的权限?
The cause of this behavior is that sudo and many other setuid programs explicitly change the effective and real user IDs with one of the setuid() system calls. These programs do so because there are often unintended side effects and access problems when all of the user IDs do not match.
这种行为的原因是sudo和许多其他setuid程序使用setuid()系统调用明确地更改有效和真实用户ID。
这些程序这样做是因为当所有用户ID不匹配时,往往会出现意外的副作用和访问问题。
NOTE If you’re interested in the details and rules regarding user ID switching, read the setuid(2) manual page and check the other manual pages listed in the SEE ALSO section. There are many different system calls for diverse situations. 注意:如果您对用户ID切换的细节和规则感兴趣,请阅读setuid(2)手册页,并检查SEE ALSO部分列出的其他手册页。有许多不同的系统调用适用于不同的情况。
Some programs don’t like to have a real user ID of root. To prevent sudo from changing the real user ID, add this line to your /etc/sudoers file (and beware of side effects on other programs you want to run as root!):
有些程序不喜欢具有root的真实用户ID。为了防止sudo更改真实用户ID,请将以下行添加到您的/etc/sudoers文件中(并注意对您希望以root身份运行的其他程序可能产生的副作用!):
代码语言:javascript复制Defaults stay_setuid
Security Implications(对安全的影响)
Because the Linux kernel handles all user switches (and as a result, file access permissions) through setuid programs and subsequent system calls, systems developers and administrators must be extremely careful with two things:
因为Linux内核通过setuid程序和随后的系统调用来处理所有用户切换(以及文件访问权限),所以系统开发人员和管理员必须非常小心处理两个方面:
o The programs that have setuid permissions o What those programs do
o 具有setuid权限的程序
o 这些程序的功能
If you make a copy of the bash shell that is setuid root, any local user can execute it and have complete run of the system. It’s really that simple. Furthermore, even a special-purpose program that is setuid root can pose a danger if it has bugs. Exploiting weaknesses in programs running as root is a primary method of systems intrusion, and there are too many such exploits to count.
如果你复制了一个设置了setuid root权限的bash shell,任何本地用户都可以执行它并完全控制系统。
就是这么简单。
此外,即使是设置了setuid root权限的特定目的程序,如果存在漏洞,也可能构成危险。
利用以root身份运行的程序中的弱点是系统入侵的主要方法,而此类漏洞利用的数量多得无法计数。
Because there are so many ways to break into a system, preventing intrusion is a multifaceted affair. One of the most essential ways to keep unwanted activity off your system is to enforce user authentication with usernames and passwords.
由于有很多方法可以侵入系统,防止入侵是一项多方面的工作。
保证用户身份验证使用用户名和密码是保持系统不受非法活动侵扰的最基本方式之一。
7.9 User Identification and Authentication(用户识别和认证)
A multiuser system must provide basic support for user security in terms of identification and authentication. The identification portion of security answers the question of who users are. The authentication piece asks users to prove that they are who they say they are. Finally, authorization is used to define and limit what users are allowed to do.
一个多用户系统必须在身份识别和认证方面为用户提供基本的安全支持。
安全的身份识别部分回答了用户是谁的问题。认证部分要求用户证明他们是他们所声称的人。
最后,授权用于定义和限制用户被允许做什么。
When it comes to user identification, the Linux kernel knows only the numeric user IDs for process and file ownership. The kernel knows authorization rules for how to run setuid executables and how user IDs may run the setuid() family of system calls to change from one user to another. However, the kernel does not know anything about authentication: usernames, passwords, and so on. Practically everything related to authentication happens in user space.
当涉及到用户身份识别时,Linux内核只知道进程和文件所有权的数字用户ID。
内核知道如何运行setuid可执行文件的授权规则,以及用户ID如何运行setuid()系统调用来从一个用户切换到另一个用户。
然而,内核对认证方面一无所知:用户名、密码等等。
实际上,与认证相关的几乎所有事情都发生在用户空间。
We discussed the mapping between user IDs and passwords in 7.3.1 The /etc/passwd File; now we’ll explain how user processes access this mapping. We’ll begin with an oversimplified case, in which a user process wants to know its username (the name corresponding to the effective user ID). On a traditional Unix system, a process could do something like this to get its username:
我们在7.3.1节“/etc/passwd文件”中讨论了用户ID和密码之间的映射;现在我们将解释用户进程如何访问这个映射。
我们将从一个过于简化的情况开始,即一个用户进程想要知道它的用户名(对应有效用户ID的名称)。
在传统的Unix系统上,一个进程可以通过以下方式获取它的用户名:
- The process asks the kernel for its effective user ID with the geteuid() system call.
- The process opens the /etc/passwd file and starts reading at the beginning.
- The process reads a line of the /etc/passwd file. If there’s nothing left to read, the process has failed to find the username.
- The process parses the line into fields (breaking out everything between the colons). The third field is the user ID for the current line.
- The process compares the ID from Step 4 to the ID from Step 1. If they’re identical, the first field in Step 4 is the desired username, and the process can stop searching and use this name.
- The process moves on to the next line in /etc/passwd and goes back to Step 3.
- 进程使用geteuid()系统调用询问内核它的有效用户ID。
- 进程打开/etc/passwd文件并从开头开始读取。
- 进程读取/etc/passwd文件的一行。如果没有剩下的内容可读,进程将无法找到用户名。
- 进程将该行解析为字段(将冒号之间的内容分隔出来)。第三个字段是当前行的用户ID。
- 进程将步骤4中的ID与步骤1中的ID进行比较。如果它们相同,步骤4中的第一个字段就是所需的用户名,进程可以停止搜索并使用这个名称。
- 进程继续读取/etc/passwd中的下一行,并回到步骤3。
This is a long procedure that’s usually much more complicated in reality.
这是一个通常在现实中更加复杂的过程。
7.9.1 Using Libraries for User Information(利用图书馆获取用户信息)
If every developer who needed to know the current username had to write all of the code you’ve just seen, the system would be a horrifyingly disjointed, buggy, bloated, and unmaintainable mess. Fortunately, we can use standard libraries to perform repetitive tasks, so all you’d normally need to do to get a username is call a function like getpwuid() in the standard library after you have the answer from geteuid(). (See the manual pages for these calls for more on how they work.)
如果每个需要知道当前用户名的开发者都必须编写你刚才看到的所有代码,那么系统将变得非常不连贯、有错误、臃肿且难以维护。
幸运的是,我们可以使用标准库来执行重复的任务,所以通常你只需要在从geteuid()获取答案后调用一个像getpwuid()这样的函数就可以获得用户名。
(有关这些调用的更多信息,请参阅相关手册页。)
When the standard library is shared, you can make significant changes to the implementation without changing any other program. For example, you can move away from using /etc/passwd for your users and use a network service such as LDAP instead.
当共享标准库时,你可以对实现进行重大更改,而不会影响其他任何程序。
例如,你可以放弃使用/etc/passwd作为用户的存储方式,而改用网络服务,如LDAP。
This approach has worked well for identifying usernames associated with user IDs, but passwords have proven more troublesome. 7.3.1 The /etc/passwd File describes how, traditionally, the encrypted password was part of /etc/passwd, so if you wanted to verify a password that a user entered, you’d encrypt whatever the user typed and compare it to the contents of the /etc/passwd file.
这种方法在识别与用户ID相关联的用户名方面效果很好,但密码的处理则更加麻烦。
7.3.1节“/etc/passwd文件”描述了传统上,加密密码是/etc/passwd文件的一部分,因此如果你想要验证用户输入的密码,你需要加密用户输入的内容并将其与/etc/passwd文件中的内容进行比较。
This traditional implementation has the following limitations:
这种传统实现有以下限制:
o It doesn’t set a system-wide standard for the encryption protocol. o It assumes that you have access to the encrypted password. o It assumes that you want to prompt the user for a password every time the user wants to access something that requires authentication (which gets annoying). o It assumes that you want to use passwords. If you want to use one-time tokens, smart cards, biometrics, or some other form of user authentication, you have to add that support yourself.
- 它没有为加密协议设定系统范围的标准。
- 它假设你可以访问加密密码。
- 它假设每次用户想要访问需要身份验证的内容时,你都要提示用户输入密码(这很烦人)。
- 它假设你想要使用密码。如果你想要使用一次性令牌、智能卡、生物识别或其他形式的用户认证,你必须自行添加支持。
Some of these limitations contributed to the development of the shadow password package discussed in 7.3.3 The /etc/shadow File, which took the first step in allowing system-wide password configuration. But the solution to the bulk of the problems came with the design and implementation of PAM.
其中一些限制促使我们开发了7.3.3节“/etc/shadow文件”中讨论的阴影密码包,它迈出了实现系统范围密码配置的第一步。
但是,解决大部分问题的方案是通过设计和实现PAM来实现的。
7.10 PAM
To accommodate flexibility in user authentication, in 1995 Sun Microsystems proposed a new standard called Pluggable Authentication Modules (PAM), a system of shared libraries for authentication (Open Source Software Foundation RFC 86.0, October 1995). To authenticate a user, an application hands the user to PAM to determine whether the user can successfully identify itself. This way, it’s relatively easy to add support for additional authentication techniques, such as two-factor and physical keys. In addition to authentication mechanism support, PAM also provides a limited amount of authorization control for services (for example, if you’d like to deny a service like cron to certain users).
为了适应用户认证的灵活性,Sun Microsystems在1995年提出了一个名为可插拔认证模块(PAM)的新标准,它是一个用于认证的共享库系统(开放源代码软件基金会RFC 86.0,1995年10月)。
为了验证用户身份,应用程序将用户交给PAM来确定用户是否能成功地进行身份验证。
这样,相对容易地添加对其他身份验证技术的支持,比如双因素认证和物理密钥。
除了认证机制的支持,PAM还为服务提供了有限的授权控制(例如,如果您想要拒绝某些用户使用cron这样的服务)。
Because there are many kinds of authentication scenarios, PAM employs a number of dynamically loadable authentication modules. Each module performs a specific task; for example, the pam_unix.so module can check a user’s password.
由于存在许多种认证场景,PAM使用了许多可动态加载的认证模块。
每个模块执行特定的任务;例如,pam_unix.so模块可以检查用户的密码。
This is tricky business, to say the least. The programming interface isn’t easy, and it’s not clear that PAM actually solves all of the existing problems. Nevertheless, PAM support is in nearly every program that requires authentication on a Linux system, and most distributions use PAM. And because it works on top of the existing Unix authentication API, integrating support into a client requires little, if any, extra work.
这是一项相当棘手的工作,可以说至少如此。
编程接口并不简单,而且并不清楚PAM是否真正解决了所有现有的问题。
尽管如此,几乎在每个需要在Linux系统上进行身份验证的程序中都有PAM支持,并且大多数发行版都使用PAM。由于它建立在现有的Unix认证API之上,将支持集成到客户端中几乎不需要额外的工作。
7.10.1 PAM Configuration(PAM 配置)
We’ll explore the basics of how PAM works by examining its configuration. You’ll normally find PAM’s application configuration files in the /etc/pam.d directory (older systems may use a single /etc/pam.conf file). Most installations include many files, so you may not know where to start. Some filenames should correspond to parts of the system that you know already, such as cron and passwd.
我们将通过检查其配置来了解 PAM 的基本工作原理。
通常,您会在 /etc/pam.d 目录中找到 PAM 的应用程序配置文件(旧系统可能使用单个 /etc/pam.conf 文件)。
大多数安装包含许多文件,所以您可能不知道从哪里开始。
一些文件名应该对应于您已经了解的系统部分,例如 cron 和 passwd。
Because the specific configuration in these files varies significantly between distributions, it can be difficult to find a common example. We’ll look at an example configuration line that you might find for chsh (the change shell program):
由于这些文件中的具体配置在不同的发行版之间有很大差异,因此很难找到一个通用的示例。
我们来看一个可能在 chsh(更改 shell 程序)中找到的示例配置行:
代码语言:javascript复制auth requisite pam_shells.so
This line says that the user’s shell must be in /etc/shells in order for the user to successfully authenticate with the chsh program. Let’s see how. Each configuration line has three fields: a function type, control argument, and module, in that order. Here’s what they mean for this example:
这行表示用户的 shell 必须在 /etc/shells 中,才能成功使用 chsh 程序进行身份验证。
让我们看看具体是如何工作的。每个配置行有三个字段:函数类型、控制参数和模块,依次排列。
对于这个示例,它们的含义如下:
o Function type. The function that a user application asks PAM to perform. Here, it’s auth, the task of authenticating the user. o Control argument. This setting controls what PAM does after success or failure of its action for the current line (requisite in this example). We’ll get to this shortly. o Module. The authentication module that runs for this line, determining what the line actually does. Here, the pam_shells.so module checks to see whether the user’s current shell is listed in /etc/shells.
o 函数类型。用户应用程序要求 PAM 执行的功能。在这里,它是 auth,即用户身份验证的任务。 o 控制参数。此设置控制 PAM 在当前行的操作成功或失败后要执行的操作(在此示例中为 requisite)。我们马上会讲到这个。 o 模块。在此行运行的身份验证模块,确定行的实际功能。在这里,pam_shells.so 模块会检查用户当前的 shell 是否在 /etc/shells 中列出。
PAM configuration is detailed on the pam.conf(5) manual page. Let’s look at a few of the essentials.
有关 PAM 配置的详细信息,请参阅 pam.conf(5) 手册页。
我们来看一些基本要点。
Function Types(功能类型)
A user application can ask PAM to perform one of the following four functions:
用户应用程序可以要求PAM执行以下四种功能之一:
o auth Authenticate a user (see if the user is who they say they are). o account Check user account status (whether the user is authorized to do something, for example). o session Perform something only for the user’s current session (such as displaying a message of the day). o password Change a user’s password or other credentials.
o auth 验证用户身份(查看用户是否是他们声称的人)。 o account 检查用户账户状态(例如,用户是否被授权执行某些操作)。 o session 仅为用户的当前会话执行某些操作(例如,显示每日消息)。 o password 更改用户的密码或其他凭据。
For any configuration line, the module and function together determine PAM’s action. A module can have more than one function type, so when determining the purpose of a configuration line, always remember to consider the function and module as a pair. For example, the pam_unix.so module checks a password when performing the auth function, but it sets a password when performing the password function.
对于任何配置行,模块和功能一起确定PAM的操作。
一个模块可以有多个功能类型,因此在确定配置行的目的时,始终要将功能和模块视为一对。
例如,pam_unix.so模块在执行auth功能时检查密码,但在执行password功能时设置密码。
Control Arguments and Stacked Rules(控制参数和堆叠规则)
One important feature of PAM is that the rules specified by its configuration lines stack, meaning that you can apply many rules when performing a function. This is why the control argument is important: The success or failure of an action in one line can impact following lines or cause the entire function to succeed or fail.
PAM的一个重要特性是其配置行指定的规则会叠加,这意味着在执行函数时可以应用多个规则。
这就是为什么控制参数很重要:一行中的动作成功或失败会影响后续的行,甚至可能导致整个函数的成功或失败。
There are two kinds of control arguments: the simple syntax and a more advanced syntax. Here are the three major simple syntax control arguments that you’ll find in a rule:
控制参数有两种类型:简单语法和更高级的语法。下面是在规则中常见的三个主要的简单语法控制参数:
o sufficient If this rule succeeds, the authentication is successful, and PAM does not need to look at any more rules. If the rule fails, PAM proceeds to additional rules. o requisite If this rule succeeds, PAM proceeds to additional rules. If the rule fails, the authentication is unsuccessful, and PAM does not need to look at any more rules. o required If this rule succeeds, PAM proceeds to additional rules. If the rule fails, PAM proceeds to additional rules but will always return an unsuccessful authentication regardless of the end result of the additional rules.
- sufficient:如果此规则成功,认证成功,PAM不需要再查看其他规则。如果此规则失败,PAM继续执行其他规则。
- requisite:如果此规则成功,PAM继续执行其他规则。如果此规则失败,认证失败,PAM不需要再查看其他规则。
- required:如果此规则成功,PAM继续执行其他规则。如果此规则失败,PAM会继续执行其他规则,但无论其他规则的最终结果如何,都会返回认证失败。
Continuing with the preceding example, here is an example stack for the chsh authentication function:
继续以上面的例子为例,这是用于chsh认证函数的一个示例堆栈:
代码语言:javascript复制auth sufficient pam_rootok.so
auth requisite pam_shells.so
auth sufficient pam_unix.so
auth required pam_deny.so
With this configuration, when the chsh command asks PAM to perform the authentication function, PAM does the following (see Figure 7-4 for a flowchart):
使用这个配置时,当chsh命令要求PAM执行身份验证功能时,PAM会执行以下操作(请参见图7-4的流程图):
- The pam_rootok.so module checks to see if the root user is the one trying to authenticate. If so, it immediately succeeds and attempts no further authentication. This works because the control argument is set to sufficient, meaning that success from this action is good enough for PAM to immediately report success back to chsh. Otherwise, it proceeds to Step 2.
- The pam_shells.so module checks to see if the user’s shell is in /etc/shells. If the shell is not there, the module returns failure, and the requisite control argument indicates that PAM must immediately report this failure back to chsh and attempt no further authentication. Otherwise, the shell is in /etc/shells, so the module returns success and fulfills the control flag of required; proceed to Step 3.
- The pam_unix.so module asks the user for the user’s password and checks it. The control argument is set to sufficient, so success from this module (a correct password) is enough for PAM to report success to chsh. If the password is incorrect, PAM continues to Step 4.
- The pam_deny.so module always fails, and because the required control argument is present, PAM reports failure back to chsh. This is a default for when there’s nothing left to try. (Note that a required control argument does not cause PAM to fail its function immediately—it will run any lines left on its stack—but the report back to the application will always be of failure.)
- pam_rootok.so模块检查是否是root用户尝试进行身份验证。如果是,则立即成功并不再进行进一步的身份验证。
- 这是因为控制参数设置为sufficient,意味着这个操作的成功足以让PAM立即向chsh报告成功。
- 否则,继续进行第2步。
- pam_shells.so模块检查用户的shell是否在/etc/shells中。如果shell不在其中,该模块返回失败,并且必需的控制参数指示PAM必须立即向chsh报告此失败,并且不再进行进一步的身份验证。
- 否则,shell在/etc/shells中,所以该模块返回成功,并满足required的控制标志;继续进行第3步。
- pam_unix.so模块要求用户输入密码并进行检查。控制参数设置为sufficient,因此该模块的成功(正确的密码)足以让PAM向chsh报告成功。
- 如果密码不正确,则PAM继续进行第4步。
- pam_deny.so模块始终失败,并且由于存在required的控制参数,PAM向chsh报告失败。这是当没有其他尝试时的默认行为。
- (请注意,required的控制参数不会立即导致PAM失败其功能-它将运行其堆栈上剩余的任何行-但向应用程序的报告将始终是失败。)
Figure 7-4. PAM rule execution flow
Figure 7-4. PAM rule execution flow
图7-4. PAM规则执行流程
NOTE Don’t confuse the terms function and action when working with PAM. The function is the high-level goal: what the user application wants PAM to do (authenticate a user, for example). An action is a specific step that PAM takes in order to reach that goal. Just remember that the user application invokes the function first and that PAM takes care of the particulars with actions. 注意,在使用PAM时不要混淆函数和操作这两个术语。 函数是高层目标:用户应用程序希望PAM执行的操作(例如身份验证用户)。 操作是PAM为达到该目标而采取的具体步骤。 只需记住用户应用程序首先调用函数,而PAM负责处理具体的操作细节。
The advanced control argument syntax, denoted inside square brackets ([]), allows you to manually control a reaction based on the specific return value of the module (not just success or failure). For details, see the pam.conf(5) manual page; when you understand the simple syntax, you’ll have no trouble with the advanced syntax.
高级控制参数语法,用方括号([])表示,允许您根据模块的具体返回值手动控制反应(不仅仅是成功或失败)。
有关详细信息,请参阅pam.conf(5)手册页;当您理解简单语法后,就不会遇到高级语法的困扰。
Module Arguments(模块参数)
PAM modules can take arguments after the module name. You’ll often encounter this example with the pam_unix.so module:
PAM模块可以在模块名称后面接受参数。
您经常会在pam_unix.so模块中遇到以下示例:
代码语言:javascript复制auth sufficient pam_unix.so nullok
The nullok argument here says that the user can have no password (the default would be fail if the user has no password).
这里的nullok参数表示用户可以没有密码(如果用户没有密码,默认情况下会失败)。
7.10.2 Notes on PAM(关于 PAM 的说明)
Due to its control flow capability and module argument syntax, the PAM configuration syntax has many features of a programming language and a certain degree of power. We’ve only scratched the surface so far, but here are a few more tips on PAM:
由于其控制流能力和模块参数语法,PAM配置语法具有许多编程语言的特性和一定的功能。
到目前为止,我们只是浅尝辄止,但以下是关于PAM的一些建议:
o To find out which PAM modules are present on your system, try man -k pam_ (note the underscore). It can be difficult to track down the location of modules. Try the locate unix_pam.so command and see where that leads you. o The manual pages contain the functions and arguments for each module. o Many distributions automatically generate certain PAM configuration files, so it may not be wise to change them directly in /etc/pam.d. Read the comments in your /etc/pam.d files before editing them; if they’re generated files, the comments will tell you where they came from. o The /etc/pam.d/other configuration file defines the default configuration for any application that lacks its own configuration file. The default is often to deny everything. o There are different ways to include additional configuration files in a PAM configuration file. The @include syntax loads an entire configuration file, but you can also use a control argument to load only the configuration for a particular function. The usage varies among distributions. o PAM configuration doesn’t end with module arguments. Some modules can access additional files in /etc/security, usually to configure per-user restrictions.
o 要查找系统上存在哪些PAM模块,可以尝试使用man -k pam_
命令(注意下划线)。追踪模块的位置可能有些困难。尝试使用locate unix_pam.so命令并查看结果。
o 手册页包含每个模块的函数和参数信息。
o 许多发行版会自动生成某些PAM配置文件,因此直接在/etc/pam.d中更改它们可能不明智。在编辑这些文件之前,请阅读/etc/pam.d文件中的注释;如果它们是生成的文件,注释将告诉您它们的来源。
o /etc/pam.d/other配置文件定义了缺乏自己配置文件的任何应用程序的默认配置。默认情况下通常是拒绝所有操作。
o 在PAM配置文件中,有多种方法可以包含其他配置文件。@include语法可以加载整个配置文件,但您也可以使用控制参数仅加载特定功能的配置。具体用法因发行版而异。 o PAM配置不仅仅限于模块参数。一些模块可以访问/etc/security中的其他文件,通常用于配置每个用户的限制。
7.10.3 PAM and Passwords(PAM 和密码)
Due to the evolution of Linux password verification over the years, a number of password configuration artifacts remain that can cause confusion at times. The first to be aware of is the file /etc/login.defs. This is the configuration file for the original shadow password suite. It contains information about the encryption algorithm used for the shadow password file, but it’s rarely used on a modern system with PAM installed, because the PAM configuration contains this information. This said, the encryption algorithm in /etc/login.defs should match the PAM configuration in the rare case that you run into an application that doesn’t support PAM.
由于Linux密码验证多年来的演变,一些密码配置遗留物可能会导致时而混淆。
首先要了解的是文件/etc/login.defs。这是原始影子密码套件的配置文件。
它包含有关用于影子密码文件的加密算法的信息,但在安装了PAM的现代系统上很少使用,因为PAM配置包含了这些信息。
也就是说,在极少数情况下,如果遇到不支持PAM的应用程序,/etc/login.defs中的加密算法应与PAM配置相匹配。
Where does PAM get its information about the password encryption scheme? Recall that there are two ways for PAM to interact with passwords: the auth function (for verifying a password) and the password function (for setting a password). It’s easiest to track down the password-setting parameter. The best way is probably just to grep it:
PAM从哪里获取有关密码加密方案的信息呢?
请回想一下,PAM与密码交互有两种方式:auth函数(用于验证密码)和password函数(用于设置密码)。
最容易找到设置密码参数的方法可能就是使用grep命令:
代码语言:javascript复制$ grep password.*unix /etc/pam.d/*
The matching lines should contain pam_unix.so and look something like this:
匹配的行应包含 pam_unix.so,并看起来像这样:
代码语言:javascript复制password sufficient pam_unix.so obscure sha512
The arguments obscure and sha512 tell PAM what to do when setting a password. First, PAM checks to see if the password is “obscure” enough (that is, the password isn’t too similar to the old password, among other things), and then PAM uses the SHA512 algorithm to encrypt the new password.
参数"obscure"和"sha512"告诉PAM在设置密码时要做什么。
首先,PAM会检查密码是否足够"obscure"(也就是说,密码与旧密码不太相似,还有其他条件),然后PAM使用SHA512算法对新密码进行加密。
But this happens only when a user sets a password, not when PAM is verifying a password. So how does PAM know which algorithm to use when authenticating? Unfortunately, the configuration won’t tell you anything; there are no encryption arguments for pam_unix.so for the auth function. The manual pages also tell you nothing.
但是,这只在用户设置密码时发生,而不是在PAM验证密码时发生。那么PAM在进行身份验证时如何知道要使用哪个算法呢?
不幸的是,配置文件不会告诉你任何信息;对于auth函数,pam_unix.so没有加密参数。
手册页也没有提供任何信息。
It turns out that (as of this writing) pam_unix.so simply tries to guess the algorithm, usually by asking the libcrypt library to do the dirty work of trying a whole bunch of things until something works or there’s nothing left to try. Therefore, you normally don’t have to worry about the verification encryption algorithm.
事实证明,(截至本文写作时),pam_unix.so只是尝试猜测算法,通常是通过请求libcrypt库来完成尝试一系列可能的算法,直到找到有效的或者没有其他尝试的算法为止。
因此,通常你不需要担心验证加密算法。
7.11 Looking Forward(展望未来)
We’re now at about the midpoint in our progression through this book, having covered many of the vital building blocks of a Linux system. The discussion of logging and users on a Linux system has introduced you to what makes it possible to divide services and tasks into small, independent chunks that still know how to interact to a certain extent.
我们现在正处于本书的中点,已经了解了 Linux 系统的许多重要组成部分。
关于 Linux 系统中的日志和用户的讨论已经向你介绍了如何将服务和任务划分为独立的小块,并在一定程度上进行交互。
This chapter dealt almost exclusively with user space, and we now need to refine our view of user-space processes and the resources they consume. To do so, we’ll go back into the kernel in Chapter 8
本章几乎只讨论了用户空间,现在我们需要完善我们对用户空间进程及其所消耗资源的看法。
为此,我们将在第 8 章中回到内核中去。