/* * Compile commandline: cc -std=gnu99 -Wall -Wextra -Os -o cpass pass.c * Source code: https://04d.co/dl/software/pass.c * License: public domain * TODO: Implement GPGME, support for Windows */ #include #include #include #include #include #include #include #include #include #define defaultPasswordLength 25 #define VER "0.4" /* Creates a new file or overrides an existing one with a user chosen password */ void createPass(char* path) { /* Check if file already exists, if so prompt for override */ struct stat st; if(stat(path, &st) == 0) { char choice; printf("Password already exists, override? (y/N): "); scanf("%c", &choice); if(choice == 'Y' || choice == 'y') {} else { printf("Aborting\n"); exit(EXIT_SUCCESS); } } /* Create a file (or empty it if it already exists) and open it for writing */ FILE *fp = fopen(path, "w"); if(fp == NULL) { fprintf(stderr, "Error opening file for writing: %s\n", strerror(errno)); exit(EXIT_FAILURE); } /* Prompt for password twice, exit if both don't match */ char* fpass = getpass("Password: "); char* lpass = getpass("Repeat password: "); if(strcmp(fpass, lpass) == 0) { fprintf(fp, "%s", fpass); printf("Password added\n"); } else { printf("Passwords do not match, try again\n"); exit(EXIT_SUCCESS); } /* Flush data to disk */ fflush(fp); fclose(fp); return; } /* Shows a password from a user provided file */ void showPass(char* path) { /* Check if file exists and open the file and read one * character at a time until EOF */ struct stat st; FILE *fp = fopen(path, "r"); if(stat(path, &st) == -1 || fp == NULL) { fprintf(stderr, "Error opening file: %s\n", strerror(errno)); exit(EXIT_FAILURE); } char curChar; while((curChar = fgetc(fp)) != EOF) printf("%c", curChar); printf("\n"); /* Clean up */ fflush(fp); fclose(fp); return; } /* Delete a user provided file */ void deletePass(char* path) { /* Check if file exists */ struct stat st; if(stat(path, &st) == 0) { char choice; printf("Are you sure? (y/N): "); scanf("%c", &choice); if(choice == 'Y' || choice == 'y') { if(remove(path) == 0) { printf("File deleted successfully\n"); exit(EXIT_SUCCESS); } else { fprintf(stderr, "Error deleting file: %s\n", strerror(errno)); exit(EXIT_FAILURE); } } else { printf("Aborting\n"); exit(EXIT_SUCCESS); } } else { fprintf(stderr, "Error deleting file: %s\n", strerror(errno)); exit(EXIT_FAILURE); } return; } /* Generate a random password and write it to a user provided file */ void generatePass(char* path, unsigned int passLen) { const char charSet[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890?!@#$%^&*<>()[]{}|~`,.;"; char password[passLen+1]; /* Check if file already exists, if so prompt for override */ struct stat st; if(stat(path, &st) == 0) { char choice; printf("Password already exists, override? (y/N): "); scanf("%c", &choice); if(choice == 'Y' || choice == 'y') {} else { printf("Aborting\n"); exit(EXIT_SUCCESS); } } /* Seed the RNG with a better source than just time(0) */ unsigned int pid = getpid(); struct timeval t1; gettimeofday(&t1, NULL); srand(t1.tv_usec ^ t1.tv_sec ^ pid); /* For every character position, pick a random char from the set */ for(unsigned int i=0; i < passLen; i++) { unsigned int randomIndex = rand() % (sizeof(charSet) - 1); password[i] = charSet[randomIndex]; } password[passLen] = '\0'; /* Null terminate the password */ /* Create a file (or empty it if it already exists) and open it for writing */ FILE *fp = fopen(path, "w"); if(fp == NULL) { fprintf(stderr, "Error opening file for writing: %s\n", strerror(errno)); exit(EXIT_FAILURE); } /* Write the newly generated password to a file */ fprintf(fp, "%s", password); /* Flush data to disk */ fflush(fp); fclose(fp); printf("Password added\n"); return; } /* Copy or rename a password file */ void cpmvPass(char* src, char* dest, unsigned int removeSource) { /* Make a buffer, source and destination pointers */ char buf[4096]; FILE *stream_src = fopen(src, "r"); if(stream_src == NULL) { fprintf(stderr, "Error opening source file for reading: %s\n", strerror(errno)); exit(EXIT_FAILURE); } FILE *stream_dest = fopen(dest, "w"); if(stream_src == NULL) { fprintf(stderr, "Error opening destination file for writing: %s\n", strerror(errno)); exit(EXIT_FAILURE); } /* Copy buf bytes at a time into memory and then to disk */ while(!feof(stream_src)) { size_t bytes = fread(buf, 1, sizeof(buf), stream_src); if(bytes) fwrite(buf, 1, bytes, stream_dest); else { fprintf(stderr, "Error when trying to copy bytes " "(are source and destination the same file?)\n"); exit(EXIT_FAILURE); } } fclose(stream_src); fflush(stream_dest); fclose(stream_dest); if(removeSource == 1) { if(remove(src) == 0) printf("File moved successfully\n"); else fprintf(stderr, "Error deleting source file: %s\n", strerror(errno)); } else printf("File copied successfully\n"); return; } int main(int argc, char *argv[]) { const char defaultDir[] = "/.local/share/passDir"; char* baseDir; /* If env variable CPASS_DIR is defined, used that instead of * the default "passDir" */ if(getenv("CPASS_DIR") == NULL) { /* Create the password folder under the user's home dir by default */ baseDir = (char*)malloc(strlen(getenv("HOME")) + strlen(defaultDir) + 1); strcpy(baseDir, getenv("HOME")); strcat(baseDir, defaultDir); } else { baseDir = (char*)malloc(strlen(getenv("CPASS_DIR")) + 1); strcpy(baseDir, getenv("CPASS_DIR")); } char *path, *srcPath, *destPath; /* Prompt to create baseDir if it doesn't exist already */ struct stat st; if(stat(baseDir, &st) == -1) { char choice; printf("Password directory %s does not yet exist. " "Do you want to create it? (y/N): ", baseDir); scanf("%c", &choice); if(choice == 'Y' || choice == 'y') if(mkdir(baseDir, 0700) == 0) printf("Base directory created successfully\n"); else { fprintf(stderr, "Error creating base directory: %s\n", strerror(errno)); exit(EXIT_FAILURE); } else { printf("Aborting\n"); exit(EXIT_SUCCESS); } } /* If no arguments are specified, show stored files */ if(argc == 1) { DIR *d = opendir(baseDir); struct dirent *dir; if(d == NULL) { fprintf(stderr, "Error opening base directory: %s\n", strerror(errno)); exit(EXIT_FAILURE); } while((dir = readdir(d)) != NULL) printf("%s\n", dir->d_name); closedir(d); } else if(strcmp(argv[1], "new") == 0) { if(argc < 3) { printf("No file name provided\n"); return 1; } else if(strcmp(argv[2], "new") == 0 || strcmp(argv[2], "ver") == 0 || strcmp(argv[2], "help") == 0 || strcmp(argv[2], "del") == 0 || strcmp(argv[2], "copy") == 0 || strcmp(argv[2], "move") == 0 || strcmp(argv[2], "gen") == 0) { printf("File cannot be named after a program command\n"); return 1; } /* Construct a path where to write the file */ path = (char*)malloc(strlen(baseDir) + strlen(argv[2]) + 1); if(path == NULL) { fprintf(stderr, "Error allocating memory: %s\n", strerror(errno)); exit(EXIT_FAILURE); } sprintf(path, "%s/%s", baseDir, argv[2]); free(baseDir); /* Do the rest */ createPass(path); /* Clean up memory */ free(path); } else if(strcmp(argv[1], "gen") == 0) { if(argc < 3) { printf("No file name provided\n"); return 1; } else if(strcmp(argv[2], "new") == 0 || strcmp(argv[2], "ver") == 0 || strcmp(argv[2], "help") == 0 || strcmp(argv[2], "del") == 0 || strcmp(argv[2], "copy") == 0 || strcmp(argv[2], "move") == 0 || strcmp(argv[2], "gen") == 0) { printf("File cannot be named after a program command\n"); return 1; } /* Construct a path where to write the file */ path = (char*)malloc(strlen(baseDir) + strlen(argv[2]) + 1); if(path == NULL) { fprintf(stderr, "Error allocating memory: %s\n", strerror(errno)); exit(EXIT_FAILURE); } sprintf(path, "%s/%s", baseDir, argv[2]); free(baseDir); /* Allow choosing generated password length */ unsigned int passLen = defaultPasswordLength; if(argc == 4) { if(atoi(argv[3]) != 0) /* Check if argument contains only digits */ { unsigned int tmp = atoi(argv[3]); if(tmp > 4096) /* Check if user doesn't provide unreasonably long password length */ { printf("Provided length is larger than 4095, " "consider choosing a shorter password\n"); exit(EXIT_FAILURE); } else passLen = atoi(argv[3]); } else { printf("Provided length is not a number\n"); exit(EXIT_FAILURE); } } /* Do the rest */ generatePass(path, passLen); /* Clean up memory */ free(path); } else if(strcmp(argv[1], "del") == 0) { if(argc < 3) { printf("No file name provided\n"); return 1; } /* Construct a path where to write the file */ path = (char*)malloc(strlen(baseDir) + strlen(argv[2]) + 1); if(path == NULL) { fprintf(stderr, "Error allocating memory: %s\n", strerror(errno)); exit(EXIT_FAILURE); } sprintf(path, "%s/%s", baseDir, argv[2]); free(baseDir); /* Do the rest */ deletePass(path); /* Clean up memory */ free(path); } else if(strcmp(argv[1], "copy") == 0 || strcmp(argv[1], "move") == 0) { if(argc < 3) { printf("No source file name provided\n"); return 1; } if(argc < 4) { printf("No destination file name provided\n"); return 1; } /* Construct a path where to find the source and destination file */ srcPath = (char*)malloc(strlen(baseDir) + strlen(argv[2]) + 1); if(srcPath == NULL) { fprintf(stderr, "Error allocating memory: %s\n", strerror(errno)); exit(EXIT_FAILURE); } destPath = (char*)malloc(strlen(baseDir) + strlen(argv[3]) + 1); if(destPath == NULL) { fprintf(stderr, "Error allocating memory: %s\n", strerror(errno)); exit(EXIT_FAILURE); } sprintf(srcPath, "%s/%s", baseDir, argv[2]); sprintf(destPath, "%s/%s", baseDir, argv[3]); free(baseDir); /* Do the rest */ if(strcmp(argv[1], "move") == 0) cpmvPass(srcPath, destPath, 1); else cpmvPass(srcPath, destPath, 0); /* Clean up memory */ free(srcPath); free(destPath); } else if(strcmp(argv[1], "help") == 0) printf("Passwords are stored in %s, dictated by the presence" " or absence of the CPASS_DIR environment variable.\n" "%s passName\n" "%s help\n" "%s ver\n" "%s new passName\n" "%s del passName\n" "%s gen passName [passLength]\n" "%s copy oldName newName\n" "%s move oldName newName\n", baseDir, argv[0], argv[0], argv[0], argv[0], argv[0], argv[0], argv[0], argv[0]); else if(strcmp(argv[1], "ver") == 0) printf("cpass version "VER", compiled on %s at %s\n", __DATE__,__TIME__); else if(argc == 2) { /* Construct a path where to find the file */ path = (char*)malloc(strlen(baseDir) + strlen(argv[1]) + 1); if(path == NULL) { fprintf(stderr, "Error allocating memory: %s\n", strerror(errno)); exit(EXIT_FAILURE); } sprintf(path, "%s/%s", baseDir, argv[1]); free(baseDir); /* Do the rest */ showPass(path); /* Clean up memory */ free(path); } return 0; }