2004年09月15日

很早就想献血了,但一直没有去,学校里的又没赶上。终于今天在街上的献血流动车上献了血,还一下子献了400CC,可惜我不是O型血,不然我献的价值就更大了,呵呵。

能无偿献血心里还是蛮开心的。

2004年09月11日

以前就想说了,Slackware10自带的gFtp 2.0.17,如果修改书签后并保存,再次点击书签菜单后gFtp会直接退出,不过以前做过的书签修改还是存在的。这个BUG屡试不爽,呵呵。Linux 下的GUI FTP客户程序与Windows下的比还是不大完善的。现在我在Windows下用的是开源的FileZilla,很不错。感觉和CuteFtp有的一拼 啊。

2004年09月10日

原来一直以为转docbooks文件转pdf文件应该没有问题的。后来发现要想成功转为中文pdf还是蛮麻烦的。

经过大半天的尝试,发现用jadetex或者doc-utils组件转成的PDF格式版面效果很不错,可惜就是不能显示中文。在 limodou的文章《使用FOP将中文DocBook xml转换成pdf的实现记录》和 白明弘的《DocBook文件转换环境设定》的指导下,终于成功用FOP把docbook的xml源文件转成了pdf文件,而且中文也能正常显示了,就是 效果就不敢恭维了。字体是没办法的,但是排版效果总觉得没有上面两个软件转的好。还在进一步努力中。。。。

2004年09月09日

DocBook提供了一个使用SGML/XML撰写结构化文档的系统。这个系统非常适合于计算机类的文章、书籍、论文等的出版,当然并不仅仅限于计算机 类。DocBook已经被Linux、FreeBSD等众多的项目制定作为文档撰写格式,并且有越来越多的组织和个人开始使用。

总所周知DocBook的SGML格式第一行总是:
<!DOCTYPE book PUBLIC “-//OASIS//DTD DocBook V4.2//EN”>

我因为是初学,所以这一行总是照抄,到用openjade将SGML源文件转为HTML文件时发现总是会出现“cannot generate system identifier for public text “-//OASIS//DTD Docbook V4.2//EN””错误,但是又能正确生成文档。仔细检查我所填的PUBLIC字段却怎么也看不出错误。后来发现,由于我的DocBook组件是 Slackware自带安装的,在/usr/share/sgml/docbook下面我发现我的DocBook版本其实是4.1,后来将第一行声明改为 <!DOCTYPE book PUBLIC “-//OASIS//DTD DocBook V4.1//EN”>就没有问题啦。

DocBook真是很方便,我现在的文档,笔记都是拿Emacs写成DocBook文件。Emacs可以很方便的添加DocBook标签。至于调用 openjade那么长的命令,完全可以写成一个shell脚本在Emacs的shell环境下执行,按几下键盘,同样格式的html,pdf文件就出来 了,真是不错。

2004年09月08日

first name

Last name

Email:

2004年09月02日

由经典(Blueidea.com)的朋友邀请,我也有了gmail,还一下子收到3个邀请,呵呵

1G的容量,怎么用啊,有难度噢,嘻嘻

PS:Wickey v0.2接近可以realease的程度了,正在测试和fix bug中。。。

2004年08月28日

原文:http://learn.tsinghua.edu.cn/homepage/2001315450/thought-20040827.html

转贴:

关于《完全用Linux工作》的思考

在一阵阵唾骂和欢呼声中,《完全用Linux工作》被转载到了天涯海角。我不知道它是怎样流传到那么多地方,其实我不知道它都被转到哪里去了。它在我的主页上放了不到一个星期就被我删掉了……

不管喜不喜欢这篇文章,大家都认为我写的很偏激。当时的情况 是这样,我用Linux 的时候被一个同学鄙视了,说:“你怎么用像 DOS 一样落后的东西,真土!看我漂亮的 Win2000…” 这跟当面嘲笑别人老婆或者妈妈有什么区别?我义愤填膺啊,就几乎跟他吵起来。 然后就写出了这篇文章放在主页上,叫了几个人来看。接着我珍爱的 TeX 又受一些人鄙视,于是我又写了一篇文章打击 Word,把 TeX 捧上了天。不过几天之后我就把《完全用Linux工作》删掉了,因为我自己都感觉到偏激而且不严谨。用偏见来对付有偏见的人,他自己就成了有偏见的人。不 过其它的介绍程序的文章留了下来,因为我觉得它们还有点用处。

