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