💾 Archived View for gemini.rmf-dev.com › repo › Vaati › dkee › files › c55c23d086425c5ff3078d1729506… captured on 2023-11-04 at 12:26:46. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
0 /*
1 * Copyright (c) 2022 RMF <rawmonk@firemail.cc>
2 *
3 * Permission to use, copy, modify, and distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 */
15 #include <unistd.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <termios.h>
20 #include <signal.h>
21
22 #define MENU "dmenu -i"
23
24 char *cache = NULL;
25 size_t cache_length;
26
27 #ifdef __linux__
28 size_t strlcpy(char*, const char*, size_t);
29 #endif
30 int text_box(char*, size_t);
31
32 int popen2(const char *cmd, int *to, int *from, pid_t* pid) {
33 pid_t id;
34 int in[2], out[2];
35
36 if(pipe(in)) return -1;
37 if(pipe(out)) return -1;
38
39 id = fork();
40 if(id < 0) return -1;
41 if(id == 0) {
42 close(in[1]);
43 dup2(in[0], 0);
44 close(out[0]);
45 dup2(out[1], 1);
46 execl("/bin/sh", "sh", "-c", cmd, NULL);
47 exit(-1);
48 }
49 *to = in[1];
50 *from = out[0];
51 close(in[0]);
52 close(out[1]);
53 if (pid) *pid = id;
54 return 0;
55 }
56
57 char *getlist(const char *safe, const char *password, size_t *length) {
58 char* data = NULL, cmd[4096];
59 int to, from;
60
61 snprintf(cmd, sizeof(cmd), "keepassxc-cli ls -R %s", safe);
62
63 if (popen2(cmd, &to, &from, NULL)) return NULL;
64 write(to, password, strlen(password));
65 close(to);
66
67 while (1) {
68 int ret;
69 char buf[1024];
70 ret = read(from, buf, sizeof(buf));
71 if (ret < 1) break;
72 data = realloc(data, *length + ret);
73 if (!data) return NULL;
74 memcpy(&data[*length], buf, ret);
75 *length += ret;
76 }
77 close(from);
78 if (*length <= 0) return NULL;
79
80 return data;
81 }
82
83 int start(const char* safe, const char* password, pid_t* pid) {
84 char* data = NULL;
85 size_t length, i;
86 char cmd[4096], path[4096], tmp[4096], *ptr;
87 int j = 0, path_len = 0;
88 int to, from;
89
90 /* read output from keepassxc */
91 if (!cache) {
92 data = getlist(safe, password, &length);
93 if (!data) return -1;
94 cache = data;
95 cache_length = length;
96 } else {
97 data = cache;
98 length = cache_length;
99 }
100
101 /* start dmenu and send it formatted password lists */
102 popen2(MENU, &to, &from, NULL);
103
104 /* format keypassxc output */
105 path[0] = '\0';
106 for (i = 0; i < length; i++) {
107 tmp[j] = data[i];
108 if (!j || tmp[j] != '\n') goto increment;
109 if (tmp[j - 1] == '/') {
110 int depth, pos, slash;
111 tmp[j] = '\0';
112 j = -1;
113 ptr = tmp;
114 while (*ptr == ' ') ptr++;
115 depth = (ptr - tmp)/2;
116
117 pos = 0;
118 slash = 0;
119 if (!depth)
120 path_len = 0;
121 while (depth && pos < path_len) {
122 if (path[pos] == '/') slash++;
123 if (slash == depth) {
124 /* format string... */
125 path_len = pos + 1;
126 break;
127 }
128 pos++;
129 }
130
131 path_len += strlcpy(&path[path_len], ptr,
132 sizeof(path) - path_len);
133 goto increment;
134 }
135 tmp[j + 1] = '\0';
136 ptr = tmp;
137 while (*ptr == ' ') ptr++;
138 write(to, path, path_len);
139 write(to, ptr, j + 1 - (ptr - tmp));
140 j = -1;
141 increment:
142 j++;
143 }
144 close(to);
145
146 /* read dmenu output from dmenu */
147 length = read(from, path, sizeof(path));
148 if (length < 1) return -1;
149 close(from);
150 path[length - 1] = 0;
151
152 /* send dmenu output keypassxc */
153 snprintf(cmd, sizeof(cmd), "keepassxc-cli clip \"%s\" \"%s\"",
154 safe, path);
155 if (popen2(cmd, &to, &from, pid)) return -1;
156 write(to, password, strlen(password));
157 close(to);
158 close(from);
159
160 return 0;
161 }
162
163 int passwd(char* buf, int max) {
164 struct termios term;
165 int c, pos = 0;
166
167 tcgetattr(1, &term);
168 term.c_lflag &= ~ECHO;
169 tcsetattr(1, TCSANOW, &term);
170
171 buf[0] = '\0';
172 while ((c = fgetc(stdin)) != '\n') {
173 buf[pos++] = (char)c;
174 if (pos + 1 >= max)
175 break;
176 }
177 buf[pos] = '\0';
178
179 term.c_lflag |= ECHO;
180 tcsetattr(1, TCSANOW, &term);
181 return pos;
182 }
183
184 int main(int argc, char* argv[]) {
185
186 char password[64];
187 char pid_path[1024];
188 const char* safe = argv[1];
189 sigset_t set;
190 int sig;
191 FILE *f;
192 pid_t pid = -1;
193 int x11 = 1;
194
195 if (argc < 2) {
196 wrong_args:
197 printf("%s [-t] <safe>\n", argv[0]);
198 return -1;
199 }
200 if (argc > 2) {
201 if (strcmp("-t", argv[1])) goto wrong_args;
202 x11 = 0;
203 safe = argv[2];
204 }
205
206 if (x11) {
207 /* X11 */
208 if (text_box(password, sizeof(password))) return -1;
209 } else {
210 /* terminal */
211 printf("Password : ");
212 fflush(stdout);
213 passwd(password, sizeof(password));
214 printf("\n");
215 }
216
217 /* prevent zombie processes */
218 signal(SIGCHLD, SIG_IGN);
219
220 /* exit if invalid password */
221 if (start(safe, password, NULL)) {
222 return -1;
223 }
224
225 /* write pid to file */
226 snprintf(pid_path, sizeof(pid_path),
227 "%s/.cache/.dkee.pid", getenv("HOME"));
228 f = fopen(pid_path, "wb");
229 if (!f) {
230 printf("unable to write pid to : %s\n", pid_path);
231 return -1;
232 }
233
234 daemon(1, 1);
235 fprintf(f, "%d", getpid());
236 fclose(f);
237
238 sigemptyset(&set);
239 sigaddset(&set, SIGUSR1);
240 sigaddset(&set, SIGUSR2);
241 sigprocmask(SIG_BLOCK, &set, NULL);
242 while (1) {
243 sigwait(&set, &sig);
244 if (pid > 0)
245 kill(pid, SIGKILL);
246 if (sig == SIGUSR1) {
247 start(safe, password, &pid);
248 } else if (sig == SIGUSR2) {
249 memset(cache, 0, cache_length);
250 free(cache);
251 cache_length = 0;
252 cache = getlist(safe, password, &cache_length);
253 } else break;
254 }
255
256 remove(pid_path);
257 return 0;
258 }
259