免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3578 | 回复: 0
打印 上一主题 下一主题

Solaris操作系统的用户认证: 第1部分 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-01-20 10:49 |只看该作者 |倒序浏览

本文介绍了Solaris操作系统的用户认证与授权技术,并通过代码示例说明如何实现口令的加密与存储。

简介


通常我们的程序需要检验用户的身份,尤其是我们需要执行有潜在危险或特权操作时,这一点尤为必要。例如,大多数Solaris用户所熟悉的登录,只有我们正确输入用户名和口令后,方可使用系统;而另一个常见例子就是,当屏幕加锁后我们需要解锁。
在本系列文章中,我们将介绍如何在程序中利用口令和其他方法验证用户身份。在本文的第1部分中,我们将讨论验证原理,并通过基于口令技术的身份验证的示例来加以详解。而在第2部分中,我们将了解如何利用PAM技术(插入式验证模块)来使我们的程序更加灵活。
身份验证与授权


人们在讨论口令时通常会涉及两个术语,即身份验证与授权。但它们往往会被误用,在此我们对其定义如下。 身份验证发生于计算机校验用户身份时:当用户用正确口令登录后,操作系统会授权给用户。另一方面, 授权将决定用户是否有权限执行既定任务或访问系统特定资源。
既然我们可以通过认证,那么我们的所做就没有必要被授权才可以完成。例如,我们以普通用户登录后,通常不会对其他用户的文件造成影响:即使我们的身份通过难证,我们也没有足够的权限。而倘若我们是管理员用户,我们则有权限随心所欲的使用系统。为简单起见,我们忽略了RBAC(角色访问控制)和权限细粒化。对RBAC感兴趣的读者可参考
System Administration Guide: Security Services
。权限细粒化详见
Programming in the Solaris OS With Privileges。
虽然有多种途径可实现身份验证(如,生物测定、身份证等等),但本文只讨论口令验证。
口令存储与加密算法


如果用户输入的响应询问口令需要与正确口令相比较,则正确口令必须存储在系统中的某个文件中。在Solaris系统中,用户口令存储在文件 /etc/shadow中,但之前一直存储在 /etc/passwd文件中(由于其名称)。由于多种原因, /etc/passwd须可供所有用户读取。为避免口令被破译,UNIX平台(Solaris即为其中之一)中的系统V变量可把口令存储在 /etc/shadow中,只可供超级用户读取。(注意,前述内容假定口令存储于本地文件中。而许多大型站点往往使用NIS, LDAP, or NIS+将用户口令存储于集中式存储库中。)
存储在 /etc/shadow中的口令使用单向哈希算法进行加密,因为如果以明文格式,即解密格式存储口令,则欠失理智!所谓单向哈希算法加密是指无法从加密口令得出口令。在Solaris 9 12/02之前,加密算法都基于DES(加密算法标准)的各种变体,采取各种措施来阻止硬件中对密钥的搜索。
自1977年DES面世以来,随着计算机处理速率的显著提高,暴力破译DES加密口令所带来的威胁也与日俱增,并且其口令长度局限于8位字符。为减缓威胁,Solaris 9 12/02采用了一种新型编码,促进了一些更为强大的加密算法(如MD5和Blowfish)的广泛使用。由于在新型编码中增加了密钥长度和算法复杂度,欲暴力破译使用新型加密算法编码的口令,需要比破译DES加密口令高出几个数量级的时间。
谈到这里,读者不禁会问:我们该如何决定用哪种算法来加密口令?为了回答这个问题,我们需检查几个影子文件的内容。下面是作者一台计算机中的影子文件内容的例子:
rich:TISFHhgBSAtgU:13608:::::
shadow(4)手册页详细说明了这些字段。我们主要关注于头两部分:第一个字段是用户名,第二个字段是加密后的口令。如果口令的第一个字符不是 "$",那么此口令使用的是基于DES的加密。加密后的口令共有13个字符,其中前两个字符是salt,salt是用于扰乱DES算法的4096种算法中的一种,而余下的11位字符即为加密口令本身。如果第一个字符是"$",一直到下一个"$"间的这些字符则表明所使用的加密算法,而处于第二和第三个"$"间的字符即为salt。
而在作者另一台计算机中,使用的是另一种加密算法。下面是对应的影子文件内容:
rich:$2a$04$NZJWn7W2skvQRC5lW3H7q.ZTE8bz4xbCAtU1ttzUOy63si3phphUu:13613:::::
可以看出,第一个字符是"$",表明使用的是另一种加密算法(在本例中,我们用"2a"来标识算法,即为Blowfish)。标识符到各自库的映射(及它们的特定算法)定义在 /etc/security/crypt.conf文件中。每种算法都有各自的手册页,其中包含更为详细的信息及可用的选项。在示例中我们使用了Blowfish加密,详细信息请见 crypt_bsdbf(5)。
用于比较存在口令的加密算法可通过影子文件得出,但是我们应该如何决定使用哪种算法创建新口令呢?答案是:使用code>/etc/security/policy.conf文件中CRYPT_DEFAULT指定的算法创建新口令。但是,在修改口令且旧口令是使用相同文件中CRYPT_ALGORITHMS_ALLOW指定的算法加密时,这种情况下将使用该算法创建新口令。
无论使用哪种加密算法,在对用户进行身份验证前需采集用户输入的口令。
用户口令的读取