可是没想到《完》竟然已经被转载到那么多地方,似乎引起不小的波澜。有段时间每天都收到十几封email,国内的,国外的,问技 术问题的,夸我的,骂我的,批判的,讲道理的,鄙视我的,想交朋友的,语 重心长的,要删掉机器上的 Windows 的…… 我的主页居然也占 据了“王垠”在 Google 上的首选位置,把那个日本的什么王公贵族 “李王垠殿下”都挤下去了。走到图书馆,亮出借书证,管理员说:“哇!你就是那个 Linux 牛人啊!”,也不知道他是褒是贬。甚至有人把他的 BBS 昵称都改成了“坚决拥护精神领袖花生” (花生是我的外号)。虽然我觉得自己身上没有什么值得 自豪的东西,但是又有点为自己兴风作浪的本事感到惊讶。虽然我一再告诫自己,我没有做什么实事,要谦虚,但是不由的有一种毛泽东,甘地,甚至摩西,lhz 的感觉。我体会到“网络上没有人知道你是一头猪”的真理 性。

这么长的时间之后,似乎不时还有人转载那篇文章,还有人写信给我说“对不起我还在用 Windows 给你写信”,“真想删掉Windows 啊”…… 我很好奇,我的文章真的有那么大的威力,竟然让别人不敢做原来能做的事情了!我再看这篇文 章,觉得有点惨不忍睹,看了开头就不忍心再看下去。我告诉同学我很后悔写了这么一篇文章,可是他说“矫枉必须过正,你没有错”。但是我也搞不清楚这个“枉”是实际存在的还是我道听途说来的了。也许我离开“阶级斗争”太久以至于自己都麻痹了?

虽然这样说,但是我希望不要再转载那篇文章。我本来很懒的,都不想议论什么,以致于我几个月前就想写现在这篇文章,到昨天才起笔。我们的传 统已经给我们的思想设下众多的禁锢,我不想让我的文章给某些计算机爱好者再设下一些精神枷锁。我没有足够的智慧来告诉别人应该做什么或者不应该做什么。每 个人自己的思想和判断力都应该受到尊重。UNIX 的工作方式难道真是那么好?符号和直觉难道就那么不可调和?为什么崇尚科学就忽略人性和艺术?为什么有些人自称计算机科学家就要鄙视程序员和工程师?为什 么崇尚理论的人就鄙视实践?为什么喜欢中国画就要鄙视西方艺术?喜欢古典音乐就要鄙视流行歌曲?似乎人总是假定自己是正确的。何时我们才能敞开心灵的大 门,获得智慧和幸福?

王垠

2004.8.27


中文片名:《车神》
英文片名:《Michel Vaillant》
导  演:路易-帕斯卡尔·库夫莱尔
类  型:动作、惊险、赛车
主要演员:萨迦莫尔· 斯蒂文纳
      彼得· 扬布鲁格· 希尔兹
      黛安娜· 克鲁格丽莎· 巴比西亚
      让·皮耶尔·卡塞尔

  本片展现了两支世界顶极F1方程式冠军车队激烈竞争的故事。

  出生于赛车世家的赛车手米歇尔·瓦扬是个对赛车运动非常痴迷的赛车天才,尽管母亲始 终对他喜欢的赛车运动持反对意见,但是米歇尔始终在追求自己的理想,那就是一定要取得勒芒24小时拉力挑战赛的冠军。凭借着他的热情和那种天生为了赛车运 动而打造的坚强的体魄,以及冷静的分析能力,他终于拿到了这一赛事的冠军。一时间媒体,和车迷都对他崇拜有加,把他看成法国赛车的天皇巨星。就在各种鲜花、喜悦包围着米歇尔的同时,一场阴谋也悄悄的展开了。

  有人对于米歇尔和他的车队所取得的成绩一直耿耿于怀,维斯·王就是这样的一个女人。 因为米歇尔的出色表现,使得维斯的父亲含恨退出赛车界,因此她对米歇尔怀恨在心,维斯精心策划,在以后的比赛中几乎施展了各种阴谋手段,不断地给米歇尔制 造险情。米歇尔在比赛中连续受到维斯所在车队车手的阻挠,不是一起制造撞车就是刻意围追堵截,加上赛事本身就充满了挑战性和危险性,使米歇尔常常面临绝 境,也同时显示了英雄本色。



2004年08月26日

What’s the difference between Slackware startup scripts and System V startup scripts?
Author: kkeller
Slackware Release: Slackware 7.0 and higher
Written on: 2002-06-24

Okay, this answer is very long. Just a warning.

Slackware uses BSD-style init scripts; many other distros use System V-style init scripts. Both SysV scripts and BSD scripts are human-readable, in that they are shell scripts, not compiled programs. The main difference is in how the scripts are designed.

