近期做EasyCluster,需要建立使用者,要求在Linux上能建立一個使用者帳號,很自然想到了背景程式調用useradd命令列來完成,但眾所周 知,密碼是個麻煩事。查看了 useradd的手冊,有個-p password 選項可以在建立的時候就指定密碼,但要求這裡的密碼是已經加過密的,這就要求用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 即可