pass.c (7511B)
1 #include <errno.h> 2 #include <fcntl.h> 3 #include <gpgme.h> 4 #include <getopt.h> 5 #include <limits.h> 6 #include <libgen.h> 7 #include <locale.h> 8 #include <pwd.h> 9 #include <stdio.h> 10 #include <string.h> 11 #include <stdlib.h> 12 #include <sys/stat.h> 13 #include <unistd.h> 14 15 #include "pass.h" 16 #include "readpassphrase.h" 17 18 #define MAX_BUFSIZ 512 19 static char file[PATH_MAX]; 20 const char *home; 21 22 void 23 initgpgme(void) 24 { 25 const char *reqlibver = "1.6.0"; 26 const char *reqgpgver = "2.1.0"; 27 28 setlocale(LC_ALL, "en_US.UTF-8"); 29 gpgme_set_locale(NULL, LC_ALL, "en_US.UTF-8"); 30 gpgme_set_global_flag("require-gnupg", reqgpgver); 31 gpgme_check_version(reqlibver); 32 } 33 34 void 35 gethomedir(void) 36 { 37 if (!(home = getenv("HOME"))) 38 fatalx("$HOME not set, cannot determine password-store location"); 39 } 40 41 42 void 43 delete(char *item) 44 { 45 char *l; 46 int r; 47 48 gethomedir(); 49 snprintf(file, sizeof(file), "%s/.password-store/%s.gpg", home, item); 50 printf("Are you sure you would like to delete %s? [y/N] ", item); 51 if ((r = getchar()) == 'N' || r == 'n') 52 exit(0); 53 else if ((r == 'y' || r == 'Y') && !remove(file)) { 54 printf("removed '%s'\n", file); 55 if ((l = strrchr(file, '/'))) 56 *l = '\0'; 57 rmdir(file); 58 } 59 } 60 61 void 62 usage(void) 63 { 64 fprintf(stderr, 65 "usage:\tpass init gpg-id\n" 66 "\tpass insert pass-name\n" 67 "\tpass pass-name\n" 68 "\tpass rm pass-name\n" 69 ); 70 exit(1); 71 } 72 73 void 74 decrypt(char *buf) 75 { 76 gpgme_data_t in, out; 77 gpgme_error_t gpgerr; 78 gpgme_ctx_t ctx; 79 gpgme_protocol_t proto; 80 int a, ret; 81 82 initgpgme(); 83 proto = GPGME_PROTOCOL_OpenPGP; 84 85 gpgerr = gpgme_new(&ctx); 86 if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR) 87 fatalgpg(gpgerr, "Error: gpgme_new: %s"); 88 gpgerr = gpgme_set_protocol(ctx, proto); 89 if (gpgme_err_code(gpgerr) == GPG_ERR_INV_VALUE) 90 fatalgpg(gpgerr, "Error: gpgme_set_protocol"); 91 gpgerr = gpgme_data_new_from_file(&in, file, 1); 92 if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR) 93 fatalgpg(gpgerr, "Error: gpgme_data_new_from_file"); 94 gpgerr = gpgme_data_new(&out); 95 if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR) 96 fatalgpg(gpgerr, "Error: gpgme_data_new"); 97 gpgerr = gpgme_op_decrypt(ctx, in, out); 98 if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR) 99 fatalgpg(gpgerr, "Error: gpgme_op_decrypt"); 100 ret = gpgme_data_seek(out, 0, SEEK_SET); 101 if (ret) 102 fatalx("gpgme_data_seek"); 103 if ((a = gpgme_data_read(out, buf, 100)) > 0) 104 buf[a] = '\0'; 105 gpgme_data_release(in); 106 gpgme_data_release(out); 107 gpgme_release(ctx); 108 } 109 110 111 void 112 printpass(char *item) 113 { 114 char buf[MAX_BUFSIZ]; 115 int fin; 116 117 gethomedir(); 118 snprintf(file, sizeof(file), "%s/.password-store/%s.gpg", home, item); 119 /* Check if file exists */ 120 fin = open(file, O_RDONLY); 121 if (fin == -1) 122 fatal("%s is not in password store.", file); 123 decrypt(buf); 124 printf("%s\n", buf); 125 close(fin); 126 } 127 128 void 129 getuserid(char *u, int usize) 130 { 131 char file[PATH_MAX]; 132 FILE *fp; 133 int i; 134 135 snprintf(file, sizeof(file), "%s/.password-store/.gpg-id", home); 136 fp = fopen(file, "r"); 137 if (!fp) 138 fatal("fopen: %s", file); 139 while ((i = fgetc(fp)) != EOF && usize--) 140 *u++ = i; 141 *u = '\0'; 142 } 143 144 void 145 mkdirp(const char *tp) 146 { 147 char *i, t[PATH_MAX]; 148 int r; 149 mode_t mode = S_IRWXU; 150 151 memcpy(t, file, strlen(file) + 1); 152 while ((i = strchr(tp, '/'))) { 153 *i = '\0'; 154 snprintf(file, sizeof(file), "%s/%s", t, tp); 155 printf("mkdir %s\n", file); 156 if ((r = mkdir(file, mode)) == -1) { 157 if (errno != EEXIST) 158 fatal("mkdir"); 159 } 160 tp = i + 1; 161 } 162 } 163 164 void 165 encrypt() 166 { 167 gpgme_data_t in, out; 168 gpgme_error_t gpgerr; 169 gpgme_ctx_t ctx; 170 gpgme_key_t key; 171 gpgme_key_t keys[2]; 172 gpgme_protocol_t proto; 173 char uid[MAX_BUFSIZ], t[PATH_MAX]; 174 FILE *fin, *fout; 175 176 proto = GPGME_PROTOCOL_OpenPGP; 177 key = NULL; 178 179 initgpgme(); 180 getuserid(uid, MAX_BUFSIZ); 181 gpgerr = gpgme_new(&ctx); 182 if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR) 183 fatalgpg(gpgerr, "gpme_new"); 184 gpgerr = gpgme_set_protocol(ctx, proto); 185 if (gpgme_err_code(gpgerr) == GPG_ERR_INV_VALUE) 186 fatalgpg(gpgerr, "gpgme_set_protocol"); 187 gpgme_set_armor(ctx, 1); 188 if (gpgme_op_keylist_start(ctx, uid, 0) != GPG_ERR_INV_VALUE) 189 while (!(gpgerr = gpgme_op_keylist_next(ctx, &key))) { 190 if (key->can_encrypt) 191 break; 192 } 193 if (gpgme_err_code(gpgerr) == GPG_ERR_EOF) 194 fatalgpg(gpgerr, "can not find key"); 195 keys[0] = key; 196 keys[1] = NULL; 197 fin = fopen(file, "r"); 198 memcpy(t, file, strlen(file) + 1); 199 snprintf(file, sizeof(file), "%s.gpg", t); 200 fout = fopen(file, "w"); 201 gpgerr = gpgme_data_new_from_stream(&in, fin); 202 if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR) 203 fatalgpg(gpgerr, "gpgme_data_new_from_stream"); 204 gpgerr = gpgme_data_new_from_stream(&out, fout); 205 gpgme_data_set_encoding(out, GPGME_DATA_ENCODING_ARMOR); 206 if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR) 207 fatalgpg(gpgerr, "gpgme_data_new_from_stream"); 208 gpgerr = gpgme_op_encrypt(ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, in, out); 209 if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR) 210 fatalgpg(gpgerr, "gpgme_op_encrypt"); 211 if (remove(t)) 212 fprintf(stderr, "remove failed\n"); 213 gpgme_key_release(key); 214 gpgme_data_release(in); 215 gpgme_data_release(out); 216 gpgme_release(ctx); 217 } 218 219 void 220 insert(char *item) 221 { 222 char *filename, t[PATH_MAX]; 223 FILE *fp; 224 char pass[MAX_BUFSIZ]; 225 int c, fd; 226 227 c = 'y'; 228 fd = fileno(stdin); 229 230 gethomedir(); 231 snprintf(file, sizeof(file), "%s/.password-store", home); 232 filename = basename(item); 233 mkdirp(item); 234 memcpy(t, file, strlen(file) + 1); 235 snprintf(file, sizeof(file), "%s/%s", t, filename); 236 snprintf(t, sizeof(t), "%s.gpg", file); 237 if ((fp = fopen(t, "r"))) { 238 if (isatty(fd)) { 239 printf("An entry already exists for %s. Overwrite it? [y/N] ", filename); 240 if ((c = getchar()) == 'N' || c == 'n') { 241 fclose(fp); 242 logdbgx("Don't overwrite"); 243 exit(1); 244 } 245 } else 246 /* Assuming user knows what he/she is doing */ 247 printf("Overwriting %s\n", filename); 248 } 249 if (c != 'Y' && c != 'y') 250 exit(1); 251 if (!(fp = fopen(file, "w+b"))) 252 fatal("fopen: %s", file); 253 if (isatty(fd)) { 254 readpassphrase("Enter password: ", pass, MAX_BUFSIZ, RPP_ECHO_OFF); 255 memcpy(t, pass, strlen(pass) + 1); 256 readpassphrase("Retype password: ", pass, MAX_BUFSIZ, RPP_ECHO_OFF); 257 if (!strcmp(pass, t)) 258 { 259 int i = 0; 260 while(t[i] != '\0') { 261 if (fputc(t[i], fp) == EOF) 262 fatal("fputc: %s", file); 263 i++; 264 } 265 } else 266 fatalx("Passwords don't match."); 267 268 } else { 269 int c; 270 while ((c = getchar()) != EOF && c != '\n') 271 fputc(c, fp); 272 } 273 fclose(fp); 274 encrypt(); 275 } 276 277 int 278 isinit(void) 279 { 280 int fp; 281 struct stat sb; 282 283 gethomedir(); 284 snprintf(file, sizeof(file), "%s/.password-store", home); 285 if ((fp = open(file, O_RDONLY)) != -1 && 286 (!fstat(fp, &sb)) && 287 (S_ISDIR(sb.st_mode))) { 288 close(fp); 289 return 1; 290 } 291 return 0; 292 } 293 294 void 295 initpass(char *pgpid) 296 { 297 FILE *gpg; 298 mode_t mode = S_IRWXU; 299 300 if (!pgpid) { 301 usage(); 302 } 303 if (!isinit()) 304 { 305 if (mkdir(file, mode)) 306 fatal("mkdir"); 307 snprintf(file, sizeof(file), "%s/.password-store/.gpg-id", home); 308 if (!(gpg = fopen(file, "a"))) 309 fatal("fopen"); 310 if (fputs(pgpid, gpg) == EOF) 311 fatal("fputs"); 312 printf("Password store initialized for %s\n", pgpid); 313 fclose(gpg); 314 } else 315 printf("Password store initialized for %s\n", pgpid); 316 } 317 318 int 319 main(int argc, char** argv) 320 { 321 char **s; 322 int i; 323 324 debug = 0; 325 326 for (s = argv, i = 0; i < argc; i++) { 327 if (!strcmp("-d", *s++)) 328 debug = 1; 329 } 330 if (argc > 1) 331 { 332 argv++; 333 loginit("pass"); 334 if (!strcmp("init", *argv)) 335 initpass(*(argv + 1)); 336 else if (!strcmp("insert", *argv)) 337 insert(*(argv + 1)); 338 else if (!strcmp("rm", *argv)) 339 delete(*(argv + 1)); 340 else 341 printpass(*argv); 342 } else 343 usage(); 344 return 0; 345 }