SysV scripts tend to take arguments like start, stop, restart, and others, depending on the program it’s starting. So you could say something like /etc/ init.d/bind start to start BIND, and /etc/init.d/bind stop to stop BIND.

SysV-style init also tends to use symlinks to organize the boot process: in /etc/rc.d/rc.4/, there might be various symlinks to actual scripts in another directory. The symlinks are named like S10network, S25xdm, and so on, where the S means to start the service (K means kill it), and the numbers designate the order in which scripts should run.

The main advantage of SysV style init scripts is that they can be set up to configure a lot of stuff automagically. If, for example, you go into runlevel 6, you can have a symlink in /etc/rc.d/rc.6/ called K75bind, which will kill off BIND if the file to which its linked is set up to do that.

The main disadvantage of SysV style is that it’s terribly convoluted. If I want to add a service, for example, I need to write a SysV-style script (which is certainly nontrivial) to at least handle “start” (and possibly “stop”). Then I need to make sure I’ve got the symlink set up correctly in each runlevel where I want it to run, and if I happen to need it to execute between two scripts that are consecutively numbered, I need to do some symlink renumbering (e.g., if S10xxx and S11yyy exist, and I want zzzz to run between, I need to resymlink one of those files to squeeze zzzz between them).

It’s also a huge pain to alter the SysV boot process temporarily–if I want service xxx to not run on next boot, the easiest way is to remove the S10xxx symlink. Not too hard, but if I want to remove it from every runlevel, I need to remove the S10xxx symlink from every directory. Then if I change my mind and want xxx to run again, I need to recreate all of the appropriate symlinks by hand.

It’s one extra level of complexity to the already-complicated boot process, and one which Slackware doesn’t use: it uses BSD-style startup scripts instead.

BSD-style scripts are straight-ahead shell scripts that tend to run sequentially and don’t take arguments like start or stop. They run when the system enters their runlevel, and that’s it.

The main disadvantage of BSD-style is that you have to use some other method of controlling daemons. For example, if I want to stop BIND, I need to ps ax|grep named, find named’s PID, and kill the pid. (Or I can find the pid file.) But I can’t say /etc/init.d/bind stop (unless I write a SysV-style script for that).

The main advantage of BSD-style scripts is that they’re terribly easy to read and edit. For example, if I add a new service zzzz, I can add the line /usr/ local/bin/zzzz to /etc/rc.d/rc.local, and zzzz will run in the runlevels where rc.local executes. If I know I want zzzz only in runlevel 4, I can put it in /etc/ rc.d/rc.4 (no longer a directory, but a shell script). If I need to change the order, I can just put the call to zzzz between the services where it should run; most editors can handle inserting text in the middle of a file (even ed!). Also, you can easily comment out a service to stop it from running, and uncomment it later.

So, while most distributions use SysV style, Slackware uses BSD-style. For many Slackware users, the ease-of-use of the BSD-style greatly outweighs the power of SysV-style. You can certainly form your own opinion.

Contrary to popular belief, it’s not that difficult to switch from one style to the other–just grab the inittab and rc files from one system and copy them to another. The init binary itself is not changed–most of the ‘’style” is set in inittab and the scripts it calls.

Slackware启动脚本与System V启动脚本的区别何在?
作者:kkeller
翻译:windrose
Slackware版本:Slackware 7.0及以上
写于:2002-6-24

好了,说来话长。莫谓言之不预也。

Slackware 使用BSD风格的init脚本,而很多别的发行版使用System V风格的init脚本。SysV和BSD脚本都是能让人读懂的,即它们都是shell脚本,而不是已编译的程序。主要的区别在于脚本是如何设计的。

SysV脚本倾向于接受诸如start、stop、restart之类的参数,依它所启动的程序而定。所以你可以用 /etc/init.d/bind start 这样的命令来启动BIND,并用 /etc/init.d/bind stop 来停止BIND。

SysV的启动还倾向于使用符号链接来组织启动进程,例如在 /etc/rc.d/rc.4/中,可能会有指向别的目录中的真正的脚本的各种各样的符号链接。这些链接的命令会像是 S10network、S25xdm之类,其中的S表示启动(start)该项服务(如果是K,则表示kill),而数字指定了脚本执行的顺序。

SysV风格的启动脚本的主要优点在于能够设置成自动配置许多东西。例如,若你进入runlevel 6,你可以建立一个链接叫K75bind来终止BIND,前提是链接所指向的文件已经设置好来做这件事。

