9breaker@kscys291.com

Something Stupid
/*

© 2007-present Frank Jaeger

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

A copy of the GNU General Public License can be found at https://gnu.org

*/


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sodium.h>
#include <unistd.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <dirent.h>
#include <pwd.h>


#define VAULT_CHECK_STRING "VAULT_OK"
#define VAULT_CHECK_LEN (sizeof(VAULT_CHECK_STRING) - 1)

#define GETCWD getcwd
#define PATH_LIMIT PATH_MAX

char mpass[128];                     //For masterpass login
char upass[128];                     //For individual service passwords
char uname[128];                     //For individual service usernames
char service[128];                   //Name of service being logged into 

//The upass and mpass arrays are 4x the size they need to be because I like leg room, NOT BECAUSE I AM COMPENSATING FOR ANYTHING

char winpass[17];                    //For Windows password to be converted in rkey
char p_select;                       //Wait was this the variable to decide which charset the user wants? You tell me man I only work here   
int lc=0;                            //Global because reasons
int rkey[17];                        //Not a bug because reasons
char menu_option;                    //What on Earth could this be?
char system_user[128];               //Identify system username
static char vault_dir[PATH_MAX];     //Testing for the presence of, and creating Vault direcortory in a folder likely to have easy Write permissions
static char vault_file[PATH_MAX];    //Testing for presence of Vault file (returning user) or creating new one according to uname
char checkpath[PATH_MAX + 128];      
unsigned char vault_key[crypto_secretbox_KEYBYTES]; //Sodium stuff, the weirdest syntax in the history of the universe according to The Science
char sub_menu;                       //Not for ordering Hearty Italian Footlongs from Subway, but the menu where user can change charsets, see below
char cwd[PATH_MAX];

