6.2 口令文件
1、UNIX系统口令文件包含如下字段,这些字段包含在<pwd.h>
中定义的passwd结构中。
2、口令文件/etc/passwd文件是一个ASCII文件。字段直接用冒号分隔。
3、关于这些登录项,有下列各点:
(1)通常有一个用户名为root的登录项,其用户ID是0(超级用户)
(2)加密口令字段包含了一个占位符。
(3)口令文件中的某些字段可能是空。
(4)shell字段包含一个可执行程序名
(5)为了阻止一个特定用户登录系统,除使用/dev/null外,还有若干种替代方法:
常见的一种是,将/bin/false用作登录shell。它简单地以不成功(非0)状态终止。
另一种常见方法是,用/bin/true禁止一个账户。它所做的一切都是以成功(0)状态终止。
某些系统提供nologin命令,它打印可定制的出错信息,然后以非0状态终止。
(6)使用nobody用户名的一个目的是,使任何人都可登录至系统。该用户ID和组ID只能访问人人皆可读、写的文件。
4、POSIX.1 定义两个获取口令文件项的函数。在给出用户登录名或数值用户ID后,这两个函数就能查看相关项:
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);
两个函数返回值:若成功,返回指针;若出错,返回NULL。
(1)getpwuid函数由ls(1)程序使用,它将i节点中的数字用户ID映射为用户登录名。在键入登录名时,getpwnam函数由login(1)程序使用。
(2)这两个函数都返回一个指向passwd结构的指针,passwd结构通常是函数内部的静态变量,只要调用任一相关函数,其内容就会被重写。
3、查看用户名或用户ID、或是查看整个口令文件,可用下列3个函数:
#include <pwd.h>
struct passwd *getpwent(void);
返回值:若成功,返回指针;若出错或到达文件尾端,返回NULL。
void setpwent(void);
void endpwent(void);
(1)调用getpwent时,它返回口令文件中的下一个记录项。每次调用此函数都会重写该结构。
在第一次调用该函数时,它打开所有使用的各个文件。在使用本函数时,对口令文件中各个记录项的安排顺序并无要求。
(2)函数setpwent反绕它所使用的文件。
(3)endpwent则关闭这些文件。
4、程序给出了getpwnam函数的一个实现。
/*************************************************************************
> File Name: ins6_2.c
> Author: Elliot
************************************************************************/
/*
* 程序给出了getpwnam函数的一个实现
*/
#include <pwd.h>
#include <cstddef>
#include <string.h>
struct passwd *
getpwname(const char *name)
{
struct passwd *ptr;
setpwent();
while ((ptr = getpwent()) != NULL)
if (strcmp(name, ptr->pw_name) == 0)
break; /* found a match */
endpwent();
return(ptr); /* ptr is NULL if no match found */
}
6.3 阴影口令
1、加密口令是经单向加密算法处理过的用户口令副本。因为此算法是单向的,所以不能从加密口令猜测到原来的口令。
2、现在,某些系统将加密口令存放在另一个通常成为阴影口令(shadow password)的文件中。该文件至少要包含用户名和加密口令。与该口令相关的其他信息也可以存放在该文件中:
(1)只有用户登录名和加密口令两个字段是必须的。其他的字段控制口令更改的频率,或者说口令的衰老以及账户仍然处于活动状态的时间。
4、在Linux3.2.0 和 Solaris 10中,与访问口令文件的一组函数相类似,由另一组函数可用于访问阴影口令文件。
#include <shadow.h>
struct spwd *getspnam(const char *name);
struct spwd *getspent(void)
两个函数返回值:若成功,返回指针;若出错,返回NULL。
void setspent(void);
void endspent(void);
6.4 组文件
1、UNIX组文件(POSIX.1称其为组数据库)包含了如下字段,这些字段包含在<grp.h>
中所定义的group结构中.
(1)gr_mem是一个指针暑促,其中每个指针指向一个属于该组的用户名。该数组以null指针结尾。
2、两个由POSIX.1定义的函数来查看组名或数值组ID。
#include <grp.h>
struct group getgrid(gid_t gid);
struct group getgrnam(const char *name);
两个函数的返回值:若成功,返回指针;若出错,返回NULL。
3、如果需要搜索整个组文件,则须使用另外几个函数。下列3个函数类似于针对口令文件的3个函数:
#include <grp.h>
struct group *getgrent(void);
返回值:若成功,返回指针;若出错或到达文件尾端,返回NULL。
void setgrent(void);
void endgrent(void);
(1)setgrent函数打开组文件(如若它尚未被打开)并反绕它。
(2)getgrent函数从组文件中读下一个记录,如若该文件尚未打开,则先打开它。
(3)endgrent函数关闭组文件。
6.6 附属组ID
1、为了获取和设置附属组ID,提供了下列3个函数
#include <unistd.h>
int getgroups(int gidsetsize, gid_t grouplist[]);
返回值:若成功,返回附属组ID数量;若出错,返回-1。
#include <grp.h> /* on Linux */
#include <unistd.h> /* on FreeBSD, Mac OS X, and Solaris */
int setgroups(int ngroups, const gid_t grouplist[]);
#include <grp.h> /* on Linux and Solaris */
#include <unistd.h> /* on FreeBSD and Mac OS X */
int initgroups(const char *username, gid_t basegid);
两个函数的返回值:若成功,返回0;若出错,返回-1。
(1)getgroups将进程所属的各附属组ID填写到grouplist中,填写入该数组的附属组ID数最多为gidsetsize个。实际填写到数组中的附属组ID数由函数返回。
(2)若gidsetsize为0,则函数只返回附属组ID数,而对数组grouplist则不做修改(这使调用者可以确定grouplist数组的长度,以便进行分配)。
(3)setgroups可由超级用户调用以便调用进程设置附属组ID表。grouplist是组ID数组,而ngroups说明了数组中的元素数。ngroups的值不能大于NGROUPS_MAX。
(4)通常,只有initgroups函数调用setgroups,initgroups读整个组文件(用前面的getgrent、setgrent和endgrent),然后对username确定的成员关系。然后,它调用setgroups,以便该用户初始化附属组ID表。
(5)因为initgroups要调用setgroups,所以只有超级用户才能调用initgroups。
6.8 登录账户记录
1、utmp文件记录当前登录到系统的各个用户。wtmp文件跟中各个登录和注销事件。
struct utmp {
short ut_type; /* Type of record */
pid_t ut_pid; /* PID of login process */
char ut_line[UT_LINESIZE]; /* Device name of tty - "/dev/" */
char ut_id[4]; /* Terminal name suffix,
or inittab(5) ID */
char ut_user[UT_NAMESIZE]; /* Username */
char ut_host[UT_HOSTSIZE]; /* Hostname for remote login, or
kernel version for run-level
messages */
struct exit_status ut_exit; /* Exit status of a process
marked as DEAD_PROCESS; not
used by Linux init (1 */
/* The ut_session and ut_tv fields must be the same size when
compiled 32- and 64-bit. This allows data files and shared
memory to be shared between 32- and 64-bit applications. */
#if __WORDSIZE == 64 && defined __WORDSIZE_COMPAT32
int32_t ut_session; /* Session ID (getsid(2)),
used for windowing */
struct {
int32_t tv_sec; /* Seconds */
int32_t tv_usec; /* Microseconds */
} ut_tv; /* Time entry was made */
#else
long ut_session; /* Session ID */
struct timeval ut_tv; /* Time entry was made */
#endif
int32_t ut_addr_v6[4]; /* Internet address of remote
host; IPv4 address uses
just ut_addr_v6[0] */
char __unused[20]; /* Reserved for future use */
};
2、登录时,login程序填写此类结构,然后将其写入到utmp文件中。同时也将其添加到wtmp文件中。
3、注销时,init进程将utmp文件相应的记录擦除(每个字节都以null字节),并将一个新纪录添写到wtmp文件中。
4、在wtmp文件的注销记录中,ut_name字段清除为0.
5、再系统再启动时,以及更改系统时间和日期的前后,都在wtmp文件中追加写特殊的记录项。
6.6 系统标识
1、POSIX.1定义了uname函数,它返回与主机和操作系统有关的信息。
#include <sys/utsname.h>
int uname(struct utsname *name);
返回值:若成功,返回非负值;若出错,返回-1。
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
};
2、返回主机名的函数
#include <unistd.h>
int gethostname(char *name, int namelen);
返回值:若成功,返回0;若出错,返回-1。
(1)namelen参数指定name缓冲区长度,若提供足够的空间,则通过name返回的字符串以null字节结尾。如果没有足够的空间,则没有说明通过name返回的字符串是否以null结尾。
6.10 时间和日期例程
1、time函数返回当前时间和日期
#include <time.h>
time_t time(time_t *calptr);
返回值:若成功,返回时间值;若出错,返回-1。
(1)时间值作为函数值返回。如果参数非空,则是兼职也存放在由calptr指向的单元内。
2、clock_gettime函数可用于获取指定时钟的时间。
#include <sys/time.h>
int clock_gettime(clockid_t clock_id, struct timespec *tsp);
返回值:若成功,返回0,;若出错,返回-1。
下面是clockid_t类型的取值:
(1)clock_gettime可能比time函数得到更高精度的时间值。
3、clock_getres函数把参数tsp指向的timespec结构初始化与clock_id参数对应的时钟精度
#include <sys/time.h>
int clock_getres(clockid_t clock_id, struct timepsec *tsp);
(1)如:精度为1毫秒tv_sec字段就是0,tv_nsec字段就是1000000
4、对特定时钟设置时间,可以调用clock_settime函数。
#include <sys/time.h>
int clock_settime(clockid_t clock_id, const struct timespec *tsp);
返回值:若成功,返回0;若出错,返回-1。
5、函数gettimeofday提供了更高的精度(可到微妙级)
#include <sys/time.h>
int gettimeofday(struct timeval *restrict tp, void *restrict tzp);
返回值,总是返回0。
(1)tzp的唯一合法值是NULL。
6、两个函数localtime和gmtime将日历时间转换为分解时间,并将这些存放在一个tm结构中。
#include <time.h>
struct tm *gmtime(const time_t *calptr);
struct tm *localtime(const time_t *calptr);
两个函数的返回值:指向分解的tm结构的指针;若出错,返回NULL。
tm结构:
struct tm {
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */
int tm_year; /* Year - 1900 */
int tm_wday; /* Day of the week (0-6, Sunday = 0) */
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */
int tm_isdst; /* Daylight saving time */
};
(1)localtime将日历时间转换为本地时间(考虑到本地失去和夏令时标志)
(2)gmtime将日历时间转换成自协调同意时间的年、月、日、时、分、秒、周日分解结构。
7、函数mktime以本地时间的年、月、日作为参数,将其变换成time_t值。
#include <time.h>
time_t mktime(struct tm *tmptr);
返回值:若成功,返回日历时间;若出错,返回-1。
8、函数strftime是一个类似于printf的时间值函数。它非常复杂,可以通过多个参数来定制产生的字符串。
#include <time.h>
size_t strftime(char *restrict buf, size_t maxsize,
const char *restrict format,
const struct tm *restrict tmptr);
size_t strftime_l(char *restrict buf, size_t maxsize,
const char *restrict format,
const struct tm *restrict tmptr, locale_t locale);
两个函数的返回值:若由空间,返回存入数组的字符数;否则,返回0。
(1)strltime_l允许调用者将区域指定为参数。strftime使用通过TZ环境变量指定的区域。
(2)tmptr参数是要格式化的时间值,由一个指向分解时间值tm结构的指针说明。
(3)格式化结果存放在一个长度为maxsize个字符的buf数组中。如果buf长度足以存放格式化结果以及一个null终止符,则该函数返回在buf中存放的字符数(不包括null终止符)。否则,该函数返回0。
(4)format参数控制时间值的格式:
9、使用strftime打印包含当前日期和时间的字符串。
/*************************************************************************
> File Name: ins6_11.c
> Author: Elliot
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int
main(void)
{
time_t t;
struct tm *tmp;
char buf1[16];
char buf2[64];
time(&t);
tmp = localtime(&t);
if (strftime(buf1, 16, "time and date: %a %b %d %Y", tmp) == 0)
printf("buffer length 16 is too small\n");
else
printf("%s\n", buf1);
if (strftime(buf2, 64, "time and date: %a %b %d %Y", tmp) == 0)
printf("buffer length 64 is too small\n");
else
printf("%s\n", buf2);
exit (0);
}
10、strptime函数是strftime函数反过来的版本,把字符串时间转换成分解时间
#include <time.h>
char *strptime(const char *restrict buf, const char *restrict format,
struct tm *restrict tmptr);
返回值,指向上一次解析的字符串的下一个字符串的指针,否则,返回NULL。