SysV风格脚本的主要缺点是太过弯弯绕。假如我想增加一个服务,我要先写一个SysV风格的脚本(不是容易的事),它至少要处理“start”(还可能有“stop”)。然后,我必须确保在每个要运行这个服务的runlevel中正确地设置好符号链接。如果恰好这个服务需要在已经连续编号的两个脚本之间运行,我就需要做一些对符号链接重新编号的工作(例如,S10xxx和S11yyy已经存在,而我想让zzzz在它们之间运行,我就需要对前两者之一重新建立符号链接来把zzzz挤进去)。

想暂时改变SysV的启动进程也是非常痛苦的事情。假如我不想在下次启动时运行xxx服务,最简单的办法是删除S10xxx这个链接,不算难吧?但如果我想在每个runlevel中都去掉它,我就需要从每个有关目录中删除S10xxx这个链接。然后,假如我改了主意,想重新运行xxx,我需要手工重新建立所有的符号链接。

这样子无疑是在已经很复杂的启动进程上叠床架屋,而Slackware是不会这么做的:它用BSD风格的启动脚本。

BSD风格的脚本是直接了当的shell脚本,它们倾向于顺序运行,而不需要start或stop之类参数。只要系统进入了它们的runlevel就会执行,就这么简单。

BSD风格的主要缺点是你需要一些其他方法来控制后台服务。例如,若我要停止BIND,我要先用命令 ps ax|grep named 找出 named的PID,然后kill这个PID(或者用这个pid的文件名)。但是我不能简单地下个命令 /etc/init.d/bind stop (除非我已经写了个这样的SysV脚本)。

BSD风格脚本的主要优点是它们非常容易阅读和编辑。例如,若我想增加一个服务zzzz,我可以在 /etc/rd.d/rc.local中增加一行 /usr/local/bin/zzzz,这样只要是执行rc.local的runlevel,zzzz就会随之运行。假如我只想在runlevel 4执行zzzz,我可以把它放在 /etc/rc.d/rc.4 (不是目录,而是一个脚本)中。如果我要改变执行顺序,我只要把zzzz放在适当的服务之间,多数编辑器都支持在文件中间插入文本(就算ed都支持)。还有,你可以用注释的方式停止一个服务,然后去掉注释让它重新运行。

因此,当多数发行版采用SysV风格时,Slackware采用了BSD风格。对于许多Slackware用户,BSD风格的易用性胜过SysV风格的强大功能。当然,你可以有自己的意见。

与普遍的观点相反,从一种风格转到另一种并不那么困难,只要把inittab和rc文件从一个系统拷贝到另一个系统即可。init程序自身没有改变,所谓“风格”多是在inittab和它所调用的脚本中设置的。

译注:现在slackware为了提高兼容性,在/etc/rc.d/提供了rc.sysvinit脚本以适应某些基于SysV启动过程的商业程序的需要。另外,在许多设置服务的脚本中,也接受start、stop、restart这一类参数,例如rc.sendmail、rc.sshd等。

2004年08月23日

UNIX系统为程序员提供了许多子程序,这些子程序可存取各种安全属性.有些是信息子程序,返回文件属性,实际的和有效的UID,GID等信息.有些子程序可 改变文件属性.UID,GID等有些处理口令文件和小组文件,还有些完成加密和解密.

本文主要讨论有关系统子程序,标准C库子程序的安全,如何写安全的C程序并从root的角度介绍程序设计(仅能被root调用的子程序).

1.系统子程序

(1)I/O子程序

*creat():建立一个新文件或重写一个暂存文件.

需要两个参数:文件名和存取许可值(8进制方式).如:

creat(“/usr/pat/read_write”,0666) /* 建立存取许可方式为0666的文件 */

调用此子程序的进程必须要有建立的文件的所在目录的写和执行许可,置

给creat()的许可方式变量将被umask()设置的文件建立屏蔽值所修改,新

文件的所有者和小组由有效的UID和GID决定.

返回值为新建文件的文件描述符.

*fstat():见后面的stat().

*open():在C程序内部打开文件.

需要两个参数:文件路径名和打开方式(I,O,I&O).

如果调用此子程序的进程没有对于要打开的文件的正确存取许可(包括文

件路径上所有目录分量的搜索许可),将会引起执行失败.

如果此子程序被调用去打开不存在的文件,除非设置了O_CREAT标志,调用

<> 将不成功.此时,新文件的存取许可作为第三个参数(可被用户的umask修改).

当文件被进程打开后再改变该文件或该文件所在目录的存取许可,不影响

对该文件的I/O操作.

*read():从已由open()打开并用作输入的文件中读信息.

它并不关心该文件的存取许可.一旦文件作为输入打开,即可从该文件中读

