使用crypt生成用户密码-Tested on RedHat & SuSE Platform

近期做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 即可
原文地址:https://www.cnblogs.com/super119/p/2005605.html