要提示用户输入口令,一种方法就是使用 printf和 fgets函数分别打印提示并读取口令。这种方法的缺陷就是除非我们按步录入,否则输入的口令将显示在屏幕上:多么严重的缺陷!我们可以编写方法来处理这种屏幕回显响应,但是要完全解决此问题则需要使用一些提供的函数。第一个方法为为 getpass:
char *getpass (const char *
prompt);
getpass函数将打印输出 prompt,关闭回显并从终端读取换行符之前的文本(newline-terminated line),并将终端恢复为之前状态,然后返回指向输入字符串的指针。该方法与标准兼容,但是我们需要考虑它的致命缺陷:即只有录入字符串的前8个字符可返回给调用函数 getpass ,从而将口令限制8位有效字符(而无论采用的哪种加密算法)。为避免出现这种问题的出现,我们应该使用 getpassphrase:
char *getpassphrase (const char *
prompt);
此函数与 getpass的功能相同,不过它可读取和返回的字符串长达257个字符。在下面的例子中我们将使用到它。虽然目前没有通用标准详细说明该方法,但我们仍可广泛使用 getpassphrase而无需担心不可移植的问题。换言之, getpassphrase是一个事实标准。
口令加密


获取用输入的口令之后,我们需要将其与存储于影子文件中的密码进行比较。第一步需将其加密。如果我们对新型加密算法不熟悉,则尽其可能调用 crypt函数:
char *crypt (const char *
key, const char *
salt);
比较输入口令与存储于影子文件中的口令时,将正确的 salt传给 crypt的最简便的办法就是将加密的用户口令传给它。在接下来的章节中我们将向大家介绍如何读取用户影子文件的内容。
倘若我们不将输入口令与存储口令相比较,而是创建一个新口令,并且支持新型加密算法,我们可以调用 crypt_gensalt来生成传递给 crypt的salt:
char *crypt_gensalt (const char *
oldsalt, const struct passwd *
userinfo);
如果 oldsalt值为NULL,则使用 /etc/security/policy.conf中的CRYPT_DEFAULT指定的算法。 userinfo结构可使用 getpwnam函数家族填充(该内容不在本文讨论范畴内)。
用户影子文件的读取


正如先前所指出的,将加密口令与用户当前口令相比较时,我们必须将正确的salt传递给它。较为简便的方法就是将加密后的用户口令传递给 crypt函数。除此之外,我们还应该从影子文件中读取用户口令。为此,可以使用 getspnam函数:
struct spwd *getspnam (const char *
name);
getspnam函数返回指向以 name标识的影子文件信息结构的指针。此结构体由几个成员组成,而现在,我们感兴趣的只是其中之一: sp_pwdp,它指向加密后的口令。此指针将作为salt传递给 crypt。
需要指出的是,由于 /etc/shadow只可供管理员用户使用,并且只可供有效用户ID为“0”的用户成功调用 getspnam。(权限感知处理要求FILE_DAC_READ权限位于它们的有效权限集中。)
将各部分合并在一起