取信息.

*write():输出信息到已由open()打开并用作输出的文件中.同read()一样

它也不关心该文件的存取许可.

(2)进程控制

*exec()族:包括execl(),execv(),execle(),execve(),execlp()和execvp()

可将一可执行模快拷贝到调用进程占有的存贮空间.正被调用进

程执行的程序将不复存在,新程序取代其位置.

这是UNIX系统中一个程序被执行的唯一方式:用将执行的程序复盖原有的

程序.

安全注意事项:

. 实际的和有效的UID和GID传递给由exec()调入的不具有SUID和SGID许

可的程序.

. 如果由exec()调入的程序有SUID和SGID许可,则有效的UID和GID将设

置给该程序的所有者或小组.

. 文件建立屏蔽值将传递给新程序.

. 除设了对exec()关闭标志的文件外,所有打开的文件都传递给新程序.

用fcntl()子程序可设置对exec()的关闭标志.

*fork():用来建立新进程.其建立的子进程是与调用fork()的进程(父进程)

完全相同的拷贝(除了进程号外)

安全注意事项:

. 子进程将继承父进程的实际和有效的UID和GID.

. 子进程继承文件方式建立屏蔽值.

. 所有打开的文件传给子进程.

*signal():允许进程处理可能发生的意外事件和中断.

需要两个参数:信号编号和信号发生时要调用的子程序.

信号编号定义在signal.h中.

信号发生时要调用的子程序可由用户编写,也可用系统给的值,如:SIG_IGN

则信号将被忽略,SIG_DFL则信号将按系统的缺省方式处理.

如许多与安全有关的程序禁止终端发中断信息(BREAK和DELETE),以免自己

被用户终端终止运行.

有些信号使UNIX系统的产生进程的核心转储(进程接收到信号时所占内存

的内容,有时含有重要信息),此系统子程序可用于禁止核心转储.

(3)文件属性

*access():检测指定文件的存取能力是否符合指定的存取类型.

需要两个参数:文件名和要检测的存取类型(整数).

存取类型定义如下:

0: 检查文件是否存在

1: 检查是否可执行(搜索)

2: 检查是否可写

3: 检查是否可写和执行

4: 检查是否可读

5: 检查是否可读和执行

6: 检查是否可读可写可执行

这些数字的意义和chmod命令中规定许可方式的数字意义相同.

此子程序使用实际的UID和GID检测文件的存取能力(一般有效的UID和GID

用于检查文件存取能力).

返回值: 0:许可 -1:不许可.

*chmod():将指定文件或目录的存取许可方式改成新的许可方式.

需要两个参数:文件名和新的存取许可方式.

*chown():同时改变指定文件的所有者和小组的UID和GID.(与chown命令不

同).

由于此子程序同时改变文件的所有者和小组,故必须取消所操作文件的SUID

和SGID许可,以防止用户建立SUID和SGID程序,然后运行chown()去获得别

人的权限.

*stat():返回文件的状态(属性).

需要两个参数:文件路径名和一个结构指针,指向状态信息的存放

的位置.

结构定义如下:

st_mode: 文件类型和存取许可方式

st_ino: I节点号

st_dev: 文件所在设备的ID

st_rdev: 特别文件的ID

st_nlink: 文件链接数

st_uid: 文件所有者的UID

st_gid: 文件小组的GID

st_size: 按字节计数的文件大小

st_atime: 最后存取时间(读)

st_mtime: 最后修改时间(写)和最后状态的改变

st_ctime: 最后的状态修改时间

返回值: 0:成功 1:失败

*umask():将调用进程及其子进程的文件建立屏蔽值设置为指定的存取许可.

需要一个参数: 新的文件建立屏值.

(4)UID和GID的处理

*getuid():返回进程的实际UID.

*getgid():返回进程的实际GID.

以上两个子程序可用于确定是谁在运行进程.

*geteuid():返回进程的有效UID.

*getegid():返回进程的有效GID.

以上两个子程序可在一个程序不得不确定它是否在运行某用户而不是运行

它的用户的SUID程序时很有用,可调用它们来检查确认本程序的确是以该

用户的SUID许可在运行.

*setuid():用于改变有效的UID.

对于一般用户,此子程序仅对要在有效和实际的UID之间变换的SUID程序才

有用(从原有效UID变换为实际UID),以保护进程不受到安全危害.实际上该

进程不再是SUID方式运行.

*setgid():用于改变有效的GID.

2.标准C库

(1)标准I/O

*fopen():打开一个文件供读或写,安全方面的考虑同open()一样.

