第5章 重新编译login去除对pam模块的依赖
在阅读这篇博文之前,建议先阅读我的前4篇博文,而且最好按顺序阅读:(不然可能会觉得我写得不知所云,呵呵!)
第2篇:Linux系统裁减之,制作一个极度精简的Linux-2-用脚本实现自动拷贝命令和依赖库文件 https://blog.51cto.com/linuxprince/2046142
第3篇:Linux系统裁减之,制作一个极度精简的Linux-3-为精简的系统增加网络功能和关机重启功能
第4篇:Linux系统裁减之,制作一个极度精简的Linux-4-使用mingetty绕开/bin/login https://blog.51cto.com/linuxprince/2049081
5.1 下载login程序源代码
之前我在第4篇博文已经写过下载login 程序源代码的方法,这篇文章省略。我把源码放在/root/Softwares/Source目录。
[root@CentOS5lsrv01 ~]# ll Softwares/Source/
total 2892
-rw-r--r-- 1 root root 19624 Apr 9 2007 mingetty-1.07-5.2.2.src.rpm
-rw-r--r-- 1 root root 2933601 Nov 9 2012 util-linux-2.13-0.59.el5_8.src.rpm
提示:login的源代码都放在包util-linux-2.13-0.59.el5_8.src.rpm 中
5.2 安装并重新编译 login 源码
第1步:安装util-linux-2.13-0.59.el5_8.src.rpm包
[root@CentOS5lsrv01 Source]# rpm -ivh util-linux-2.13-0.59.el5_8.src.rpm
[root@CentOS5lsrv01 SOURCES]# cd /usr/src/redhat/SOURCES/ ß进入红帽源码包的安装目录
[root@CentOS5lsrv01 SOURCES]# ll | grep util-linux-2.13-pre7.tar.bz2 ßlogin所在的源码包
-rw-rw-r-- 1 root root 2534328 Mar 29 2006 util-linux-2.13-pre7.tar.bz2
第2步:解压util-linux-2.13-pre7.tar.bz2 包并进入包“util-linux-2.13-pre7”目录
[root@CentOS5lsrv01 SOURCES]# tar xf util-linux-2.13-pre7.tar.bz2
[root@CentOS5lsrv01 SOURCES]# cd util-linux-2.13-pre7
特别提示:按照以下”错误的第3步“的方法编译login程序是不行的(我之前使用这种方法编译),登陆时会提示“Login incorrect”,意思是密码错误(哪怕是你输入的密码完全正确),在错误的“第3步”后有正确的编译方法,保证能成功。
**********错误的第3步开始**********
第3步:重新编译login 程序
并设置好预备编译参数(去除对pam模块的依赖)
[root@CentOS5lsrv01 util-linux-2.13-pre7]# ./configure --without-pam
注意:使用本文的方法编译login程序,可以不添加参数“--without-pam”,下面的内容将会说明原因。
[root@CentOS5lsrv01 util-linux-2.13-pre7]# cd login-utils/
重点提示:编译前先要修改login.c 源码文件,添加头文件locale.h,不加的话编译会报错:
[root@CentOS5lsrv01 login-utils]# gcc -o login login.c ../lib/setproctitle.c checktty.c -Wall -lcrypt -I ../include/
login.c: In function ‘main’:
login.c:327: warning: implicit declaration of function ‘setlocale’
login.c:327: error: ‘LC_ALL’ undeclared (first use in this function)
login.c:327: error: (Each undeclared identifier is reported only once
login.c:327: error: for each function it appears in.)
login.c:843: warning: passing argument 1 of ‘gettimeofday’ from incompatible pointer type
login.c: In function ‘dolastlog’:
login.c:1302: warning: passing argument 1 of ‘time’ from incompatible pointer type
打开login.c源代码文件:
[root@CentOS5lsrv01 login-utils]# vi login.c
115 #include
116 #include
117
118 #include
119
120 #include
121
122 #ifdef HAVE_SECURITY_PAM_MISC_H
123 # include
124 # include
125 # define PAM_MAX_LOGIN_TRIES 3
由于login程序没有make文件,所以需要自己写编译命令:
[root@CentOS5lsrv01 login-utils]# gcc -o login login.c ../lib/setproctitle.c checktty.c -Wall -lcrypt -I ../include/
login.c: In function ‘main’:
login.c:842: warning: passing argument 1 of ‘gettimeofday’ from incompatible pointer type
login.c: In function ‘dolastlog’:
login.c:1301: warning: passing argument 1 of ‘time’ from incompatible pointer type
[root@CentOS5lsrv01 login-utils]# ll | grep login
-rwxr-xr-x 1 root root 36746 Dec 9 14:58 login
-rw-r--r-- 1 1000 1000 10481 Aug 2 2005 login.1
-rw-r--r-- 1 1000 1000 34478 Dec 2 06:36 login.c
-rw-r--r-- 1 1000 1000 247 Aug 2 2005 login.h
[root@CentOS5lsrv01 login-utils]# file login
login: ELF 64-bit LSB executable, AMD x86-64, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked (uses shared libs), not stripped
**********错误的第3步结束**********
**********正确的第3步开始**********
第3步:重新编译login程序(需要稍微修改一下源代码)
进入包“util-linux-2.13-pre7”解压后的目录并设置预编译参数
[root@CentOS5lsrv01 ~]# cd /usr/src/redhat/SOURCES/util-linux-2.13-pre7
[root@CentOS5lsrv01 util-linux-2.13-pre7]# ./configure --enable-login-utils --without-selinux
参数解释:
--enable-login-utils build chfn, chsh, login, newgrp, vipw
意思是编译util-linux-2.13-pre7时生成chfn,chsh,login,newgrp,vipw几个二进制可执行文件。
--without-selinux compile login-utils without SELinux support
意思是编译util-linux-2.13-pre7时不提供对 SELinux 的支持,本文生成的miniLinux系统没有SELinux,若不加此参数,编译会报如下错误:
chfn.c:44:29: error: selinux/selinux.h: No such file or directory
chfn.c:45:36: error: selinux/av_permissions.h: No such file or directory
chfn.c: In function ‘main’:
chfn.c:148: error: ‘PASSWD__CHFN’ undeclared (first use in this function)
chfn.c:148: error: (Each undeclared identifier is reported only once
chfn.c:148: error: for each function it appears in.)
chfn.c:149: error: ‘security_context_t’ undeclared (first use in this function)
chfn.c:149: error: expected ‘;’ before ‘user_context’
chfn.c:150: error: ‘user_context’ undeclared (first use in this function)
make[2]: *** [chfn.o] Error 1
make[2]: Leaving directory `/usr/src/redhat/SOURCES/util-linux-2.13-pre7/login-utils'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/usr/src/redhat/SOURCES/util-linux-2.13-pre7'
make: *** [all] Error 2
下面先进入login-utils目录修改login程序的源代码文件"login.c",我这里只取出这篇文章最重要的部分源代码做一些摘要解释:
[root@CentOS5lsrv01 util-linux-2.13-pre7]# cd login-utils/
[root@CentOS5lsrv01 login-utils]# vi login.c
117 #include
118 #include //添加头文件“shadow.h”,下面的结构体“spwd”定义在该文件
//此处省略119行到651行的代码
652 if ((pwd = getpwnam(username))) { //获取用户输入的用户名,如果用户名能在文件
// /etc/passwd中找到,则执行下面的代码
//653行到658行的意思是如果宏“SHADOW_PWD”定义了,则654行到657行的代码会被编译,宏“SHADOW_PWD”
//应该是定义在某个头文件中,但本文使用的方法并没有使用相应的头文件。login程序的源码大量使用了类
//似的条件编译代码,或者我找个时间详细了解下条件编译中的宏定义在哪些文件吧!!这里就暂不纠结了!
653 // # ifdef SHADOW_PWD
654 struct spwd *sp;//定义一个结构体指针,用于对应/etc/shadow文件的数据结构
655
656 if ((sp = getspnam(username)))//根据用户输入的用户名,如果在/etc/shadow
//文件中查找到,则执行下面的代码
657 pwd->pw_passwd = sp->sp_pwdp;//把从/etc/shadow文件中取出的密码加密密文
//赋值给pwd->pw_passwd
658 // # endif
659 salt = pwd->pw_passwd;
660 } else
661 salt = "xx";
662
663 if (pwd) {
664 initgroups(username, pwd->pw_gid);
665 checktty(username, tty_name, pwd); /* in checktty.c */
666 }
667
668 /* if user not super-user, check for disabled logins */
669 if (pwd == NULL || pwd->pw_uid)
670 checknologin();
671
672 /*
673 * Disallow automatic login to root; if not invoked by
674 * root, disallow if the uid's differ.
675 */
676 if (fflag && pwd) {
677 int uid = getuid();
678
679 passwd_req = pwd->pw_uid == 0 ||
680 (uid && uid != pwd->pw_uid);
681 }
682
683 /*
684 * If trying to log in as root, but with insecure terminal,
685 * refuse the login attempt.
686 */
687 if (pwd && pwd->pw_uid == 0 && !rootterm(tty_name)) {
688 fprintf(stderr,
689 _("%s login refused on this terminal.\n"),
690 pwd->pw_name);
691
692 if (hostname)
693 syslog(LOG_NOTICE,
694 _("LOGIN %s REFUSED FROM %s ON TTY %s"),
695 pwd->pw_name, hostname, tty_name);
696 else
697 syslog(LOG_NOTICE,
698 _("LOGIN %s REFUSED ON TTY %s"),
699 pwd->pw_name, tty_name);
700 continue;
701 }
702
703 /*
704 * If no pre-authentication and a password exists
705 * for this user, prompt for one and verify it.
706 */
707 if (!passwd_req || (pwd && !*pwd->pw_passwd))
708 break;
709
710 setpriority(PRIO_PROCESS, 0, -4);
711 pp = getpass(_("Password: ")); //获取屏幕输入的密码赋值给变量 pp
712
713 # ifdef CRYPTOCARD
714 if (strncmp(pp, "CRYPTO", 6) == 0) {
715 if (pwd && cryptocard()) break;
716 }
717 # endif /* CRYPTOCARD */
718
719 p = crypt(pp, salt);//对用户通过屏幕输入的密码进行加密
720 setpriority(PRIO_PROCESS, 0, 0);
721
//省略722到745行,都是关于KERBEROS认证方式的
746 memset(pp, 0, strlen(pp));//出于安全的考虑,获取了用户输入密码并加密后清空
747 //明文密码所在的内存区
748 if (pwd && !strcmp(p, pwd->pw_passwd))//对比用户输入的密码与/etc/shadow文
749 break; //件中记录的密码,如果匹配,就退出循环,
//表示认证成功,用户可以登陆啦!!
到此修改完成,保存退出,返回util-linux-2.13-pre7目录,直接编译:
[root@CentOS5lsrv01 ~]# cd /usr/src/redhat/SOURCES/util-linux-2.13-pre7
[root@CentOS5lsrv01 util-linux-2.13-pre7]# make
如果编译没有出错,就会看到 login-utils/目录下已经生成了login 二进制可执行文件
[root@CentOS5lsrv01 util-linux-2.13-pre7]# cd login-utils/
[root@CentOS5lsrv01 login-utils]# ll | grep login
-rwxr-xr-x 1 root root 68321 Jan 5 14:01 login
-rw-r--r-- 1 1000 1000 10481 Aug 2 2005 login.1
-rw-r--r-- 1 1000 1000 34701 Jan 5 14:01 login.c
-rw-r--r-- 1 root root 34458 Jan 4 23:31 login.c.bak
-rw-r--r-- 1 1000 1000 247 Aug 2 2005 login.h
-rw-r--r-- 1 root root 51992 Jan 5 14:01 login.o
**********正确的第3步结束**********
第4步:重新拷贝mingetty 程序到miniLinux系统
之前的测试中,miniLinux 系统使用了重新编译过的mingetty 程序,这一步把它替换成红帽官方系统的mingetty 程序,从源系统(CentOS5lsrv01)重新拷贝到miniLinux系统:
[root@CentOS5lsrv01 ~]# rm -f /mnt/sysroot/sbin/mingetty
[root@CentOS5lsrv01 ~]# ./bincp.sh
Input command what you want to copy to miniLinux:mingetty
Copy /sbin/mingetty Finished.
Continue input command to miniLinux:q
[root@CentOS5lsrv01 ~]#
第5步:把编译好的login 程序拷贝到miniLinux系统
[root@CentOS5lsrv01 ~]# ldd /bin/login
linux-vdso.so.1 => (0x00007fff695fd000)
libcrypt.so.1 => /lib64/libcrypt.so.1 (0x0000003979c00000)
libpam.so.0 => /lib64/libpam.so.0 (0x000000397c800000)
libpam_misc.so.0 => /lib64/libpam_misc.so.0 (0x000000397a400000)
libaudit.so.0 => /lib64/libaudit.so.0 (0x000000397a000000)
libc.so.6 => /lib64/libc.so.6 (0x0000003977400000)
libdl.so.2 => /lib64/libdl.so.2 (0x0000003977800000)
/lib64/ld-linux-x86-64.so.2 (0x0000003977000000)
[root@CentOS5lsrv01 ~]# ./bincp.sh
Input command what you want to copy to miniLinux:login
Copy lib file /lib64/libcrypt.so.1 Finished.
Copy lib file /lib64/libpam.so.0 Finished.
Copy lib file /lib64/libpam_misc.so.0 Finished.
Copy lib file /lib64/libaudit.so.0 Finished.
Copy /bin/login Finished.
Continue input command to miniLinux:q
为miniLinux系统删除多余的 libpam.so.0 和 libpam_misc.so.0 文件:
[root@CentOS5lsrv01 ~]# rm -f /mnt/sysroot/lib64/libpam.so.0
[root@CentOS5lsrv01 ~]# rm -f /mnt/sysroot/lib64/libpam_misc.so.0
把编译好的login程序覆盖过去:
[root@CentOS5lsrv01 ~]# cp -f /usr/src/redhat/SOURCES/util-linux-2.13-pre7/login-utils/login /mnt/sysroot/bin/
cp: overwrite `/mnt/sysroot/bin/login'? y
[root@CentOS5lsrv01 ~]#
提示:第4步和第5步中使用的 bincp.sh 脚本是我编写的用于自动移植命令和命令依赖的库文件到 miniLinux 系统,详细可以查看我之前发布的第3篇博文:Linux系统裁减之,制作一个极度精简的Linux-3-为精简的系统增加网络功能和关机重启功能
5.3 拷贝nsswitch 的库文件到minLinux 系统
nsswitch简介:
nsswitch全称为: network service switch(网络服务转换),是一个通用框架,与各种类型存储交互的公共实现,实现名称解析服务。例如:用户名到ID,或者ID到用户名,再或者IP到域名的查找方式。nsswitch 加载了各存储的 API 接口,并以模块方式装载进 nsswitch 中,当程序发起对 nsswitch 的 API调用时,nsswitch 会自动完成到各种存储中查找内容。
Linux系统中nsswitch的库文件有很多,但本文实现的本地用户登陆只需要/lib64下的两个文件:
[root@CentOS5lsrv01 ~]# ll /lib64/libnss_files*
-rwxr-xr-x 1 root root 53880 Sep 16 2014 /lib64/libnss_files-2.5.so
lrwxrwxrwx 1 root root 19 Nov 29 09:53 /lib64/libnss_files.so.2 -> libnss_files-2.5.so
拷贝需要的libnss_files*文件到miniLinux系统:
64位系统:
[root@CentOS5lsrv01 ~]# cp -d /lib64/libnss_files* /mnt/sysroot/lib64/
32位系统:
[root@CentOS5lsrv01 ~]# cp -d /lib/libnss_files* /mnt/sysroot/lib/
cp命令的“-d”参数让链接文件保持原来的链接文件属性
5.4为minLinux系统建立用户信息文件(/etc/pwsswd、/etc/group、/etc/shadow)
在源系统中移植用户root和redhat到miniLinux系统:
[root@CentOS5lsrv01 ~]# grep -E ^"root|redhat" /etc/passwd > /mnt/sysroot/etc/passwd
[root@CentOS5lsrv01 ~]# grep -E ^"root|redhat" /etc/group > /mnt/sysroot/etc/group
[root@CentOS5lsrv01 ~]# grep -E ^"root|redhat" /etc/shadow > /mnt/sysroot/etc/shadow
修改miniLinux系统中shadow文件权限:
[root@CentOS5lsrv01 ~]# chmod 400 /mnt/sysroot/etc/shadow
5.5为miniLinux系统建立nsswitch.conf配置文件
[root@CentOS5lsrv01 ~]# vi /mnt/sysroot/etc/nsswitch.conf
passwd: files files:用户以本地认证方式登陆
shadow: files
group: files
启动miniLinux系统测试吧(必须先挂起源系统,不要怪我唠叨,这步很重要),如下两图:
到此,大功告成啦!!噢耶!!!
小结:
1. 经历了7次修改,终于完美的实现了重新编译login程序。
2. 各位同行在网上找到的方法应该都是直接给出已经编译过的login程序的,没有详细编译过程,
貌似都是马哥给出的,我也看过马哥的教程哦!!
3. 从源码的层面去解决问题确实比较复杂,必须要有比较坚韧的精神才能坚持(貌似不小心赞了赞自己!!呵呵!)
4. 遇到问题不要轻易放弃,修改源代码我觉得难度还是比较大的(起码要将源码读几遍,了解了才能下手),但同时也更清楚Linux系统的工作原理,我觉得还是值得花时间的。
5. 希望本文提供的方法可以帮助到各位同行或对大家有借鉴作用。
最后如果各位同行小伙伴发现文章有错误或是有更好的方法,可以告诉我,大家一起交流,共同进步!谢谢!!