UNIX系统编程常用库函数说明
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()
补充:综合编程 , 安全编程 ,