*fread(),getc(),fgetc(),gets(),scanf()和fscanf():从已由fopen()打

开供读的文件中读取信息.它们并不关心文件的存取许可.这一点

同read().

*fwrite(),put(),fputc(),puts,fputs(),printf(),fprintf():写信息到

已由fopen()打开供写的文件中.它们也不关心文件的存取许可.

同write().

*getpass():从终端上读至多8个字符长的口令,不回显用户输入的字符.

需要一个参数: 提示信息.

该子程序将提示信息显示在终端上,禁止字符回显功能,从/dev/tty读取口

令,然后再恢复字符回显功能,返回刚敲入的口令的指针.

*popen():将在(5)运行shell中介绍.

(2)/etc/passwd处理

有一组子程序可对/etc/passwd文件进行方便的存取,可对文件读取到入口

项或写新的入口项或更新等等.

*getpwuid():从/etc/passwd文件中获取指定的UID的入口项.

*getpwnam():对于指定的登录名,在/etc/passwd文件检索入口项.

以上两个子程序返回一指向passwd结构的指针,该结构定义在

/usr/include/pwd.h中,定义如下:

struct passwd {

char * pw_name; /* 登录名 */

char * pw_passwd; /* 加密后的口令 */

uid_t pw_uid; /* UID */

gid_t pw_gid; /* GID */

char * pw_age; /* 代理信息 */

char * pw_comment; /* 注释 */

char * pw_gecos;

char * pw_dir; /* 主目录 */

char * pw_shell; /* 使用的shell */

};

*getpwent(),setpwent(),endpwent():对口令文件作后续处理.

首次调用getpwent(),打开/etc/passwd并返回指向文件中第一个入口项的

指针,保持调用之间文件的打开状态.

再调用getpwent()可顺序地返回口令文件中的各入口项.

调用setpwent()把口令文件的指针重新置为文件的开始处.

使用完口令文件后调用endpwent()关闭口令文件.

*putpwent():修改或增加/etc/passwd文件中的入口项.

此子程序将入口项写到一个指定的文件中,一般是一个临时文件,直接写口

令文件是很危险的.最好在执行前做文件封锁,使两个程序不能同时写一个

文件.算法如下:

. 建立一个独立的临时文件,即/etc/passnnn,nnn是PID号.

. 建立新产生的临时文件和标准临时文件/etc/ptmp的链,若建链失败,

则为有人正在使用/etc/ptmp,等待直到/etc/ptmp可用为止或退出.

. 将/etc/passwd拷贝到/etc/ptmp,可对此文件做任何修改.

. 将/etc/passwd移到备份文件/etc/opasswd.

. 建立/etc/ptmp和/etc/passwd的链.

. 断开/etc/passnnn与/etc/ptmp的链.

注意:临时文件应建立在/etc目录,才能保证文件处于同一文件系统中,建

链才能成功,且临时文件不会不安全.此外,若新文件已存在,即便建

链的是root用户,也将失败,从而保证了一旦临时文件成功地建链后

没有人能再插进来干扰.当然,使用临时文件的程序应确保清除所有

临时文件,正确地捕捉信号.

(3)/etc/group的处理

有一组类似于前面的子程序处理/etc/group的信息,使用时必须用include

语句将/usr/include/grp.h文件加入到自己的程序中.该文件定义了group

结构,将由getgrnam(),getgrgid(),getgrent()返回group结构指针.

*getgrnam():在/etc/group文件中搜索指定的小组名,然后返回指向小组入

口项的指针.

*getgrgid():类似于前一子程序,不同的是搜索指定的GID.

*getgrent():返回group文件中的下一个入口项.

*setgrent():将group文件的文件指针恢复到文件的起点.

*endgrent():用于完成工作后,关闭group文件.

*getuid():返回调用进程的实际UID.

*getpruid():以getuid()返回的实际UID为参数,确定与实际UID相应的登录

名,或指定一UID为参数.

*getlogin():返回在终端上登录的用户的指针.

系统依次检查STDIN,STDOUT,STDERR是否与终端相联,与终端相联的标准输

入用于确定终端名,终端名用于查找列于/etc/utmp文件中的用户,该文件

由login维护,由who程序用来确认用户.

*cuserid():首先调用getlogin(),若getlogin()返回NULL指针,再调用

getpwuid(getuid()).

*以下为命令:

*logname:列出登录进终端的用户名.

*who am i:显示出运行这条命令的用户的登录名.

*id:显示实际的UID和GID(若有效的UID和GID和实际的不同时也显示有效的

UID和GID)和相应的登录名.

(4)加密子程序

