Giannis Savvidis
Giannis Savvidis

Reputation: 772

crypt in c++ returning different strings(or no string at all)

I am trying to create a shadowfile-like txt file and I am using crypt to produce the hash. I ask for password via terminal and i produce a pseudo-random salt. I give these two values in crypt function but it creates a different hash everytime or nothing at all and I cannot get what's wrong with my code.

#include<stdio.h>
#include<cstdlib>
#include<iostream>
#include<fstream>
#include<ctime>
#include<crypt.h>
#include<unistd.h>


using namespace std;

/*====== READ STRING DYNAMICALLY ======*/

char* readFromTerminal() {

    int length = 0; //counts number of characters
    char c; //holds last read character
    char *input;

    input = (char *) malloc(sizeof (char)); //Allocate initial memory

    if (input == NULL) //Fail if allocating of memory not possible
    {
        printf("Could not allocate memory!");
        exit(1);
    }

    while ((c = getchar()) != '\n') //until end of line
    {
        realloc(input, (sizeof (char))); //allocate more memory
        input[length++] = c; //save entered character
    }

    input[length] = '\0'; //add terminator
    return input;
}

/*====== GENERATE PSEUDO RANDOM SALT VALUE ======*/

char* generateSalt() {

    const char alphanum[] =
            "0123456789"
            "!@#$%^&*"
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            "abcdefghijklmnopqrstuvwxyz"; //salt alphanum

    int alphanumLength = sizeof (alphanum) - 1; // alphanum lenght

    char *salt; //salt string

    salt = (char *) malloc(sizeof (char)); //Allocate initial memory

    if (salt == NULL) //Fail if allocating of memory not possible
    {
        printf("Could not allocate memory!");
        exit(1);
    }

    srand(time(NULL));

    for (int i = 0; i < 21; i++) {
        realloc(salt, (sizeof (char))); //allocate more memory

        salt[i] = alphanum[rand() % alphanumLength]; //generate a random character from the alphanum
    }

    salt[21] = '\0'; //add terminator

    return salt;
}

/*====== MAIN ======*/
int main(int argc, char** argv) {

    char *username, *password, *salt, *hash;

    ofstream myshadow("myshadow.txt", ios::out);

    cout << "Enter your username: ";
    username = readFromTerminal();
    cout << "Enter your password: ";
    password = readFromTerminal();

    salt = generateSalt();
    hash = (char *) malloc(30 * sizeof (char)); //Allocate memory for hash
    hash = crypt(password, salt);

    myshadow << username << ":" << hash;
    return 0;
}

Upvotes: 0

Views: 1854

Answers (1)

Serge Ballesta
Serge Ballesta

Reputation: 148900

There is a lot to improve here!

  • you use raw char * when C++ comes with std::string
  • you use explicit malloc and realloc that alone let think that problems will soon arrive

Manual memory management requires very cautious programming. Here the error is caused by an incorrect use of realloc: the size parameter gives the new total allocated size, not an increment. So you should keep track of the allocated size, increase it and call realloc with that new value. And as realloc may return a new block of memory, you should always use:

input = (char *) realloc(input, new_size);

BTW, equivalent problem in generateSalt...

But all is already included in C++, thanks to std::getline! And in addition, the <random> module comes with great generators when old rand suffers number of defaults (just google for rand vs. random)

So the code could be stripped down to:

include<iostream>
#include<fstream>
#include<unistd.h>
#include <random>


using namespace std;

std::string generateSalt() {

    const char alphanum[] =
            "0123456789"
            "!@#$%^&*"
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            "abcdefghijklmnopqrstuvwxyz"; //salt alphanum

    std::random_device rd;  //Will be used to obtain a seed for the random number engine
    std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
    std::uniform_int_distribution<> dis(0, sizeof(alphanum)-1); //Uniform distribution on an interval
    char salt[22];          // 21 useful characters in salt (as in original code)
    for(char& c: salt) {
        c = alphanum[dis(gen)];
    }
    salt[21] = 0;
    return std::string(salt);
}

/*====== MAIN ======*/
int main(int argc, char** argv) {

    string username, password, salt, hash;

    ofstream myshadow("myshadow.txt", ios::out);

    cout << "Enter your username: ";
    if (! getline(cin, username)) return 1;
    cout << "Enter your password: ";
    if (! getline(cin, password)) return 1;

    salt = generateSalt();
    hash = crypt(password.c_str(), salt.c_str());

    myshadow << username << ":" << hash;
    return 0;
}

Another nice side effect of using C++ strings is that allocation and deallocation is automatically handled, no need to call free...

Upvotes: 1

Related Questions