⬅
9breaker@kscys291.com
/*
© 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;
}