其他章节
第七章
第八章
第九章
第十章
提示:个人学习笔记,无参考价值
文章目录
第六章 系统数据文件和信息
1. etc下的passwd、shadow、group文件
在Ubuntu系统中,/etc目录下,有三个文件:passwd shadow group。 这三个配置文件用于系统帐号管理,都是文本文件,可用vim等文本编辑器打开。
(1) /etc/passwd
在 Linux 系统中,每个用户都有一个对应的 /etc/passwd 文件中的记录行。这个文件对所有用户都是可读的,它记录了每个用户的一些基本属性信息。文件的内容如下:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4::sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
......
每一行是由分号分隔的字串组成,它的格式如下:
username:password:uid:gid:gecos:homedir:shell
各域对应的中文说明如下:
用户名:密码:用户ID:组ID:用户的全称等其它详细信息:主目录:登录shell
字段名 | 描述 |
---|---|
用户名 | 用户账号的字符串,通常长度不超过 8 个字符,并且由大小写字母和/或数字组成。 |
密码 | 存放着加密后的用户密码字,但是由于安全隐患,现在多数 Linux 系统使用 shadow 技术将真正的加密后的用户密码字存放到 /etc/shadow 文件中。 |
用户标识号 | 一个整数,系统内部用它来标识用户。一般情况下它与用户名是一一对应的。一般情况,超级用户是0,系统用户是1 ~ 499,普通用户是500 ~ 65535。 |
组标识号 | 记录用户所属的用户组。它对应着 /etc/group 文件中的一条记录。 |
注释性描述 | 记录着用户的一些个人情况,例如用户的真实姓名、电话、地址等,这个字段并没有什么实际的用途。 |
主目录 | 用户的起始工作目录,即用户在登录到系统之后所处的目录。 |
登录 Shell | 用户登录后启动的进程,也就是用户与 Linux 系统之间的接口。 |
Unix系统最初是用明文保存密码的,后来由于安全的考虑,采用crypt()算法加密密码并存放在/etc/passwd文件。现在,由 于计算机处理能力的提高,使密码破解变得越来越容易。/etc/passwd文件是所有合法用户都可访问的,大家都可互相看到密码的加密字符串,这给系统 带来很大的安全威胁。现代的Unix系统使用影子密码系统,它把密码从/etc/passwd文件中分离出来,真正的密码保存在/etc/shadow文件中,shadow文件只能由超级用户访问。
这样入侵者就不能获得加密密码串,用于破解。
使用shadow密码文件后,/etc/passwd文件中所有帐户的password域的内容为"x",如果password域的内容为"*",则该帐号被停用。使用passwd这个程序可修改用户的密码。
(2) /etc/shadow
/etc/shadow存放加密的口令,该文件只能由root读取和修改
。下面是shadow文件的内容:
root:$1$43ZR5j08$kuduq1uH36ihQuiqUGi/E9::0::7:::
daemon:*::0::7:::
bin:*::0::7:::
sys:*::0::7:::
sync:*::0::7:::
...
shaohao:$6$.afaIAfKOfGq0Y.P$5oNj2WwjGDEJ3aNQk...qYTEKKeR9lBEX/:19612:0:99999:7:::
...
/etc/shadow文件的格式如下:
用户名:加密密码:最后一次修改时间:最小修改时间间隔:密码有效期:密码需要变更前的警告天数:密码过期后的宽限时间:账号失效时间:保留字段
-
加密密码
这里保存的是真正加密的密码。目前 Linux 的密码采用的是 SHA512 散列加密算法,原来采用的是 MD5 或 DES 加密算法。SHA512 散列加密算法的加密等级更高,也更加安全。 -
最后一次修改时间
上面用户shahao密码最后一次修改时间字段值为19612,即1970-1-1后的19612天,如下图
-
最小修改时间间隔
最小修改间隔时间,也就是说,该字段规定了从第 3 字段(最后一次修改密码的日期)起,多长时间之内不能修改密码。如果是 0,则密码可以随时修改;如果是 10,则代表密码修改后 10 天之内不能再次修改密码。
此字段是为了针对某些人频繁更改账户密码而设计的。 -
密码有效期
经常变更密码是个好习惯,为了强制要求用户变更密码,这个字段可以指定距离第 3 字段(最后一次更改密码)多长时间内需要再次变更密码,否则该账户密码进行过期阶段。
该字段的默认值为 99999,也就是 273 年,可认为是永久生效。如果改为 90,则表示密码被修改 90 天之后必须再次修改,否则该用户即将过期。管理服务器时,通过这个字段强制用户定期修改密码。
密码需要变更前的警告天数与第 5 字段相比较,当账户密码有效期快到时,系统会发出警告信息给此账户,提醒用户 “再过 n 天你的密码就要过期了,请尽快重新设置你的密码!”。
该字段的默认值是 7,也就是说,距离密码有效期的第 7 天开始,每次登录系统都会向该账户发出 “修改密码” 的警告信息。 -
密码过期后的宽限天数
也称为“口令失效日”,简单理解就是,在密码过期后,用户如果还是没有修改密码,则在此字段规定的宽限天数内,用户还是可以登录系统的;如果过了宽限天数,系统将不再让此账户登陆,也不会提示账户过期,是完全禁用。
比如说,此字段规定的宽限天数是 10,则代表密码过期 10 天后失效;如果是 0,则代表密码过期后立即失效;如果是 -1,则代表密码永远不会失效。 -
账号失效时间
同第 3 个字段一样,使用自 1970 年 1 月 1 日以来的总天数作为账户的失效时间。该字段表示,账号在此字段规定的时间之外,不论你的密码是否过期,都将无法使用!
该字段通常被使用在具有收费服务的系统中。 -
保留
这个字段目前没有使用,等待新功能的加入。
(3) /etc/group
etc/group 文件是用户组的配置文件,内容包括用户和用户组,并且能显示出用户是归属哪个用户组或哪几个用户组,同一用户组的用户之间具有相似的特征。比如我们把某一用户加入到root用户组,那么这个用户就可以浏览root用户家目录的文件,如果root用户把某个文件的读写执行权限开放,root用户组的所有用户都可以修改此文件,如果是可执行的文件(比如脚本),root用户组的用户也是可以执行的。
用户组的特性在系统管理中为系统管理员提供了极大的方便,但安全性也是值得关注的,如某个用户下有对系统管理有最重要的内容,最好让用户拥有独立的用户 组,或者是把用户下的文件的权限设置为完全私有;另外root用户组一般不要轻易把普通用户加入进去.
/etc/group 每一行的内容包括用户组(Group)、用户组口令、GID及该用户组所包含的用户(User)。格式如下:
group_name:passwd:GID:user_list
- 第一字段:用户组名称
- 第二字段:用户组密码
- 第三字段:GID
- 第四字段:用户列表,每个用户之间用,号分割
2. /etc/passwd
getpwnam() 与 getpwuid()
#include <sys/types.h>
#include <pwd.h>
struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);
struct passwd {
char *pw_name; /* username */
char *pw_passwd; /* user password */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* group ID */
char *pw_gecos; /* user information */
char *pw_dir; /* home directory */
char *pw_shell; /* shell program */
};
案例
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]){
if (argc < 2){
fprintf(stderr,"usage:./%s uid\n",argv[0]);
exit(1);
}
uid_t uid = atoi(argv[1]);
struct passwd * userInfo = getpwuid(uid);
printf("user name = %s\n", userInfo->pw_name); //输出用户名
exit(0);
}
3. /etc/group
getgrnam() 和 getgrgid()
#include <sys/types.h>
#include <grp.h>
struct group *getgrnam(const char *name);
struct group *getgrgid(gid_t gid);
struct group {
char *gr_name; /* group name */
char *gr_passwd; /* group password */
gid_t gr_gid; /* group ID */
char **gr_mem; /* NULL-terminated array of pointers to names of group members*/
};
案例
#include <stdlib.h>
#include <stdio.h>
#include <grp.h>
int main(int argc, char* argv[]){
if (argc < 2){
fprintf(stderr,"usage:./%s gid\n",argv[0]);
exit(1);
}
struct group * groupInfo;
groupInfo = getgrgid(atoi(argv[1]));
printf("%s\n",groupInfo->gr_name); //输出组名
exit(0);
}
4. /etc/shadow
(1) getspnam()
//通过username 获取它在shadow中的一行内容
#include <shadow.h>
struct spwd *getspnam(const char *name);
struct spwd {
char *sp_namp; /* Login name */
char *sp_pwdp; /* Encrypted password */
long sp_lstchg; /* Date of last change
(measured in days since
1970-01-01 00:00:00 +0000 (UTC)) */
long sp_min; /* Min # of days between changes */
long sp_max; /* Max # of days between changes */
long sp_warn; /* # of days before password expires
to warn user to change it */
long sp_inact; /* # of days after password expires
until account is disabled */
long sp_expire; /* Date when account expires
(measured in days since
1970-01-01 00:00:00 +0000 (UTC)) */
unsigned long sp_flag; /* Reserved */
};
(2) crypt()
#include <crypt.h>
/**
* 参数一:要加密的串
* 参数二:加密的方式和盐,crypt函数只能用到第三个$符号之前的内容
*/
char * crypt(const char *key, const char *salt);
#include <unistd.h>
char *getpass(const char *prompt); //参数是显示出来的提示符,返回的值是用户的输入
用户密码被加密,格式为$加密方式$加密使用的salt(杂串)$encrypted加密后的口令
。用 $ 符号隔开
案例
#include <stdlib.h>
#include <stdio.h>
#include <crypt.h>
#include <unistd.h>
#include <shadow.h>
#include <string.h>
int main(int argc, char* argv[]){
if (argc < 2){
fprintf(stderr,"usage: ./%s username",argv[0]);
exit(1);
}
char * input_pass;
input_pass = getpass("input your password:");
struct spwd * shadow_line;
shadow_line = getspnam(argv[1]);
//第二个参数 crypt函数指挥用到它第三个$之前的字符串数据
char * crypted_input_pass = crypt(input_pass, shadow_line->sp_pwdp);
if (strcmp(crypted_input_pass, shadow_line->sp_pwdp) == 0){
puts("OK!");
}else{
puts("ERROR!");
}
exit(0);
}
编译指令
gcc crypt_test.cpp -o crypt_test -lcrypt
5. 时间戳
(1) time()
#include <time.h>
time_t time(time_t *tloc);
-
函数描述
time() returns the time as the number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC). -
返回值
On success, the value of time in seconds since the Epoch is returned.
On error, ((time_t) -1) is returned, and errno is set appropriately.time_t res; //拿到时间戳的两种方法 time(&res); res = time(NULL);
(2) gmtime() 与localtime()
函数描述:都是把time_t的类型转换为结构体tm
struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);
struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);
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 */
};
(3) mktime()
time_t mktime(struct tm *tm);
将结构体tm转换为time_t类型。
失败则返回 (time_t) -1 并且设置 errno.
【副作用】
会修改传入的参数,可以用来调节溢出的部分,如天数溢出,则进位地调整月数和年数。
(4) strftime()
strftime用来格式化时间和日期,转换成用户需要的形式。
#include <time.h>
/**
* 参数一:用于存放结果的字符串
* 参数二:用于说明存放结果的空间最多能存放多少个字节数据
* %y 不显示世纪。如:2014显示14
%Y 显示完整的年份
%m 月
%d 日
%H 时
%M 分
%S 秒
。。。。。。
* 参数三:想要的格式
* 参数四:要被格式化的时间结构体tm
*/
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
案例
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define BUFSIZE 1024
int main(){
time_t * my_time ;
//获取当前时间
time(my_time);
//将time_t类型的时间转成tm类型结构体
struct tm * tm_my_time = localtime(my_time);
char buf[BUFSIZE];
memset(buf,'\0',BUFSIZE);
//格式化输出 年-月-日 时:分:秒
strftime(buf, BUFSIZE, "%Y-%m-%d %H:%M:%S", tm_my_time);
puts(buf);
exit(0);
}
案例一 每隔一秒写一次当前日期
每隔一秒,往文本文件输入一行内容,内容为:当前时间
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#define BUFSIZE 1024
int main(){
//以追加+读的模式打开文件
FILE * my_file = fopen("./timelog.txt","a+");
if (my_file == NULL){
perror("fopen()");
exit(1);
}
time_t * my_time ;
struct tm * tm_my_time ;
char buf[BUFSIZE];
int i = 0;
while (i < 100){
//获取当前时间戳
time(my_time);
//转成结构体类型
tm_my_time = localtime(my_time);
//格式化
memset(buf,'\0',BUFSIZE);
strftime(buf, BUFSIZE, "%Y-%m-%d %H:%M:%S\n",tm_my_time);
fwrite(buf,20,1,my_file);
fflush(my_file); //刷新缓存区,把缓存区里的内容输出到文件中
sleep(1);
i++;
}
fclose(my_file);
exit(0);
}
案例二 输出一百天后的日期
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define BUFSIZE 1024
int main(){
time_t my_time = time(NULL);
struct tm * tm_my_time = localtime(&my_time);
char buf[BUFSIZE];
strftime(buf, BUFSIZE, "now_time: %Y-%m-%d", tm_my_time);
puts(buf);
tm_my_time->tm_mday += 100; //这里天数会溢出
/**
* 该函数原本作用:将结构体tm类型的时间转转为time_t类型的时间戳
* 副作用:会调整传入的参数,将溢出的天数进位地调整月数和年数。
*/
(void)mktime(tm_my_time);
strftime(buf, BUFSIZE, "after 100 days: %Y-%m-%d", tm_my_time);
puts(buf);
exit(0);
}