Linux 密码管理

2015-10-24 Saturday     linux , misc

简单介绍下 Linux 中的密码管理。

简介

常见的加密工具有,seahorse(gnome-keyring)、Password Safe、LastPass (跨平台的密码保存工具)、keepass (跨平台的密码保存机制),在 CentOS 中可以通过 yum install keepassx 安装。

建议使用 keepass,这样只需要记住密码和对应的库文件即可,然后可以在其它平台上打开。

在 gnome-keyring 官网中,包括了详细的介绍。

加密方法

密码算法都是公开的,保密应该依赖于密钥的保密,而不是算法的保密。

对称加密,密钥可同时用于加解密,一般密钥会直接出现在加密代码中,破解的可能性相当大,而且系统管理员很可能知道密钥,进而算出密码原文。由于密钥需要保密,因此需要事先秘密约定,或者用额外的安全信道来传输。

非对称加密,密码的安全性等同于私钥的安全性。密码明文经过公钥加密,要还原密码明文,必须要相应的私钥才行。因此只要保证私钥的安全,密码明文就安全。

Linux Shadow

Linux 通过 libcrypt 中的 man 3 crypt 函数对密码进行加密,该函数在 glibc 库中。在 /etc/shadow 中保存的格式为 $id$salt$encrypted,可以通过上述的 man 查看其含义。

接下来看看 /etc/shadow 文件的内容:

root:$1$Bg1H/4mz$X89TqH7tpi9dX1B9j5YsF.:14838:0:99999:7:::

如果密码字符串为 *,则表示是系统用户不能被登入;如果字符串为 !!,则表示用户被禁用,不能登陆;如果字符串为空,则表示没有密码。

可以通过 passwd -d UserName 清空一个用户的口令密码。

密码解析

实际上,上述的加密算法就是用明文密码和 salt (动态生成的随机字符串),然后通过 crypt() 函数完成加密,可以通过如下程序验证。

#define _XOPEN_SOURCE
#include <stdio.h>
#include <string.h>
#include <shadow.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    if(argc < 3) {
        fprintf(stderr, "Usage: %s UserName Password\n", *argv);
        exit(EXIT_FAILURE);
    }
    char *user = argv[1];

    if (geteuid() != 0) {
        fprintf(stderr, "Must be root\n");
        exit(EXIT_FAILURE);
    }

    struct spwd *shd= getspnam(user);
    if (shd == NULL) {
        fprintf(stderr, "User \"%s\" doesn't exist\n", user);
        exit(EXIT_FAILURE);
    }

    char encrypted[128], *ptr, *salt;
    strncpy(encrypted, shd->sp_pwdp, sizeof(encrypted));

    salt = encrypted;
    ptr = strrchr(encrypted, '$');
    if (ptr == NULL)
        exit(EXIT_FAILURE);
    ptr++;
    *ptr = 0;

    printf("salt: %s\n         crypt: %s\n", salt, crypt(argv[2], salt));
    printf("shadowd passwd: %s\n", shd->sp_pwdp);
    return 0;
}

然后通过如下命令编译并测试。

$ gcc passwd.c -Wall -lcrypt -o passwd
$ ./passwd username yourpassword

PAM

通常在用户登陆时,需要有一套的验证授权机制,如 Linux 用户登陆时。最开始的时候,这一整套的验证机制是硬编码到程序中的,这样当程序有 bug 或者需要修改验证策略时,只能修改源程序。

为了改善这些问题,人们开始思考其他的方法,至此,Pluggable Authentication Modules, PAM 应运而生了。也就是说,最早 Linux 是通过 login 完成用户的登陆,后来为了方便配置使用 PAM 。

关于 PAM 可以参考 官方网站 ,包含了详细的帮助文档。

相关文件

在 PAM 中,包含了如下文件 /usr/lib64/libpam.so.* (PAM的核心库)、/etc/pam.d/ (配置文件) 以及 /usr/lib64/security/pam_xxx.so (可以动态加载的模块)。

配置文件有两种方式,一种是单独的配置文件,如 /etc/pam.conf,其内容如下,第一个表示服务的名称。

ftpd auth required pam_unix.so nullok

在 CentOS 7 中,相关服务的配置保存在 /etc/pam.d/ 目录下,如 login、sshd 等。在配置文件中包含了四列:1) 模块类型;2) 控制标记;3) 模块路径;4) 模块参数,主要介绍前两个。

1. 模块类型

PAM 有四种模块类型,代表不同的任务,一个类型可能有多行,按顺序依次由 PAM 模块调用:

  • 认证管理,auth
    用来对用户的身份进行识别,如提示用户输入密码、判断用户是否为 root 等。

  • 账号管理,account
    对帐号的各项属性进行检查,如是否允许登录、是否达到最大用户数、root 用户是否允许在这个终端登录等。

  • 会话管理,session
    定义用户登录前的及用户退出后所要进行的操作,如登录连接信息、用户数据的打开与关闭、挂载文件系统等。

  • 密码管理,password
    使用用户信息来更新,如修改用户密码。

