口令文件
UNIX系统的口令文件是/etc/passwd,包含了多个字段,字段之间使用冒号分隔。这些字段包含在pwd.h中定义的passwd结构中。对于Linux系统,其结构定义为:
/* The passwd structure. */
struct passwd
{
char *pw_name; /* Username. */
char *pw_passwd; /* Password. */
__uid_t pw_uid; /* User ID. */
__gid_t pw_gid; /* Group ID. */
char *pw_gecos; /* Real name. */
char *pw_dir; /* Home directory. */
char *pw_shell; /* Shell program. */
};
在Linux系统中,/etc/passwd的部分内容如下:
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
jamza:x:1011:1012::/home/jamza:/bin/bash
若想阻止任何人以某个用户名登录到系统,则将该用户名对应的shell字段设置为/dev/null即可。而使用/bin/nologin命令,打印可定制的出错信息,然后以非0状态阻止登录到系统。
标准提供了2个获取口令文件项的函数:
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);
/*若成功,返回指针,若出错,返回NULL*/
如果要查看整个的口令文件,可调用一下的函数:
#include <pwd.h>
struct passwd *getpwent(void);
/*若成功,返回指针,若出错或者到达文件尾端,返回NULL*/
void setpwent(void);
void endpwent(void);
每次调用getpwent函数,则返回口令文件中的下一个记录项。
函数setpwent反绕它所使用的口令文件,即将getpwent的读写地址指回口令文件的起始位置,endpwent关闭使用的口令文件。
在使用getpwent函数查看完口令文件后,一定要使用endpwent关闭口令文件。
阴影口令
为了保证系统的安全性,将加密口令存放在另一个被称为阴影口令的文件中,改文件至少包含用户名与加密口令,以及其他与口令相关的信息字段。
在Linux系统中的阴影口令文件为/etc/shadow,其字段对应的数据结构为spwd结构,在shadow.h中定义:
/* Structure of the password file. */
struct spwd
{
char *sp_namp; /* Login name. */
char *sp_pwdp; /* Encrypted password. */
long int sp_lstchg; /* Date of last change. */
long int sp_min; /* Minimum number of days between changes. */
long int sp_max; /* Maximum number of days between changes. */
long int sp_warn; /* Number of days to warn user to change the password. */
long int sp_inact; /* Number of days the account may be inactive. */
long int sp_expire; /* Number of days since 1970-01-01 until account expires. */
unsigned long int sp_flag; /* Reserved. */
};
访问阴影口令文件的相关函数为:
#include <shadow.h>
struct spwd *getspent(uid_t uid);
struct spwd *getspnam(const char *name);
/*若成功,返回指针,若出错,返回NULL*/
void setspwent(void);
void endspwent(void);
组文件
组文件在Linux系统中为/etc/group,文件中的相关字段对应grp.h文件中定义的group结构:
/* The group structure. */
struct group
{
char *gr_name; /* Group name. */
char *gr_passwd; /* Password. */
__gid_t gr_gid; /* Group ID. */
char **gr_mem; /* Member list. */
};
字段gr_mem是一个指针数组,每个指针指向属于该组的用户名。数组以null结尾。
使用以下的函数来查看组名和组ID值:
#include <grp.h>
struct group *getgrgid(gid_t gid);
struct group *getgrnam(const char *name);
/*若成功,返回指针,若出错,返回NULL*/
若是搜索整个组文件,使用以下3个函数,这些函数类似针对口令文件的3个函数:
#include <grp.h>
struct group *getgrent(void);
/*若成功,返回指针,若出错或者到达文件尾端,返回NULL*/
void setgrent(void);
void endgrent(void);
附属组
每个用户账户不仅可以属于口令文件记录项中组ID所对应的组,也可以属于多至16个另外的组。
当有附属组的概念后,文件访问权限检查相应的修改为:不仅将进程的有效组ID与文件的组ID比较,还将所有附属组ID与文件的组ID进行比较。
为了获取与设置附属组ID,使用以下函数:
#include <unistd.h>
int getgroups(int gidsetsize, gid_t grouplist[]);
/*若成功,返回附属组ID数量,若出错,返回-1*/
#include <grp.h> /* on Linux */
int setgroups(int ngroups, const gid_t grouplist[]);
int initgroups(const char *username, gid_t basegid);
函数getgroups将进程所属用户的各个附属组ID写到数组grouplist中,填到该数组的ID数量最多为gidsetsize,函数返回实际填写到数组中的ID数量。
若gidsetsize为0,则函数只返回附属组ID的数量,对数组grouplist不作修改。
其他数据文件
访问系统数据的相关例程与函数如下:
系统标识
系统定义了uname函数,返回与主机与操作系统相关的信息。
#include <sys/utsname.h>
int uname(struct utsname *name);
/*若成功,返回非负值,若出错,返回-1*/
在Linux系统中,结构体utsname定义在/usr/include/sys/utsname.h。具体定义如下:
/* Structure describing the system and machine. */
struct utsname
{
/* Name of the implementation of the operating system. */
char sysname[_UTSNAME_SYSNAME_LENGTH];
/* Name of this node on the network. */
char nodename[_UTSNAME_NODENAME_LENGTH];
/* Current release level of this implementation. */
char release[_UTSNAME_RELEASE_LENGTH];
/* Current version level of this release. */
char version[_UTSNAME_VERSION_LENGTH];
/* Name of the hardware type the system is running on. */
char machine[_UTSNAME_MACHINE_LENGTH];
#if _UTSNAME_DOMAIN_LENGTH - 0
/* Name of the domain of this node on the network. */
# ifdef __USE_GNU
char domainname[_UTSNAME_DOMAIN_LENGTH];
# else
char __domainname[_UTSNAME_DOMAIN_LENGTH];
# endif
#endif
};
时间和日期
在UNIX系统中,计算从公元1970年1月1日 00:00:00(协调世界时,UTC)这一特定时间以来所经历的秒数,这种秒数是以数据类型time_t来表示。称为日历时间。日历时间包括时间和日期。
函数time返回日历时间:
#include <time.h>
time_t time(time_t *calptr);
/*若成功,返回时间值,若出错,返回-1*/
函数clock_gettime可用于获取指定时钟的时间,函数原型为:
#include <sys/time.h>
int clock_gettime(clockid_t clock_id, struct timespec *tsp);
/*若成功,返回0,若出错,返回-1*/
其中时钟通过类型为clockid_t来标识,可取的值保护:
# ifdef __USE_POSIX199309
/* Identifier for system-wide realtime clock. */
# define CLOCK_REALTIME 0
/* Monotonic system-wide clock. */
# define CLOCK_MONOTONIC 1
/* High-resolution timer from the CPU. */
# define CLOCK_PROCESS_CPUTIME_ID 2
/* Thread-specific CPU-time clock. */
# define CLOCK_THREAD_CPUTIME_ID 3
/* Monotonic system-wide clock, not adjusted for frequency scaling. */
# define CLOCK_MONOTONIC_RAW 4
/* Identifier for system-wide realtime clock, updated only on ticks. */
# define CLOCK_REALTIME_COARSE 5
/* Monotonic system-wide clock, updated only on ticks. */
# define CLOCK_MONOTONIC_COARSE 6
/* Monotonic system-wide clock that includes time spent in suspension. */
# define CLOCK_BOOTTIME 7
/* Like CLOCK_REALTIME but also wakes suspended system. */
# define CLOCK_REALTIME_ALARM 8
/* Like CLOCK_BOOTTIME but also wakes suspended system. */
# define CLOCK_BOOTTIME_ALARM 9
/* Flag to indicate time is absolute. */
# define TIMER_ABSTIME 1
# endif
另外Linux系统中常用的关于时间的数据结构为struct timespec 和struct timeval。两者的区别是timespec的第二个参数是纳秒数,而timeval的第二个参数是毫秒数。
struct timespec
{
__time_t tv_sec; /* Seconds. */
long tv_nsec; /* Nanoseconds. */
};
struct timeval {
time_t tv_sec;
suseconds_t tv_usec;
};