Recently Written · git

pass-simple

fork of zerous' minimal pass rewrite with bugfixes

git clone https://github.com/equwal/pass-simple

Log | Files | Refs


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 }