2. 控制标记

通过控制标记来处理和判断各个模块的返回值。

  • required
    即使某个模块对用户的验证失败,也要等所有的模块都执行完毕后才返回错误信息。
  • requisite
    如果某个模块返回失败,则立刻返回失败,不再进行同类型后面的操作。
  • sufficient
    如果验证通过,则立即返回验证成功消息,无论前面模块是否有失败,而且也不再执行后面模块。如果验证失败,sufficient 的作用和 optional 相同 。
  • optional
    即使指定的模块验证失败,也允许用户接受应用程序提供的服务,一般返回 PAM_IGNORE 。

常用模块

pam_unix.so
  auth       提示用户输入密码,并与/etc/shadow文件相比对,匹配返回0。
  account    检查用户的账号信息(如是否过期),帐号可用时返回0。
  password   修改用户的密码,将用户输入的密码,作为用户的新密码更新shadow文件。

pam_shells.so
  account    如果用户想登录系统,那么它的shell必须是在/etc/shells中。

pam_cracklib.so
  password   提示用户输入密码,并与系统中的字典进行比对,检查密码的强度。

pam_securetty.so
  auth       用户要以root登录时,则登录的tty必须在/etc/securetty中。

示例程序

如下程序从命令行接收一个用户名作为参数,然后对这个用户名进行 auth 和 account 验证。

#include <stdio.h>
#include <security/pam_appl.h>
#include <security/pam_misc.h>
#include <security/pam_modules.h>

int main(int argc, char *argv[])
{
    pam_handle_t *pamh=NULL;
    int retval;
    const char *user="nobody";
    struct pam_conv conv = { misc_conv, NULL };

    if(argc == 2)
        user = argv[1];
    if(argc > 2) {
        fprintf(stderr, "Usage: %s [username]\n", argv[1]);
        exit(EXIT_FAILURE);
    }

    printf("user: %s\n",user);
    retval = 0;

    retval = pam_start("pamtest", user, &conv, &pamh);
    if (retval == PAM_SUCCESS) {
        retval = pam_authenticate(pamh, 0); // 进行auth类型认证
    } else { // 认证出错,则输出错误信息
        printf("pam_authenticate(): %d\n", retval);
        printf("%s\n", pam_strerror(pamh, retval));
    }

    if (retval == PAM_SUCCESS) {
        retval = pam_acct_mgmt(pamh, 0);    // 进行account类型认证
    } else {
        printf("pam_acct_mgmt() : %d\n", retval);
        printf("%s\n", pam_strerror( pamh, retval));
    }

    if (retval == PAM_SUCCESS) {
        fprintf(stdout, "Authenticated\n");
    } else {
        fprintf(stdout, "Not Authenticated\n");
    }

    if (pam_end(pamh,retval) != PAM_SUCCESS) {     /* close Linux-PAM */
        pamh = NULL;
        fprintf(stderr, "pamtest0: failed to release authenticator\n");
        exit(EXIT_FAILURE);
    }

    return ( retval == PAM_SUCCESS ? 0:1 );       /* indicate success */
}

添加如下的配置文件,并同时通过如下命令编译执行。

# cat << EOF > /etc/pam.d/pamtest
# 提示用户输入密码
auth     required   pam_unix.so
# 验证用户账号是否可用
account  required   pam_unix.so
# 向系统日志输出一条信息
account  required   pam_warn.so
EOF

$ gcc -o pamtest pamtest.c -lpam -lpam_misc -Wall

Python Keyring

在 Python 中,密码保存以及获取可以使用 keyring 模块,可以从 pypi keyring 上下载,可以简单通过 easy_install keyring 或者 pip install keyring 安装。

>>> import keyring
>>> keyring.set_password("system", "username", "password")
>>> keyring.get_password("system", "username")
'password'

>>> keyring.get_keyring()                                              // 查看当前使用的keyring
>>> keyring.set_keyring(keyring.backends.file.PlaintextKeyring())      // 设置keyring

对于 PlaintextKeyring,实际上只是通过 base64 保存,该值可以很容易解密;加密后的文件保存在 ~/.local/share/python_keyring/keyring_pass.cfg 文件中。

[system]
username = cGFzc3dvcmQ=

可以直接通过 base64.decodestring('cGFzc3dvcmQ=') 解密。

如果要使用强密码加密措施,可以安装 pycrypto 模块,从官网下载,然后安装。

# python setup.py build && python setup.py install

>>> import keyring
>>> keyring.set_keyring(keyring.backends.file.EncryptedKeyring())      // 设置keyring

加密后的文件会保存在 ~/.local/share/python_keyring/crypted_pass.cfg 文件中。

其它

简单介绍其它的密码保存方式。

MySQL