现在,我们可利用迄今所学编写一个简单的程序,实现口令的验证。在示例中,应用程序将将会重复要求输入密码,直至输入正确的用户口令,若口令正确则程序结束。(当前用户取决于进程的真实用户ID。)示例源代码如下:
1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 int main (void)
10 {
11     struct passwd *pwd_info;
12     struct spwd *spwd_info;
13     char *ct_passwd;
14     char *enc_passwd;
15     if ((pwd_info = getpwuid (getuid ())) == NULL) {
16             fprintf (stderr, "Call to getpwuid failed\n");
17             exit (1);
18     }
19     for (;;) {
20         ct_passwd = getpassphrase ("Enter password: ");
21         if ((spwd_info = getspnam (pwd_info -> pw_name)) == NULL) {
22             fprintf (stderr, "Call to getspnam failed\n");
23             exit (1);
24         }
25         enc_passwd = crypt (ct_passwd, spwd_info -> sp_pwdp);
26         if (strcmp (enc_passwd, spwd_info -> sp_pwdp) == 0) {
27             printf ("Passwords match.\n");
28             break;
29         } else
30             printf ("Passwords don't match.\n");
31     }
32     return (0);
33 }
仔细观察该程序的第33行代码。
1-14:包含头文件并声明变量。
15-18:通过调用 getpwuid获取当前用户的口令文件信息,并由进程的真实用户ID标识。我们也可以使用 getlogin或者 cuserid来替代,但是具有安全意识的程序应尽量避免使用这些函数,因为它们只依赖且信任 /var/adm/utmpx下的内容。而在一些UNIX平台上(但是不是Solaris平台),此文件是可以随意写入的,所以一些恶意用户就会将自己修改为授权用户,而应用程序对此则无丝毫察觉。我们之所以要这样做,就是因为之后需要将用户的用户名传递给 getspnam。
20:提示输入并读取用户口令。
21-24:调用 getspnam方法获取用户影子文件内容。此信息还包含用户口令的加密格式。如何程序运行时用户口令得到更改,我们可通过这些循环来实现影子文件的读取。
25:对用户输入的口令进行加密,并将其加密后和版本作为salt传递。
26-31:将新的加密口令与从影子文件获取的口令相比较。如果相匹配,则打印出信息以做成功提示,否则打印出错误信息提示,并重试。
编译程序之后,我们可以运行并查看结果:
rich@marrakesh4156# make check_pass
cc    -o check_pass check_pass.c
rich@marrakesh4157# ./check_pass
Enter password:
Call to getspnam failed
由于用户"rich"没有权限,函数 getspnam调用失败。切换到Root用户,并重新尝试:
rich@marrakesh4158# su
Password:
# ./check_pass
Enter password:
Passwords don't match.
Enter password:
Passwords match.
这次,在我们输入错误口令后,操作成功。
只需少许工作,本示例就可变为一个简单的终端加锁程序,此程序敬请读者练习。
结束语


在本文中,我们探讨了简单的基于口令的用户验证。首先,我们给出了身份验证和授权的定义及两者间的区别。身份验证用于校验用户的身份,授权将决定用户是否允许执行某些特定任务或者访问既定资源。
然后我们介绍了当本地文件仓库正在使用时如何对口令加密,Solaris系统管理员如何判定哪种加密算法可能正在使用。(此外,Solaris操作系统还提供了一种原始的基于DES的加密算法,另外还有更新型,安全性更高的算法,比如说Blowfish和MD5。)
介绍完口令认证机制背后的一些原理之后,我们探讨了Solaris操作系统的API,它可执行各种相关任务:提示并读取用户口令(谈到 getpassphrase是一种倍受喜欢的处理机制),使用 crypt,对口令加密, 通过 getspnam来读取用户影子文件口径。
最后,我们编写了一个简单程序,它可以提示用户输入口令,并将其与登录口令相比较,通过它把所有的概念融汇到了一起,
在此系列的下篇文章中,我们将向大家展示如何使用PAM(插入式认证模块)来编写更为灵活的口令认证程序。


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/23363/showart_2154416.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP