passman.c (10282B)
1 /* 2 * Copyright 2022 kqueue <kqueue@cocaine.ninja> 3 4 Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 5 6 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING 7 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 8 */ 9 #include <stdio.h> 10 #include <string.h> 11 #include <stdlib.h> 12 #include <sys/types.h> 13 #include <sys/stat.h> 14 #include <termios.h> 15 #include <stdint.h> 16 #include "third-party/monocypher.h" 17 #include <dirent.h> 18 #include "argon2i-settings.h" 19 #if defined(__linux__) 20 uint32_t arc4random_uniform(uint32_t upper_bound); 21 void arc4random_buf(void *buf, size_t nbytes); 22 #endif 23 // return value for verify() 24 struct verifyreturn { 25 uint8_t key[32]; 26 int matches; 27 }; 28 struct stat st = { 0 }; 29 30 void password_gen(uint8_t * buf, int length) 31 { 32 char characters[81] = 33 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~=+%^*()[]{}/!@#$?|"; 34 int random_num; 35 for (int i = 0; i < length; i++) { 36 random_num = arc4random_uniform(81); 37 buf[i] = characters[random_num]; 38 } 39 } 40 41 int getpasswd(char password[64]) 42 { 43 struct termios oflags, nflags; 44 45 /* disabling echo */ 46 tcgetattr(fileno(stdin), &oflags); 47 nflags = oflags; 48 nflags.c_lflag &= ~ECHO; 49 nflags.c_lflag |= ECHONL; 50 51 if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0) { 52 perror("tcsetattr"); 53 return EXIT_FAILURE; 54 } 55 56 fgets(password, 64, stdin); 57 password[strlen(password) - 1] = 0; 58 /* restore terminal */ 59 if (tcsetattr(fileno(stdin), TCSANOW, &oflags) != 0) { 60 perror("tcsetattr"); 61 return EXIT_FAILURE; 62 } 63 return 0; 64 } 65 66 struct verifyreturn verify(char password[64]) 67 { 68 char passmanpath[500]; 69 sprintf(passmanpath, "%s/.passman-store", getenv("HOME")); 70 struct verifyreturn returnval; 71 int length = strlen(password); 72 uint8_t uintpass[length + 1]; 73 // Converts from char to uint8_t 74 for (int i = 0; i < length; i++) { 75 uintpass[i] = password[i]; 76 } 77 uint8_t salt[16]; 78 crypto_wipe(password, length); 79 void *work_area = malloc(nb_blocks * 1024); /* Work area */ 80 if (work_area == NULL) { 81 /* Handle malloc() failure */ 82 /* Wipe secrets if they are no longer needed */ 83 puts("malloc failed"); 84 crypto_wipe(uintpass, length); 85 } 86 char filepath[200]; 87 sprintf(filepath, "%s/passman-hash", passmanpath); 88 FILE *file = fopen(filepath, "rb"); 89 uint8_t filehash[32]; 90 fread(filehash, 1, 32, file); 91 fread(salt, 1, 16, file); 92 fclose(file); 93 crypto_argon2i(returnval.key, 32, work_area, nb_blocks, nb_iterations, 94 uintpass, length, salt, 16); 95 free(work_area); 96 crypto_wipe(uintpass, length); 97 uint8_t hash[32]; 98 crypto_blake2b_general(hash, 32, NULL, 0, returnval.key, 32); 99 int matches = crypto_verify32(hash, filehash); 100 if (matches == 0) { 101 returnval.matches = 1; 102 } else { 103 returnval.matches = 0; 104 } 105 return returnval; 106 } 107 108 void init() 109 { 110 char passmanpath[200]; 111 sprintf(passmanpath, "%s/.passman-store", getenv("HOME")); 112 if (stat(passmanpath, &st) == -1) { 113 mkdir(passmanpath, 0770); 114 } else { 115 printf 116 ("%s already exists, you may have already initialized passman. If you want to initialize again move ~/.passman-store to a new area or delete the directory all together", 117 passmanpath); 118 return; 119 } 120 puts("Enter the password that you want you use for the encryption key"); 121 char password[64]; 122 getpasswd(password); 123 puts("Confirm password"); 124 char confirm_password[64]; 125 getpasswd(confirm_password); 126 if (strcmp(password, confirm_password) != 0) { 127 puts("passwords don't match"); 128 return; 129 } 130 uint8_t key[32]; 131 int length = strlen(password); 132 uint8_t uintpass[length]; 133 // Converts from char to uint8_t 134 for (int i = 0; i < length; i++) { 135 uintpass[i] = password[i]; 136 } 137 uint8_t salt[16]; 138 crypto_wipe(password, length); 139 void *work_area = malloc(nb_blocks * 1024); /* Work area */ 140 if (work_area == NULL) { 141 /* Handle malloc() failure */ 142 /* Wipe secrets if they are no longer needed */ 143 puts("malloc failed"); 144 crypto_wipe(uintpass, length); 145 } 146 arc4random_buf(salt, 16); 147 crypto_argon2i(key, 32, work_area, nb_blocks, nb_iterations, uintpass, 148 length, salt, 16); 149 free(work_area); 150 uint8_t hash[32]; 151 crypto_blake2b_general(hash, 32, NULL, 0, key, 32); 152 crypto_wipe(key, 32); 153 char hashpath[300]; 154 sprintf(hashpath, "%s/passman-hash", passmanpath); 155 FILE *hashfile = fopen(hashpath, "wb"); 156 if (hashfile == NULL) { 157 puts("file couldn't be opened"); 158 return; 159 } 160 fwrite(hash, 1, 32, hashfile); 161 fwrite(salt, 1, 16, hashfile); 162 crypto_wipe(hash, 32); 163 fclose(hashfile); 164 } 165 166 void add(char *path) 167 { 168 char password[64]; 169 puts("put your key password"); 170 getpasswd(password); 171 struct verifyreturn key = verify(password); 172 if (key.matches == 0) { 173 puts("the password you entered does not match with the key"); 174 return; 175 } 176 puts("put the password you'd like to add"); 177 char password2[1000]; 178 getpasswd(password2); 179 uint8_t nonce[24]; 180 uint8_t mac[16]; 181 int length = strlen(password2); 182 uint8_t encrypted_password[length]; 183 uint8_t uintpassword[length]; 184 for (int i = 0; i < length; i++) { 185 uintpassword[i] = password2[i]; 186 } 187 crypto_wipe(password2, length); 188 arc4random_buf(nonce, 24); 189 crypto_lock(mac, encrypted_password, key.key, nonce, uintpassword, 190 length); 191 crypto_wipe(key.key, 32); 192 crypto_wipe(uintpassword, length); 193 char passwordpath[300]; 194 sprintf(passwordpath, "%s/.passman-store/%s", getenv("HOME"), path); 195 FILE *passwordfile = fopen(passwordpath, "wb"); 196 fwrite(mac, 1, 16, passwordfile); 197 fwrite(nonce, 1, 24, passwordfile); 198 fwrite(encrypted_password, 1, length, passwordfile); 199 fclose(passwordfile); 200 } 201 202 void generate(char *path, int length) 203 { 204 uint8_t generatedpass[length]; 205 password_gen(generatedpass, length); 206 char password[64]; 207 puts("put your key password"); 208 getpasswd(password); 209 struct verifyreturn key = verify(password); 210 if (key.matches == 0) { 211 puts("the password you entered does not match with the key"); 212 return; 213 } 214 uint8_t nonce[24]; 215 uint8_t mac[16]; 216 uint8_t encrypted_password[length]; 217 arc4random_buf(nonce, 24); 218 crypto_lock(mac, encrypted_password, key.key, nonce, generatedpass, 219 length); 220 char passwordpath[300]; 221 sprintf(passwordpath, "%s/.passman-store/%s", getenv("HOME"), path); 222 FILE *passwordfile = fopen(passwordpath, "wb"); 223 fwrite(mac, 1, 16, passwordfile); 224 fwrite(nonce, 1, 24, passwordfile); 225 fwrite(encrypted_password, 1, length, passwordfile); 226 fclose(passwordfile); 227 } 228 229 void walk(char *path, int len) 230 { 231 struct dirent *de; 232 DIR *dr = opendir(path); 233 if (dr == NULL) // opendir returns NULL if couldn't open directory 234 { 235 printf("Could not open current directory"); 236 return; 237 } 238 while ((de = readdir(dr)) != NULL) { 239 if (strcmp(de->d_name, ".") == 0) { 240 /* do nothing */ 241 } else if (strcmp(de->d_name, "..") == 0) { 242 /* do nothing */ 243 } else if (strcmp(de->d_name, "passman-hash") == 0) { 244 /* do nothing */ 245 } else if (de->d_type == DT_DIR) { 246 int length = strlen(path) + strlen(de->d_name) + 3; 247 char dir[length]; 248 sprintf(dir, "%s/%s", path, de->d_name); 249 walk(dir, len); 250 } else { 251 printf("%s/%s\n", path + len, de->d_name); 252 } 253 } 254 closedir(dr); 255 return; 256 } 257 258 void show(char *path) 259 { 260 puts("put your key password"); 261 char password[64]; 262 getpasswd(password); 263 struct verifyreturn key = verify(password); 264 if (key.matches == 0) { 265 puts("the password you entered does not match with the key"); 266 return; 267 } 268 uint8_t nonce[24]; 269 uint8_t mac[16]; 270 char passwordpath[500]; 271 sprintf(passwordpath, "%s/.passman-store/%s", getenv("HOME"), path); 272 FILE *passfile = fopen(passwordpath, "rb"); 273 fseek(passfile, 0, SEEK_END); 274 long filesize = ftell(passfile); 275 fseek(passfile, 0, SEEK_SET); 276 fread(mac, 1, 16, passfile); 277 fread(nonce, 1, 24, passfile); 278 int length = filesize - 40; 279 uint8_t uint_password[length]; 280 fread(uint_password, 1, length, passfile); 281 fclose(passfile); 282 uint8_t plain_text[length + 1]; 283 if (crypto_unlock 284 (plain_text, key.key, nonce, mac, uint_password, length)) { 285 printf("message is corrupted\n"); 286 crypto_wipe(key.key, 32); 287 return; 288 } else { 289 for (int i = 0; i < length; i++) { 290 printf("%c", plain_text[i]); 291 } 292 printf("\n"); 293 crypto_wipe(plain_text, 12); 294 crypto_wipe(key.key, 32); 295 } 296 297 } 298 299 int main(int argc, char *argv[]) 300 { 301 if(argv[1] == NULL || strcmp(argv[1], "help") == 0 ){ 302 printf("Usage:\n\tpassman help\tdisplays help message\n\tpassman generate [name] [length]\tgenerates a password and securely stores it\n\tpassman show [name]\tprints the unencrypted value of a stored password\n\tpassman add\tprompts for a password and securely stores it\n\tpassman list\tlists the passwords that are stored\n"); 303 return 0; 304 } 305 if (strcmp(argv[1], "init") == 0) { 306 init(); 307 return 0; 308 } else if (strcmp(argv[1], "generate") == 0) { 309 if (argc < 4) { 310 puts("not enough arguments passed for generate"); 311 return 1; 312 } 313 generate(argv[2], atoi(argv[3])); 314 return 0; 315 } else if (strcmp(argv[1], "show") == 0) { 316 if (argc < 3) { 317 puts("not enough arguments passed for show"); 318 return 1; 319 } 320 show(argv[2]); 321 return 0; 322 } else if (strcmp(argv[1], "add") == 0 323 || strcmp(argv[1], "insert") == 0) { 324 if (argc < 3) { 325 puts("not enough arguments passed for add/insert"); 326 return 1; 327 } 328 add(argv[2]); 329 return 0; 330 } else if (strcmp(argv[1], "list") == 0) { 331 char path[500]; 332 sprintf(path, "%s/.passman-store/", getenv("HOME")); 333 walk(path, strlen(path)+1); 334 } else { 335 puts("command not found"); 336 return 0; 337 } 338 }