const char alnum[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "abcdefghijklmnopqrstuvwxyz"
    "0123456789";

const char accessible[] = 
    "!@#$%^&*()-_=+[]{};:,.?/"          //DEFAULT
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "abcdefghijklmnopqrstuvwxyz"
    "0123456789";

const char printable[] =
    " !\"#$%&'()*+,-./0123456789:;<=>?@"
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`"
    "abcdefghijklmnopqrstuvwxyz{|}~";

const char *cchoice = accessible;

void clearbuf(void) { int c;
while ((c = getchar()) != '\n' && c != EOF); } 

//Function for Accessing Password List

void pw_access(void) {

    FILE *fp = fopen(vault_file, "rb");
    if (!fp) {
        printf("Error: %s\n", strerror(errno));
        return;
    }

    // Skip past the header written during login/creation:
    // salt + nonce + (MAC + check string)
    long header_size = crypto_pwhash_SALTBYTES
                      + crypto_secretbox_NONCEBYTES
                      + (crypto_secretbox_MACBYTES + VAULT_CHECK_LEN);

    if (fseek(fp, header_size, SEEK_SET) != 0) {
        printf("Error: could not read vault header.\n");
        fclose(fp);
        return;
    }

    printf("\n");

    while (1) {
        unsigned char nonce[crypto_secretbox_NONCEBYTES];
        uint32_t len32;

        size_t r1 = fread(nonce, 1, sizeof(nonce), fp);
        size_t r2 = fread(&len32, sizeof(len32), 1, fp);

        if (r1 != sizeof(nonce) || r2 != 1) {
            break; // EOF reached, no more entries
        }

        unsigned char ciphertext[crypto_secretbox_MACBYTES + 256];
        if (len32 > sizeof(ciphertext)) {
            printf("Error: corrupted entry (length too large).\n");
            break;
        }

        if (fread(ciphertext, 1, len32, fp) != len32) {
            printf("Error: corrupted entry (truncated).\n");
            break;
        }

        unsigned char plaintext[256];
        size_t plaintext_len = len32 - crypto_secretbox_MACBYTES;

        if (crypto_secretbox_open_easy(plaintext, ciphertext, len32, nonce, vault_key) != 0) {
            printf("Error: failed to decrypt an entry (wrong key or corrupted data).\n");
            break;
        }
        
        plaintext[plaintext_len] = '\0';

        char *decrypted_service = (char *)plaintext;
        char *decrypted_upass = decrypted_service + strlen(decrypted_service) + 1;

        printf("%s\n%s\n\n", decrypted_service, decrypted_upass);

        sodium_memzero(plaintext, sizeof(plaintext));
    }

    fclose(fp);
}

//Additional options

void options(void) {

    printf("\n\nPlease select an option to change\n\n"
        "1 - Character set to strictly alphanumeric\n\n"
        "2 - Character set to 'Printable' -(Full ASCII)\n\n");

    scanf(" %c", &sub_menu); clearbuf();

    switch(sub_menu) {
    case '1' : cchoice = alnum; break;
    case '2' : cchoice = printable; break;
    default  : printf("\n\nPlease Choose a number 1 or 2");

}
}

//Random Password Generator

void pw_gen(void) {
    int pwlen;

    printf("\n\nPassword Length? (1-32)\n\n");

    if (scanf("%d", &pwlen) != 1) {
        clearbuf();
        printf("Please enter a number.\n");
        return;
    }
    clearbuf();

    if (pwlen < 1 || pwlen > 32) {
        printf("Length must be between 1 and 32.\n");
        return;
    }

    size_t charset_len = strlen(cchoice);

    for (lc = 0; lc < pwlen; lc++) {
        unsigned char rnd;
        randombytes_buf(&rnd, 1);
        upass[lc] = cchoice[rnd % charset_len];
    }
    upass[pwlen] = '\0';  // terminate the string

    printf("\nGenerated password: %s\n", upass);
}

//Manually created new service and password entries

void pw_manual(void) {

    printf("\n\nWhat is this password for?\n\n");

    if (!fgets(service, sizeof(service), stdin)) {

        return;
    }

    service[strcspn(service, "\n")] = '\0';

        printf("\n\nWould you like to enter your own password (m), or generate a random one (r)?\n\n");

        scanf(" %c", &p_select);

        clearbuf();

        switch(p_select) {

    case 'm' : printf("\n\nPlease Enter the password you would like to use\n\n"); if (!fgets(upass, sizeof(upass), stdin)) {
    return;
} upass[strcspn(upass, "\n")] = '\0'; break;


    case 'r' : pw_gen(); break;
    default  : printf("\n\nPlease Choose either 'm' or 'r'\n\n");
    return;

}

size_t service_len = strlen(service);
    size_t upass_len = strlen(upass);
    char plaintext[256];
    size_t plaintext_len = service_len + 1 + upass_len + 1;

    if (plaintext_len > sizeof(plaintext)) {
        printf("Error: entry too long.\n");
        sodium_memzero(upass, sizeof(upass));
        return;
    }

    memcpy(plaintext, service, service_len);
    plaintext[service_len] = '\0';
    memcpy(plaintext + service_len + 1, upass, upass_len + 1);

    unsigned char nonce[crypto_secretbox_NONCEBYTES];
    randombytes_buf(nonce, sizeof(nonce));

    size_t ciphertext_len = crypto_secretbox_MACBYTES + plaintext_len;
    unsigned char ciphertext[crypto_secretbox_MACBYTES + sizeof(plaintext)];

    crypto_secretbox_easy(ciphertext, (const unsigned char *)plaintext, plaintext_len, nonce, vault_key);

    FILE *fp = fopen(vault_file, "ab");
    if (!fp) {
        printf("Error: %s\n", strerror(errno));
        sodium_memzero(upass, sizeof(upass));
        sodium_memzero(plaintext, sizeof(plaintext));
        return;
    }

    uint32_t len32 = (uint32_t)ciphertext_len;
    fwrite(nonce, 1, sizeof(nonce), fp);
    fwrite(&len32, sizeof(len32), 1, fp);
    fwrite(ciphertext, 1, ciphertext_len, fp);
    fclose(fp);

    sodium_memzero(upass, sizeof(upass));
    sodium_memzero(plaintext, sizeof(plaintext));

    printf("\n\nSaved entry for '%s'.\n", service);
}

//The previous Bitlocker bootloop prevention function

void Rkey(void) {
	
    memset(winpass, 0, sizeof(winpass));
    memset(rkey, 0, sizeof(rkey));

    printf("\n\nEnter Windows Password\n\n"); 
  
  if (fgets(winpass, sizeof(winpass), stdin)) {

        int filler=230;

        for (lc = 0; lc < 17; lc++) {

            if (winpass[lc] == '\0' || winpass[lc] == '\n') {
                rkey[lc] = filler;
            } else {

                rkey[lc] = (int)winpass[lc] +101;

            }
        }
}
printf("\n\nYour Bitlocker Recovery Key is:\n\n");

    for (lc = 0; lc < 14; lc += 2) {
        printf("%d - ", (rkey[lc] *100 + rkey[lc + 1]) *11);
    }

    printf("%d", (rkey[14] *100 + rkey[15]) *11);

printf("\n\nPress Enter to Conitinue");

    if (strchr(winpass, '\n') == NULL) {
    int c;
    while ((c = getchar()) != '\n' && c != EOF);
}

getchar();

}

//Finding Current Working directory to store and look for vauilt files

void get_location(void)

{
    if (getcwd(cwd, sizeof(cwd)) == NULL)
    {
        perror("getcwd");
    }
}



//Main Menu Function

void menu(void) {

    while (1) {

printf("\n\nWhat would you like to do?\n\n"
    "1 - Access Passwords\n"
    "2 - Generate New Password\n"
    "3 - Generate Bitlocker Recover Key\n"
    "4 - Change Character Set\n"
    "5 - Manual input\n"
    "6 - Exit\n\n");

scanf(" %c", &menu_option); clearbuf();

    switch(menu_option) {
    case '1' : pw_access(); break;
    case '2' : pw_gen();  break;
    case '3' : Rkey(); break;
    case '4' : options(); break;
    case '5' : pw_manual(); break;
    case '6' : exit(0); break;
    default  : printf("\n\nPlease Choose a Number Between 1 & 5\n\n");

} 
}
}

//Check if new user, ask for password if returning

void login(void) {
    printf("\n\nPlease Enter username: ");
    if (!fgets(uname, sizeof(uname), stdin)) {
        return;
    }
    uname[strcspn(uname, "\n")] = '\0';

    snprintf(checkpath, sizeof(checkpath), "%s/%s", vault_dir, uname);

    if (access(checkpath, F_OK) == 0) {                 // Returning user

        printf("\n\nPlease Enter Your Password: ");
        if (!fgets(mpass, sizeof(mpass), stdin)) {
            return;
        }
        mpass[strcspn(mpass, "\n")] = '\0';

        int fn = snprintf(vault_file, sizeof(vault_file), "%s/%s", vault_dir, uname);
    if (fn < 0 || (size_t)fn >= sizeof(vault_file)) {
        fprintf(stderr, "Error: file path too long\n");
        return;
    }

        FILE *fp = fopen(checkpath, "rb");
        if (!fp) {
            printf("Error: %s\n", strerror(errno));
            return;
        }

        unsigned char salt[crypto_pwhash_SALTBYTES];
        unsigned char nonce[crypto_secretbox_NONCEBYTES];
        unsigned char ciphertext[crypto_secretbox_MACBYTES + VAULT_CHECK_LEN];

        if (fread(salt, 1, sizeof(salt), fp) != sizeof(salt) ||
            fread(nonce, 1, sizeof(nonce), fp) != sizeof(nonce) ||
            fread(ciphertext, 1, sizeof(ciphertext), fp) != sizeof(ciphertext)) {
            printf("Error: vault file is corrupted or unreadable.\n");
            fclose(fp);
            return;
        }
        fclose(fp);

        if (crypto_pwhash(
                vault_key, sizeof(vault_key),
                mpass, strlen(mpass),
                salt,
                crypto_pwhash_OPSLIMIT_MODERATE,
                crypto_pwhash_MEMLIMIT_MODERATE,
                crypto_pwhash_ALG_DEFAULT) != 0) {
            printf("Error: key derivation failed (out of memory?).\n");
            sodium_memzero(mpass, sizeof(mpass));
            return;
        }

        unsigned char check[VAULT_CHECK_LEN];
        if (crypto_secretbox_open_easy(check, ciphertext, sizeof(ciphertext), nonce, vault_key) != 0) {
            printf("\n\nIncorrect password.\n");
            sodium_memzero(mpass, sizeof(mpass));
            sodium_memzero(vault_key, sizeof(vault_key));
            return;
        }

        sodium_memzero(mpass, sizeof(mpass));
        printf("\n\nWelcome back, %s.\n", uname);
        menu();

    } else {            //1st time user

        printf("\n\n1st time? Please choose a password: ");
        if (!fgets(mpass, sizeof(mpass), stdin)) {
            return;
        }
        mpass[strcspn(mpass, "\n")] = '\0';

        unsigned char salt[crypto_pwhash_SALTBYTES];
        randombytes_buf(salt, sizeof(salt));

        if (crypto_pwhash(
                vault_key, sizeof(vault_key),
                mpass, strlen(mpass),
                salt,
                crypto_pwhash_OPSLIMIT_MODERATE,
                crypto_pwhash_MEMLIMIT_MODERATE,
                crypto_pwhash_ALG_DEFAULT) != 0) {
            printf("Error: key derivation failed (out of memory?).\n");
            sodium_memzero(mpass, sizeof(mpass));
            return;
        }

        unsigned char nonce[crypto_secretbox_NONCEBYTES];
        randombytes_buf(nonce, sizeof(nonce));

        unsigned char ciphertext[crypto_secretbox_MACBYTES + VAULT_CHECK_LEN];
        crypto_secretbox_easy(ciphertext, (const unsigned char *)VAULT_CHECK_STRING, VAULT_CHECK_LEN, nonce, vault_key);

        int fn = snprintf(vault_file, sizeof(vault_file), "%s/%s", vault_dir, uname);
        if (fn < 0 || (size_t)fn >= sizeof(vault_file)) {
            fprintf(stderr, "Error: file path too long\n");
            sodium_memzero(mpass, sizeof(mpass));
            return;
        }

        FILE *fp = fopen(vault_file, "wb");
        if (!fp) {
            printf("Error: %s\n", strerror(errno));
            sodium_memzero(mpass, sizeof(mpass));
            return;
        }

        fwrite(salt, 1, sizeof(salt), fp);
        fwrite(nonce, 1, sizeof(nonce), fp);
        fwrite(ciphertext, 1, sizeof(ciphertext), fp);
        fclose(fp);

        sodium_memzero(mpass, sizeof(mpass));
        printf("\n\nVault created for %s.\n", uname);
        menu();
    }
}
    

int main(void) {

if (sodium_init() < 0) {
        printf("Error: Libsodium initialization failed.\n");
        return 1;

}

get_location();

    int n = snprintf(vault_dir, sizeof(vault_dir), "%s/multipass", cwd);
    if (n < 0 || (size_t)n >= sizeof(vault_dir)) {
        fprintf(stderr, "Error: path too long\n");
        return 1;
    }

    //Check if vault_dir exists yet - create it if it doesn't

    DIR *dir = opendir(vault_dir);
    if (dir) {
        closedir(dir);
    } else if (ENOENT == errno) {
        if (mkdir(vault_dir, S_IRWXU) == -1) {
            printf("Error: %s\n", strerror(errno));
            return 1;
        }
    } else {
        printf("Error: %s\n", strerror(errno));
        return 1;
    }

    login();

    int cn = snprintf(checkpath, sizeof(checkpath), "%s/%s", vault_dir, uname);
    if (cn < 0 || (size_t)cn >= sizeof(checkpath)) {
        fprintf(stderr, "Error: path too long\n");
        return 1;
    }

    return 0;
}