Go Back

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