1977年1月,NBS宣布一个用于美国联邦政府ADP系统的网络的标准加密法:数

据加密标准即DES用于非机密应用方面.DES一次处理64BITS的块,56位的加

密键.

*setkey(),encrypt():提供用户对DES的存取.

此两子程序都取64BITS长的字符数组,数组中的每个元素代表一个位,为0

或1.setkey()设置将按DES处理的加密键,忽略每第8位构成一个56位的加

密键.encrypt()然后加密或解密给定的64BITS长的一块,加密或解密取决

于该子程序的第二个变元,0:加密 1:解密.

*crypt():是UNIX系统中的口令加密程序,也被/usr/lib/makekey命令调用.

crypt()子程序与crypt命令无关,它与/usr/lib/makekey一样取8个字符长

的关键词,2个salt字符.关键词送给setkey(),salt字符用于混合encrypt()

中的DES算法,最终调用encrypt()重复25次加密一个相同的字符串.

返回加密后的字符串指针.

(5)运行shell

*system():运行/bin/sh执行其参数指定的命令,当指定命令完成时返回.

*popen():类似于system(),不同的是命令运行时,其标准输入或输出联到由

popen()返回的文件指针.

二者都调用fork(),exec(),popen()还调用pipe(),完成各自的工作,因而

fork()和exec()的安全方面的考虑开始起作用.

3.写安全的C程序

一般有两方面的安全问题,在写程序时必须考虑:

(1)确保自己建立的任何临时文件不含有机密数据,如果有机密数据,设置

临时文件仅对自己可读/写.确保建立临时文件的目录仅对自己可写.

(2)确保自己要运行的任何命令(通过system(),popen(),execlp(),

execvp()运行的命令)的确是自己要运行的命令,而不是其它什么命

令,尤其是自己的程序为SUID或SGID许可时要小心.

第一方面比较简单,在程序开始前调用umask(077).若要使文件对其他人可

读,可再调chmod(),也可用下述语名建立一个”不可见”的临时文件.

creat(“/tmp/xxx”,0);

file=open(“/tmp/xxx”,O_RDWR);

unlink(“/tmp/xxx”);

文件/tmp/xxx建立后,打开,然后断开链,但是分配给该文件的存储器并未删

除,直到最终指向该文件的文件通道被关闭时才被删除.打开该文件的进程

和它的任何子进程都可存取这个临时文件,而其它进程不能存取该文件,因

为它在/tmp中的目录项已被unlink()删除.

第二方面比较复杂而微妙,由于system(),popen(),execlp(),execvp()执行

时,若不给出执行命令的全路径,就能”骗”用户的程序去执行不同的命令.因

为系统子程序是根据PATH变量确定哪种顺序搜索哪些目录,以寻找指定的命

令,这称为SUID陷井.最安全的办法是在调用system()前将有效UID改变成实

际UID,另一种比较好的方法是以全路径名命令作为参数.execl(),execv(),

execle(),execve()都要求全路径名作为参数.有关SUID陷井的另一方式是

在程序中设置PATH,由于system()和popen()都启动shell,故可使用shell句

法.如:

system(“PATH=/bin:/usr/bin cd”);

这样允许用户运行系统命令而不必知道要执行的命令在哪个目录中,但这种

方法不能用于execlp(),execvp()中,因为它们不能启动shell执行调用序列

传递的命令字符串.

关于shell解释传递给system()和popen()的命令行的方式,有两个其它的问

题:

*shell使用IFS shell变量中的字符,将命令行分解成单词(通常这个

shell变量中是空格,tab,换行),如IFS中是/,字符串/bin/ed被解释成单词

bin,接下来是单词ed,从而引起命令行的曲解.

再强调一次:在通过自己的程序运行另一个程序前,应将有效UID改为实际的

UID,等另一个程序退出后,再将有效UID改回原来的有效UID.

SUID/SGID程序指导准则

(1)不要写SUID/SGID程序,大多数时候无此必要.

(2)设置SGID许可,不要设置SUID许可.应独自建立一个新的小组.

(3)不要用exec()执行任何程序.记住exec()也被system()和popen()调用.

. 若要调用exec()(或system(),popen()),应事先用setgid(getgid())

将有效GID置加实际GID.

. 若不能用setgid(),则调用system()或popen()时,应设置IFS:

popen(“IFS=\t\n;export IFS;/bin/ls”,”r”);

. 使用要执行的命令的全路径名.

. 若不能使用全路径名,则应在命令前先设置PATH:

popen(“IFS=\t\n;export IFS;PATH=/bin:/usr/bin;/bin/ls”,”r”);