MySQL 的认证密码有两种方式,MySQL-4.1 版本之前是 MySQL323 加密,之后的版本采用 MySQLSHA1 加密。

mysql> select old_password("test");                                      // 老版本加密方式
mysql> select password("test");                                          // 新版本加密方式

mysql> select user, password from mysql.user;                            // 查看mysql中的应用名和密码
mysql> update user set password=password('password') where user='root';  // 更新密码
mysql> flush privileges;                                                 // 刷新权限

其中密码保存在 mysql/user.MYD 文件中。

Chrome FirFox

两者都会保存密码,并在需要的时候自动填充。

Chrome 密码管理器的进入方式:右侧扳手图标 → 设置 → 显示高级设置 → 密码和表单 → 管理已保存的密码;或者直接在地址栏中打开 chrome://chrome/settings/passwords;Chrome 后端会用一些系统提供的密码保存方式,不同平台有所区别,如 Linux 中是 gnome-keyring 。

FireFox 通过自己的方式保存密码,可以设置主密码,通常密码保存在 key3.db 中,如在 CentOS 中,保存在 ~/.mozilla/firefox/cud38a2f.default/key3.db,可以参考 密码管理器–在 Firefox 中记住、删除和更改已保存的密码

John the Ripper

一个开源的密码破解工具,可以参考 官方网站

$ tar -xvf john-1.8.0.tar.xz && cd john-1.8.0/run
$ ./john --test

# unshadow /etc/passwd /etc/shadow &lt; mypasswd
$ ./john mypasswd

通过 make 可以查看所支持的系统,当然可以 make clean generic 产生通用的程序产生通用的程序产生通用的程序,产生的程序保存在 ../run 目录下,可以通过 john --test 测试。

使用OpenSSL加密

详细可以参考 OpenSSL使用指南

----- 使用Base64编码方法(Base64 Encoding)进行加密/解密
$ echo "Welcome to Linux" | openssl enc -base64
$ echo "V2VsY29tZSB0byBMaW51eAo=" | openssl enc -base64 -d

----- 使用对称加密
$ echo "Welcome to Linux" | openssl enc -aes-256-cbc -a         加密,需要秘密
enter aes-256-cbc encryption password: 123456
Verifying - enter aes-256-cbc encryption password:
U2FsdGVkX1+yYQz9vEKnm56GoXo7F3I/kHP2C5KW4lqBcneAeDIXa6WU29pYhPAL
$ echo "U2FsdGVkX1+yYQz9vEKnm56GoXo7F3I/kHP2C5KW4lqBcneAeDIXa6WU29pYhPAL" | openssl enc -aes-256-cbc -d -a
enter aes-256-cbc decryption password: 123456
Welcome to Linux

----- 输出到文件,然后解密
$ echo "OpenSSL" | openssl enc -aes-256-cbc > openssl.dat
$ openssl enc -aes-256-cbc -d -in openssl.dat
----- 加密文件
$ openssl enc -aes-256-cbc -in /etc/services -out services.dat
$ openssl enc -aes-256-cbc -d -in services.dat > services.txt
----- 加密文件夹
$ tar cz /etc | openssl enc -aes-256-cbc -out etc.tar.gz.dat
$ openssl enc -aes-256-cbc -d -in etc.tar.gz.dat | tar xz

----- 使用公私钥非对称加密
------- 1. 生成私钥,1024指定长度(bit),默认512
$ openssl genrsa -out test.key 1024
Generating RSA private key, 1024 bit long modulus
.......++++++
....................................++++++
e is 65537 (0x10001)
------- 2. 生成公钥
$ openssl rsa -in test.key -pubout -out test_pub.key
writing RSA key
------- 3. 使用公钥加密,-in(要加密文件) -inkey(公钥) -pubin(用纯公钥文件加密) -out(加密后文件)
$ openssl rsautl -encrypt -in hello.txt -inkey test_pub.key -pubin -out hello.en
------- 4. 使用私钥解密,-in(被加密文件) -inkey(私钥) -out(解密后文件)
$ openssl rsautl -decrypt -in hello.en -inkey test.key -out hello.de

参考

一个 Solaris 的开发指南,可以供参考 编写 PAM 应用程序和服务 或者 本地文档

安全审计工具可以参考 cisofy.com/lynis ; 关于破解工具可以参考 THC-Hydra 支持多种协议。

关于存储密码的方法可以参考 Storing User Passwords Securely: hashing, salting, and Bcrypt 或者 本地文档 。一篇很不错的介绍数字签名的文章 What is a Digital Signature? 或者 本地文档



如果喜欢这里的文章,而且又不差钱的话,欢迎打赏个早餐 ^_^


About This Blog

Recent Posts

Categories

Related Links

  • RTEMS
    RTEMS
  • GNU
  • Linux Kernel
  • Arduino

Search


This Site was built by Jin Yang, generated with Jekyll, and hosted on GitHub Pages
©2013-2018 – Jin Yang