main.c (8170B)
1 #define _POSIX_C_SOURCE 200112L 2 3 /* for random() */ 4 #define _XOPEN_SOURCE 500 5 6 #include <sys/stat.h> 7 #include <sys/types.h> 8 9 #include <crypt.h> 10 #include <fcntl.h> 11 #include <stdint.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <termios.h> 16 #include <time.h> 17 #include <unistd.h> 18 19 #define AES256 1 20 #include "aes.h" 21 22 #include "config.h" 23 24 #define USAGEMSG \ 25 "Usage: np {name}\n" \ 26 " np -i\n" \ 27 " np -n {name}\n" \ 28 /*"see np(1) for help\n"*/ 29 30 #define OPENERRMSG \ 31 "Error opening .notpass file\n" \ 32 "Did you initialize it with `np -i'?\n" \ 33 /*"see np(1) for more help\n"*/ 34 35 #if __STDC_VERSION__ < 201112L 36 37 #define _Noreturn 38 39 #if __STDC_VERSION__ < 199901L 40 41 #define inline 42 43 #endif 44 #endif 45 46 static _Noreturn void 47 usage(void) 48 { 49 fputs(USAGEMSG, stderr); 50 exit(EXIT_FAILURE); 51 } 52 53 static void * 54 xmalloc(size_t size) 55 { 56 void *ret; 57 if (!(ret = malloc(size))) { 58 fputs("Out of memory\n", stderr); 59 exit(EXIT_FAILURE); 60 } 61 return ret; 62 } 63 64 static char * 65 randstr(size_t sz) 66 { 67 char *s = xmalloc(sz + 1); 68 69 s[sz] = 0; 70 while (sz--) { 71 s[sz] = random() % 64; 72 if (s[sz] < 12) 73 s[sz] += '.'; 74 else if (s[sz] < 38) 75 s[sz] += 'A' - 12; 76 else 77 s[sz] += 'a' - 38; 78 } 79 80 return s; 81 } 82 83 static char * 84 gensalt(void) 85 { 86 char *salt = xmalloc(21); 87 char *rand = randstr(16); 88 89 memcpy(salt, "$6$", 3); 90 memcpy(salt + 3, rand, 16); 91 free(rand); 92 memcpy(salt + 19, "$", 2); 93 94 return salt; 95 } 96 97 /* this does not set termios; that must be done before calling this function */ 98 static enum { VALID, INVALID, ERR } 99 validate(char **plain_dest) 100 { 101 char buf[256], hashed[107]; 102 FILE *f; 103 104 /* for consistency. everytime the same master password is entered, 105 * the entire buffer contents should be identical 106 */ 107 memset(buf, 0, 256); 108 109 if (!(f = fopen(PWDIR ".notpass", "r"))) { 110 fputs(OPENERRMSG, stderr); 111 return ERR; 112 } 113 if (fread(hashed, 1, 107, f) < 107) { 114 fputs("Error reading .notpass file\n", stderr); 115 fclose(f); 116 return ERR; 117 } 118 fclose(f); 119 120 fputs("Enter master password: ", stderr); 121 if (!fgets(buf, 256, stdin)) { 122 memset(buf, 0, 256); 123 fputs("Failed to read input\n", stderr); 124 return ERR; 125 } 126 putchar('\n'); 127 128 if (strcmp(crypt(buf, hashed), hashed) == 0) { 129 *plain_dest = xmalloc(256); 130 memcpy(*plain_dest, buf, 256); 131 return VALID; 132 } else { 133 memset(buf, 0, 256); 134 return INVALID; 135 } 136 } 137 138 static inline void 139 init(const char *dir) 140 { 141 struct termios orig_termios, new_termios; 142 char buf[256], *salt, *hashed, *filename; 143 ssize_t bytes_written = 0; 144 size_t len; 145 int fd; 146 147 /* prevent input from being echoed to the screen. 148 * orig_termios is restored after the password is validated. 149 */ 150 if (tcgetattr(0, &orig_termios) == -1) { 151 fputs("tcgetattr error\n", stderr); 152 exit(EXIT_FAILURE); 153 } 154 new_termios = orig_termios; 155 new_termios.c_lflag &= ~ECHO; 156 if (tcsetattr(0, TCSANOW, &new_termios) == -1) { 157 fputs("tcsetattr error\n", stderr); 158 exit(EXIT_FAILURE); 159 } 160 161 if (dir) { 162 len = strlen(dir); 163 filename = xmalloc(len + sizeof(".notpass") + 1); 164 strcpy(filename, dir); 165 strcpy(filename + len, dir[len - 1] == '/' 166 ? ".notpass" 167 : "/.notpass"); 168 } else { 169 filename = PWDIR ".notpass"; 170 } 171 172 if ((fd = creat(filename, 0600)) == -1) { 173 tcsetattr(0, TCSAFLUSH, &orig_termios); 174 fputs("Error creating .notpass file\n", stderr); 175 exit(EXIT_FAILURE); 176 } 177 178 salt = gensalt(); 179 180 fputs("Enter new master password: ", stderr); 181 if (!fgets(buf, 256, stdin)) { 182 memset(buf, 0, 256); 183 tcsetattr(0, TCSAFLUSH, &orig_termios); 184 fputs("Failed to read input\n", stderr); 185 exit(EXIT_FAILURE); 186 } 187 putchar('\n'); 188 189 tcsetattr(0, TCSAFLUSH, &orig_termios); 190 191 hashed = crypt(buf, salt); 192 memset(buf, 0, 256); 193 194 len = strlen(hashed) + 1; 195 while ((bytes_written += write(fd, hashed + bytes_written, 196 len - bytes_written)) < len) { 197 if (bytes_written >= 0) 198 continue; 199 fputs("Failed to write to .notpass file\n", stderr); 200 exit(EXIT_FAILURE); 201 } 202 203 close(fd); 204 } 205 206 static inline void 207 get(const char *name, const char *dir) 208 { 209 struct AES_ctx aes_ctx; 210 struct termios orig_termios, new_termios; 211 char *master, buf[256], iv[AES_BLOCKLEN]; 212 char *filename = xmalloc(strlen(dir) + strlen(name) + 1); 213 FILE *f; 214 215 /* prevent input from being echoed to the screen. 216 * orig_termios is restored after the password is validated. 217 */ 218 if (tcgetattr(0, &orig_termios) == -1) { 219 fputs("tcgetattr error\n", stderr); 220 exit(EXIT_FAILURE); 221 } 222 new_termios = orig_termios; 223 new_termios.c_lflag &= ~ECHO; 224 if (tcsetattr(0, TCSANOW, &new_termios) == -1) { 225 fputs("tcsetattr error\n", stderr); 226 exit(EXIT_FAILURE); 227 } 228 229 switch (validate(&master)) { 230 case VALID: 231 tcsetattr(0, TCSAFLUSH, &orig_termios); 232 break; 233 case INVALID: 234 fputs("Invalid master password\n", stderr); 235 /* fallthrough */ 236 case ERR: 237 /* in case of error, validate fn already printed error msg */ 238 tcsetattr(0, TCSAFLUSH, &orig_termios); 239 exit(EXIT_FAILURE); 240 } 241 242 strcpy(filename, dir); 243 strcpy(filename + strlen(dir), name); 244 245 if (!(f = fopen(filename, "r"))) { 246 memset(master, 0, strlen(master)); 247 fprintf(stderr, "Failed to open file `%s'\n", filename); 248 exit(EXIT_FAILURE); 249 } 250 if (fread(iv, 1, AES_BLOCKLEN, f) < AES_BLOCKLEN) 251 goto read_err; 252 if (fread(buf, 1, 256, f) < 256) 253 goto read_err; 254 fclose(f); 255 256 AES_init_ctx_iv(&aes_ctx, (uint8_t *)master, (uint8_t *)iv); 257 memset(master, 0, 256); 258 free(master); 259 AES_CBC_decrypt_buffer(&aes_ctx, (uint8_t *)buf, 256); 260 261 printf("%.*s", 256 - (unsigned char)(buf[255]), buf); 262 memset(buf, 0, 256); 263 return; 264 265 read_err: 266 memset(master, 0, 256); 267 fprintf(stderr, "Error reading file `%s'\n", filename); 268 exit(EXIT_FAILURE); 269 } 270 271 static inline void 272 new(const char *name, const char *dir) 273 { 274 struct termios orig_termios, new_termios; 275 struct AES_ctx aes_ctx; 276 char buf[256], *master, *iv, *filename; 277 FILE *f; 278 int fd, i; 279 280 /* prevent input from being echoed to the screen. 281 * orig_termios is restored after the password is validated. 282 */ 283 if (tcgetattr(0, &orig_termios) == -1) { 284 fputs("tcgetattr error\n", stderr); 285 exit(EXIT_FAILURE); 286 } 287 new_termios = orig_termios; 288 new_termios.c_lflag &= ~ECHO; 289 if (tcsetattr(0, TCSANOW, &new_termios) == -1) { 290 fputs("tcsetattr error\n", stderr); 291 exit(EXIT_FAILURE); 292 } 293 294 switch (validate(&master)) { 295 case VALID: 296 break; 297 case INVALID: 298 fputs("Invalid master password\n", stderr); 299 /* fallthrough */ 300 case ERR: 301 /* in case of error, validate fn already printed error msg */ 302 tcsetattr(0, TCSAFLUSH, &orig_termios); 303 exit(EXIT_FAILURE); 304 } 305 306 fputs("Enter new password entry: ", stderr); 307 308 if (!fgets(buf, 256, stdin)) { 309 memset(buf, 0, 256); 310 memset(master, 0, 256); 311 tcsetattr(0, TCSAFLUSH, &orig_termios); 312 fputs("Failed to read input\n", stderr); 313 exit(EXIT_FAILURE); 314 } 315 putchar('\n'); 316 317 tcsetattr(0, TCSAFLUSH, &orig_termios); 318 319 /* padding for buf: PKCS #7 */ 320 for (i = 0; buf[i]; i++); 321 memset(buf + i, 256 - i, 256 - i); 322 323 iv = randstr(AES_BLOCKLEN); 324 AES_init_ctx_iv(&aes_ctx, (uint8_t *)master, (uint8_t *)iv); 325 free(master); 326 327 AES_CBC_encrypt_buffer(&aes_ctx, (uint8_t *)buf, 256); 328 329 filename = xmalloc(strlen(dir) + strlen(name) + 1); 330 strcpy(filename, dir); 331 strcpy(filename + strlen(dir), name); 332 if ((fd = creat(filename, 0600)) == -1) { 333 fprintf(stderr, "Error creating file `%s'\n", filename); 334 exit(EXIT_FAILURE); 335 } 336 337 if (!(f = fdopen(fd, "w"))) { 338 fprintf(stderr, "Error opening file `%s'\n", filename); 339 exit(EXIT_FAILURE); 340 } 341 if (fwrite(iv, 1, AES_BLOCKLEN, f) < AES_BLOCKLEN) 342 goto write_err; 343 free(iv); 344 if (fwrite(buf, 1, 256, f) < 256) 345 goto write_err; 346 347 fclose(f); 348 return; 349 350 write_err: 351 fprintf(stderr, "Failed to write to file `%s'\n", filename); 352 exit(EXIT_FAILURE); 353 } 354 355 int 356 main(int argc, char **argv) 357 { 358 if (argc < 2) 359 usage(); 360 361 srandom(time(NULL)); 362 363 if (argv[1][0] == '-') { 364 switch (argv[1][1]) { 365 case 'i': /* initialize new key / master password */ 366 if (argc > 3) 367 usage(); 368 369 init((argc == 3) ? argv[2] : NULL); 370 return EXIT_SUCCESS; 371 case 'n': /* new */ 372 if (argc > 4 || argv[2][0] == '-') 373 usage(); 374 375 new(argv[2], (argc == 4) ? argv[3] : PWDIR); 376 return EXIT_SUCCESS; 377 default: 378 usage(); 379 } 380 } 381 382 if (argc > 3) 383 usage(); 384 385 get(argv[1], (argc == 3) ? argv[2] : PWDIR); 386 return EXIT_SUCCESS; 387 }