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