. 不要将用户规定的参数传给system()或popen();若无法避免则应检查

变元字符串中是否有特殊的shell字符.

. 若用户有个大程序,调用exec()执行许多其它程序,这种情况下不要将

大程序设置为SGID许可.可以写一个(或多个)更小,更简单的SGID程序

执行必须具有SGID许可的任务,然后由大程序执行这些小SGID程序.

(4)若用户必须使用SUID而不是SGID,以相同的顺序记住(2),(3)项内容,并

相应调整.不要设置root的SUID许可.选一个其它户头.

(5)若用户想给予其他人执行自己的shell程序的许可,但又不想让他们能

读该程序,可将程序设置为仅执行许可,并只能通过自己的shell程序来

运行.

编译,安装SUID/SGID程序时应按下面的方法

(1)确保所有的SUID(SGID)程序是对于小组和其他用户都是不可写的,存取

权限的限制低于4755(2755)将带来麻烦.只能更严格.4111(2111)将使

其他人无法寻找程序中的安全漏洞.

(2)警惕外来的编码和make/install方法

. 某些make/install方法不加选择地建立SUID/SGID程序.

. 检查违背上述指导原则的SUID/SGID许可的编码.

. 检查makefile文件中可能建立SUID/SGID文件的命令.

4.root程序的设计

有若干个子程序可以从有效UID为0的进程中调用.许多前面提到的子程序,

当从root进程中调用时,将完成和原来不同的处理.主要是忽略了许可权限的检

查.

由root用户运行的程序当然是root进程(SUID除外),因有效UID用于确定文

件的存取权限,所以从具有root的程序中,调用fork()产生的进程,也是root进程.

(1)setuid():从root进程调用setuid()时,其处理有所不同,setuid()将把有

效的和实际的UID都置为指定的值.这个值可以是任何整型数.而对非root

进程则仅能以实际UID或本进程原来有效的UID为变量值调用setuid().

(2)setgid():在系统进程中调用setgid()时,与setuid()类似,将实际和有效

的GID都改变成其参数指定的值.

* 调用以上两个子程序时,应当注意下面几点:

. 调用一次setuid()(setgid())将同时设置有效和实际UID(GID),独立分

别设置有效或实际UID(GID)固然很好,但无法做到这点.

. setuid()(setgid())可将有效和实际UID(GID)设置成任何整型数,其数

值不必一定与/etc/passwd(/etc/group)中用户(小组)相关联.

. 一旦程序以一个用户的UID了setuid(),该程序就不再做为root运行,也

不可能再获root特权.

(3)chown():当root进程运行chown()时,chown()将不删除文件的SUID和/或

SGID许可,但当非root进程运行chown()时,chown()将取消文件的SUID和/

或SGID许可.

(4)chroot():改变进程对根目录的概念,调用chroot()后,进程就不能把当前

工作目录改变到新的根目录以上的任一目录,所有以/开始的路径搜索,都

从新的根目录开始.

(5)mknod():用于建立一个文件,类似于creat(),差别是mknod()不返回所打开

文件的文件描述符,并且能建立任何类型的文件(普通文件,特殊文件,目录

文件).若从非root进程调用mknod()将执行失败,只有建立FIFO特别文件

(有名管道文件)时例外,其它任何情况下,必须从root进程调用mknod().由

于creat()仅能建立普通文件,mknod()是建立目录文件的唯一途径,因而仅

有root能建立目录,这就是为什么mkdir命令具有SUID许可并属root所有.

一般不从程序中调用mknod().通常用/etc/mknod命令建立特别设备文件而

这些文件一般不能在使用着时建立和删除,mkdir命令用于建立目录.当用

mknod()建立特别文件时,应当注意确从所建的特别文件不允许存取内存,

磁盘,终端和其它设备.

(6)unlink():用于删除文件.参数是要删除文件的路径名指针.当指定了目录

时,必须从root进程调用unlink(),这是必须从root进程调用unlink()的唯

一情况,这就是为什么rmdir命令具有root的SGID许可的原因.

(7)mount(),umount():由root进程调用,分别用于安装和拆卸文件系统.这两

个子程序也被mount和umount命令调用,其参数基本和命令的参数相同.调

用mount(),需要给出一个特别文件和一个目录的指针,特别文件上的文件

系统就将安装在该目录下,调用时还要给出一个标识选项,指定被安装的文

件系统要被读/写(0)还是仅读(1).umount()的参数是要一个要拆卸的特别

文件的指针.

本文由isbase成员编译或原创,如要转载请保持文章的完整性

欢迎访问我们的站点http://www.isbase.com