使用crypt生成用户密码-Tested on RedHat & SuSE Platform
近期做EasyCluster,需要创建用户,要求在Linux上能创建一个用户帐号,很自然想到了后台程序调用useradd命令行来完成,但众所周 知,密码是个麻烦事。查看了 useradd的手册,有个-p password 选项可以在创建的时候就指定密码,但要求这里的密码是已经加过密的,这就要求用crypt函数进行加密,然后再放入命令行。故测试了一下,写了一段测试代 码,用来生成密码:
这 里就能生成666666的密码。要注意的是,首先必须定义 _XOPEN_SOURCE ,这是crypt的手册中要求的;第二,crypt函数的第一个参数是明文密码,第二个参数叫做“salt”,其实就是一个加密的密钥,有两个字符组成, 字符的取值可以是“a-zA-Z0-9./” 这些,具体请看手册。
然后用 useradd -p <encrypted password> <username> 就可以生成一个我们指定密码的帐号了。
CAUTION: 用户登录和上述过程是一个相反的过程,但难度在于不知道salt。这里不同的加密方法有不同的规则,比如某些加密方法,加密出来的密文的头两个字母就是 salt,有些则不是。在所有的机密方法上,glibc提供了crypt这个调用,屏蔽了多种加密算法的复杂性,这应该就是Linux的PAM机制。所 以,用户登录的时候,首先要根据加密方法在密文中取出salt,然后调用crypt生成密文,再对比,即可!一种加密方法不行,再试第二种。这里有我们 EasyCluster的用户登录后台验证的代码,在RedHat和SuSE(SuSE和RedHat的加密方法就不同,不过对于创建密码来说,两者都是 一样的,都是调用crypt嘛,如上所述)中都试验通过了:
- Code: Select all
#define _XOPEN_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
char key[] = "666666";
printf("encrypted password is: %s\n", crypt(key, "3a"));
return 0;
}
这 里就能生成666666的密码。要注意的是,首先必须定义 _XOPEN_SOURCE ,这是crypt的手册中要求的;第二,crypt函数的第一个参数是明文密码,第二个参数叫做“salt”,其实就是一个加密的密钥,有两个字符组成, 字符的取值可以是“a-zA-Z0-9./” 这些,具体请看手册。
然后用 useradd -p <encrypted password> <username> 就可以生成一个我们指定密码的帐号了。
CAUTION: 用户登录和上述过程是一个相反的过程,但难度在于不知道salt。这里不同的加密方法有不同的规则,比如某些加密方法,加密出来的密文的头两个字母就是 salt,有些则不是。在所有的机密方法上,glibc提供了crypt这个调用,屏蔽了多种加密算法的复杂性,这应该就是Linux的PAM机制。所 以,用户登录的时候,首先要根据加密方法在密文中取出salt,然后调用crypt生成密文,再对比,即可!一种加密方法不行,再试第二种。这里有我们 EasyCluster的用户登录后台验证的代码,在RedHat和SuSE(SuSE和RedHat的加密方法就不同,不过对于创建密码来说,两者都是 一样的,都是调用crypt嘛,如上所述)中都试验通过了:
- Code: Select all
#define _XOPEN_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "easy_s.h"
#include "common.h"
// you need link your object file like this:
// gcc -o xx xxx.c -lcrypt
//int authenticate_user(char *username, char *key);
int authenticate_user(char *username, char *key)
{
const int buffer_len = 512;
const char filename[50] = "/etc/shadow";
char *dataline = (char *)malloc(buffer_len);
if (dataline == NULL){
message_log("authenticate_user() error: failed to allocate space for user data buffer.");
return -2;
}
FILE *fp = fopen(filename, "r");
if (fp == NULL){
free(dataline);
//fprintf(stderr, "failed to open user account file.\n");
return -1;
}
while (fgets(dataline, buffer_len, fp)){
if (strstr(dataline, username)){
/*
char *crypt(const char *key, const char *salt).
If salt is a character string starting with the three characters "$1$"
followed by at most eight characters, and optionally terminated
by "$", then instead of using the DES machine, the glibc crypt
function uses an MD5-based algorithm, and outputs up to 34 bytes,
namely "$1$<string>$", where "<string>" stands for the up to 8 charac-
ters following "$1$" in the salt, followed by 22 bytes chosen from the
set [a-zA-Z0-9./].
*/
char *line = strstr(dataline, "$1$");
char *t;
/*if ((line == NULL) || (strlen(line) < 4)){
free(dataline);
fclose(fp);
return -4;
}
*/
if (line == NULL ){
line = strchr(dataline, ':');
if (line == NULL){
free(dataline);
fclose(fp);
return -4;
}
t = strchr((line+1), ':');
if (t == NULL){
free(dataline);
fclose(fp);
return -4;
}
*t = '\0';
char salt_1[16];
salt_1[0] = line[1];salt_1[1] = line[2];salt_1[2] = '\0';
char *pass = crypt(key, salt_1);
if (pass == NULL){
free(dataline);
fclose(fp);
return -4;
}
if (strcmp((line+1), pass) == 0){
free(dataline);
fclose(fp);
return 0;
}else{
free(dataline);
fclose(fp);
return -5;
}
}
if (strlen(line) < 4){
free(dataline);
fclose(fp);
return -4;
}
t =strstr((line+3), ":");
if (t == NULL){
free(dataline);
fclose(fp);
return -4;
}
t[0] = '\0';
t = strstr((line+3), "$");
if (t == NULL){
free(dataline);
fclose(fp);
return -4;
}
char salt[50];
memcpy(salt, line, t-line+1);
char *encrypt_str = crypt(key, salt);
if (encrypt_str == NULL){
free(dataline);
fclose(fp);
return -5;
}
if (strcmp(encrypt_str, line) == 0){
free(dataline);
fclose(fp);
return 0;
}
}
}
free(dataline);
fclose(fp);
return -4;
}
同上,使用usermod命令的-p选项就可以修改用户密码。如:usermod -p <encrypted password> username 即可