~siretart/cryptsetup/debian

« back to all changes in this revision

Viewing changes to debian/askpass.c

  • Committer: Reinhard Tartler
  • Date: 2008-06-13 19:34:53 UTC
  • Revision ID: siretart@tauware.de-20080613193453-ywl35m67ce8pzvp3
import cryptsetup_1.0.6-1.dsc

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define _GNU_SOURCE
 
2
#include <stdio.h>
 
3
#include <unistd.h>
 
4
#include <sys/types.h>
 
5
#include <sys/stat.h>
 
6
#include <fcntl.h>
 
7
#include <stdarg.h>
 
8
#include <stdlib.h>
 
9
#include <errno.h>
 
10
#include <stdbool.h>
 
11
#include <string.h>
 
12
#include <termios.h>
 
13
#include <sys/klog.h>
 
14
#include <sys/select.h>
 
15
#include <sys/ioctl.h>
 
16
#include <signal.h>
 
17
 
 
18
#define DEBUG 0
 
19
 
 
20
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 
21
 
 
22
static bool disable_method(const char *method);
 
23
 
 
24
/*****************************************************************************
 
25
 * Utility functions                                                         *
 
26
 *****************************************************************************/
 
27
static void
 
28
debug(const char *fmt, ...)
 
29
{
 
30
        va_list ap;
 
31
        
 
32
        if (!DEBUG)
 
33
                return;
 
34
        va_start(ap, fmt);
 
35
        vfprintf(stderr, fmt, ap);
 
36
        va_end(ap);
 
37
}
 
38
 
 
39
static void
 
40
die_error(const char *fmt, ...)
 
41
{
 
42
        va_list ap;
 
43
 
 
44
        va_start(ap, fmt);
 
45
        vfprintf(stderr, fmt, ap);
 
46
        va_end(ap);
 
47
        exit(EXIT_FAILURE);
 
48
}
 
49
 
 
50
static void
 
51
usage(const char *arg0, const char *errmsg)
 
52
{
 
53
        if (errmsg)
 
54
                die_error("Error: %s\nUsage: %s PROMPT\n", errmsg, arg0);
 
55
        else
 
56
                die_error("Usage: %s PROMPT\n", arg0);
 
57
}
 
58
 
 
59
static void
 
60
fifo_common_finish(int fd, char **buf, size_t *used, size_t *size)
 
61
{
 
62
        if (fd >= 0)
 
63
                close(fd);
 
64
 
 
65
        if (!*buf)
 
66
                return;
 
67
 
 
68
        memset(*buf, '\0', *size);
 
69
        free(*buf);
 
70
        *buf = NULL;
 
71
        *used = 0;
 
72
        *size = 0;
 
73
}
 
74
 
 
75
static bool
 
76
fifo_common_read(int fd, char **buf, size_t *used, size_t *size)
 
77
{
 
78
        ssize_t result;
 
79
 
 
80
again:
 
81
        if ((*size - *used) == 0) {
 
82
                *size += 4096;
 
83
                *buf = realloc(*buf, *size);
 
84
                if (!*buf) {
 
85
                        *size = 0;
 
86
                        *used = 0;
 
87
                        debug("Failed to allocate memory for passphrase\n");
 
88
                        return false;
 
89
                }
 
90
        }
 
91
 
 
92
reread:
 
93
        result = read(fd, *buf + *used, *size - *used);
 
94
 
 
95
        if (result < 0) {
 
96
                if (errno == EAGAIN)
 
97
                        return false;
 
98
                if (errno == EINTR)
 
99
                        goto reread;
 
100
                debug("Error when reading from fifo\n");
 
101
                return false;
 
102
        }
 
103
 
 
104
        debug("Read %i bytes from fifo\n", (int)result);
 
105
        *used += result;
 
106
 
 
107
        if (result == 0)
 
108
                return true;
 
109
 
 
110
        goto again;
 
111
}
 
112
 
 
113
 
 
114
/*****************************************************************************
 
115
 * usplash functions                                                         *
 
116
 *****************************************************************************/
 
117
#define USPLASH_WRITE_FIFO "/dev/.initramfs/usplash_fifo"
 
118
#define USPLASH_READ_FIFO "/dev/.initramfs/usplash_outfifo"
 
119
#define USPLASH_CONSOLE "/dev/tty8"
 
120
static size_t usplashused = 0;
 
121
static size_t usplashsize = 0;
 
122
static char *usplashbuf = NULL;
 
123
static bool usplashwaiting = false;
 
124
 
 
125
static void
 
126
usplash_finish(int fd)
 
127
{
 
128
        int console;
 
129
        char buf[100];
 
130
        ssize_t ret;
 
131
        int tries = 0;
 
132
        bool somedata = false;
 
133
 
 
134
        if (usplashwaiting) {
 
135
                /* This is ugly, but we need to unwedge usplash if a different
 
136
                 * method has been used to provide the passphrase and usplash
 
137
                 * is still waiting for user input
 
138
                 */
 
139
                debug("Usplash cleanup fd %i\n", fd);
 
140
                console = open(USPLASH_CONSOLE, O_RDWR);
 
141
                if (console >= 0) {
 
142
                        /* Send newline to usplash */
 
143
                        ioctl(console, TIOCSTI, "\n");
 
144
                        close(console);
 
145
 
 
146
                        /* Flush (partial) passphrase from pipe */
 
147
                        while (tries < 10) {
 
148
                                ret = read(fd, buf, sizeof(buf));
 
149
 
 
150
                                if (ret > 0) {
 
151
                                        somedata = true;
 
152
                                        continue;
 
153
                                }
 
154
 
 
155
                                if (ret < 0 && errno == EINTR)
 
156
                                        continue;
 
157
 
 
158
                                if ((ret <  0 && errno == EAGAIN) ||
 
159
                                    (ret == 0 && somedata == false)) {
 
160
                                        sleep(1);
 
161
                                        tries++;
 
162
                                        continue;
 
163
                                }
 
164
 
 
165
                                break;
 
166
                        }
 
167
                        memset(buf, '\0', sizeof(buf));
 
168
                }
 
169
                usplashwaiting = false;
 
170
        }
 
171
 
 
172
        fifo_common_finish(fd, &usplashbuf, &usplashused, &usplashsize);
 
173
}
 
174
 
 
175
static bool
 
176
usplash_read(int fd, char **buf, size_t *size)
 
177
{
 
178
        debug("In usplash_read\n");
 
179
        if (fifo_common_read(fd, &usplashbuf, &usplashused, &usplashsize)) {
 
180
                *buf = usplashbuf;
 
181
                *size = usplashused;
 
182
                usplashwaiting = false;
 
183
                return true;
 
184
        }
 
185
 
 
186
        return false;
 
187
}
 
188
 
 
189
static int
 
190
usplash_prepare(const char *prompt)
 
191
{
 
192
        int wrfd = -1;
 
193
        int rdfd = -1;
 
194
        ssize_t len;
 
195
        char command[strlen(prompt) + strlen("INPUTQUIET") + 2];
 
196
 
 
197
        sprintf(command, "INPUTQUIET %s", prompt);
 
198
 
 
199
        wrfd = open(USPLASH_WRITE_FIFO, O_WRONLY | O_NONBLOCK);
 
200
        if (wrfd < 0)
 
201
                goto out;
 
202
 
 
203
        len = write(wrfd, command, strlen(command) + 1);
 
204
        if (len < 0)
 
205
                goto out;
 
206
 
 
207
        rdfd = open(USPLASH_READ_FIFO, O_RDONLY | O_NONBLOCK);
 
208
        /* If usplash is enabled, disable console */
 
209
        if (rdfd >= 0) {
 
210
                disable_method("console");
 
211
                usplashwaiting = true;
 
212
        }
 
213
 
 
214
out:
 
215
        if (wrfd >= 0)
 
216
                close(wrfd);
 
217
        return rdfd;
 
218
}
 
219
 
 
220
/*****************************************************************************
 
221
 * fifo functions                                                            *
 
222
 *****************************************************************************/
 
223
#define FIFO_PATH "/lib/cryptsetup/passfifo"
 
224
static size_t fifoused = 0;
 
225
static size_t fifosize = 0;
 
226
static char *fifobuf = NULL;
 
227
 
 
228
static void
 
229
fifo_finish(int fd)
 
230
{
 
231
        fifo_common_finish(fd, &fifobuf, &fifoused, &fifosize);
 
232
}
 
233
 
 
234
static bool
 
235
fifo_read(int fd, char **buf, size_t *size)
 
236
{
 
237
        debug("In fifo_read\n");
 
238
        if (fifo_common_read(fd, &fifobuf, &fifoused, &fifosize)) {
 
239
                *buf = fifobuf;
 
240
                *size = fifoused;
 
241
                return true;
 
242
        }
 
243
 
 
244
        return false;
 
245
}
 
246
 
 
247
static int
 
248
fifo_prepare(const char *prompt)
 
249
{
 
250
        int ret;
 
251
 
 
252
        ret = mkfifo(FIFO_PATH, 0600);
 
253
        if (ret && errno != EEXIST)
 
254
                return -1;
 
255
 
 
256
        return open(FIFO_PATH, O_RDONLY | O_NONBLOCK);
 
257
}
 
258
 
 
259
/*****************************************************************************
 
260
 * console functions                                                         *
 
261
 *****************************************************************************/
 
262
#define CONSOLE_PATH "/dev/console"
 
263
static struct termios term_old;
 
264
static bool term_set = false;
 
265
static char *consolebuf = NULL;
 
266
static size_t consolebuflen = 0;
 
267
 
 
268
static void
 
269
console_finish(int fd)
 
270
{
 
271
        if (consolebuf) {
 
272
                memset(consolebuf, '\0', consolebuflen);
 
273
                free(consolebuf);
 
274
                consolebuf = NULL;
 
275
                consolebuflen = 0;
 
276
        }
 
277
 
 
278
        if (!term_set || fd < 0)
 
279
                return;
 
280
 
 
281
        term_set = false;
 
282
        tcsetattr(fd, TCSAFLUSH, &term_old);
 
283
        fprintf(stderr, "\n");
 
284
        klogctl(7, NULL, 0);
 
285
}
 
286
 
 
287
bool
 
288
console_read(int fd, char **buf, size_t *size)
 
289
{
 
290
        ssize_t nread;
 
291
 
 
292
        /* Console is in ICANON mode so we'll get entire lines */
 
293
        nread = getline(&consolebuf, &consolebuflen, stdin);
 
294
 
 
295
        if (nread < 0)
 
296
                return NULL;
 
297
 
 
298
        /* Strip trailing newline, if any */
 
299
        if (nread > 0 && consolebuf[nread - 1] == '\n') {
 
300
                nread--;
 
301
                consolebuf[nread] = '\0';
 
302
        }
 
303
 
 
304
        *size = nread;
 
305
        *buf = consolebuf;
 
306
 
 
307
        return true;
 
308
}
 
309
 
 
310
static int
 
311
console_prepare(const char *prompt)
 
312
{
 
313
        struct termios term_new;
 
314
 
 
315
        if (!isatty(STDIN_FILENO)) {
 
316
                if (access(CONSOLE_PATH, R_OK | W_OK)) {
 
317
                        debug("No access to console device " CONSOLE_PATH "\n");
 
318
                        return -1;
 
319
                }
 
320
 
 
321
                if (!freopen(CONSOLE_PATH, "r", stdin)  ||
 
322
                    !freopen(CONSOLE_PATH, "a", stdout) ||
 
323
                    !freopen(CONSOLE_PATH, "a", stderr) ||
 
324
                    !isatty(STDIN_FILENO)) {
 
325
                        debug("Failed to open console\n");
 
326
                        return -1;
 
327
                }
 
328
        }
 
329
 
 
330
        if (tcgetattr(STDIN_FILENO, &term_old)) {
 
331
                debug("Failed to get terminal settings\n");
 
332
                return -1;
 
333
        }
 
334
 
 
335
        term_new = term_old;
 
336
        term_new.c_lflag &= ~ECHO;
 
337
        term_new.c_lflag |= ICANON;
 
338
 
 
339
        if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_new)) {
 
340
                debug("Failed to disable echoing\n");
 
341
                return -1;
 
342
        }
 
343
 
 
344
        if (fprintf(stderr, prompt) < 0) {
 
345
                debug("Failed to print prompt\n");
 
346
                tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_old);
 
347
                return -1;
 
348
        }
 
349
 
 
350
        /* Disable printk to console */
 
351
        klogctl(6, NULL, 0);
 
352
        term_set = true;
 
353
        return STDIN_FILENO;
 
354
}
 
355
 
 
356
/*****************************************************************************
 
357
 * main functions                                                            *
 
358
 *****************************************************************************/
 
359
 
 
360
struct method {
 
361
        const char *name;
 
362
        int (*prepare)(const char *prompt);
 
363
        bool (*read)(int fd, char **buf, size_t *size);
 
364
        void (*finish)(int fd);
 
365
        bool enabled;
 
366
        int fd;
 
367
};
 
368
 
 
369
static struct method methods[] = {
 
370
        { "usplash", usplash_prepare, usplash_read, usplash_finish, true, -1 },
 
371
        { "fifo", fifo_prepare, fifo_read, fifo_finish, true, -1 },
 
372
        { "console", console_prepare, console_read, console_finish, true, -1 }
 
373
};
 
374
 
 
375
static bool
 
376
disable_method(const char *method)
 
377
{
 
378
        int i;
 
379
        bool result = false;
 
380
 
 
381
        for (i = 0; i < ARRAY_SIZE(methods); i++) {
 
382
                if (method && strcmp(methods[i].name, method))
 
383
                        continue;
 
384
                if (!methods[i].enabled)
 
385
                        continue;
 
386
 
 
387
                methods[i].finish(methods[i].fd);
 
388
                methods[i].fd = -1;
 
389
                methods[i].enabled = false;
 
390
                result = true;
 
391
        }
 
392
 
 
393
        return result;
 
394
}
 
395
 
 
396
int
 
397
main(int argc, char **argv, char **envp)
 
398
{
 
399
        char *pass = NULL;
 
400
        size_t passlen = 0;
 
401
        int i;
 
402
        int nfds;
 
403
        fd_set fds;
 
404
        int ret;
 
405
        bool done = false;
 
406
        sigset_t sigset;
 
407
 
 
408
        if (argc != 2)
 
409
                usage(argv[0], "incorrect number of arguments");
 
410
 
 
411
        sigfillset(&sigset);
 
412
        sigprocmask(SIG_BLOCK, &sigset, NULL);
 
413
 
 
414
        for (i = 0; i < ARRAY_SIZE(methods); i++) {
 
415
                if (!methods[i].enabled)
 
416
                        continue;
 
417
                debug("Enabling method %s\n", methods[i].name);
 
418
                methods[i].fd = methods[i].prepare(argv[1]);
 
419
                if (methods[i].fd < 0)
 
420
                        methods[i].enabled = false;
 
421
        }
 
422
 
 
423
        while (!done) {
 
424
                nfds = 0;
 
425
                FD_ZERO(&fds);
 
426
                for (i = 0; i < ARRAY_SIZE(methods); i++) {
 
427
                        if (!methods[i].enabled || methods[i].fd < 0)
 
428
                                continue;
 
429
                        debug("method %i has fd %i and name %s\n", i, methods[i].fd, methods[i].name);
 
430
                        FD_SET(methods[i].fd, &fds);
 
431
                        if (methods[i].fd + 1 > nfds)
 
432
                                nfds = methods[i].fd + 1;
 
433
                }
 
434
 
 
435
                if (nfds == 0) {
 
436
                        debug("All methods disabled\n");
 
437
                        exit(EXIT_FAILURE);
 
438
                }
 
439
 
 
440
                debug("Starting select with nfds %i\n", nfds);
 
441
                ret = select(nfds, &fds, NULL, NULL, NULL);
 
442
 
 
443
                if (ret <= 0) {
 
444
                        if (ret == 0 || errno == EINTR)
 
445
                                continue;
 
446
                        debug("Select failed\n");
 
447
                        disable_method(NULL);
 
448
                        exit(EXIT_FAILURE);
 
449
                }
 
450
 
 
451
                for (i = 0; i < ARRAY_SIZE(methods); i++) {
 
452
                        if (!methods[i].enabled || methods[i].fd < 0)
 
453
                                continue;
 
454
                        if (!FD_ISSET(methods[i].fd, &fds))
 
455
                                continue;
 
456
                        if (methods[i].read(methods[i].fd, &pass, &passlen) && pass) {
 
457
                                done = true;
 
458
                                break;
 
459
                        }
 
460
                }
 
461
        }
 
462
 
 
463
        write(STDOUT_FILENO, pass, passlen);
 
464
        disable_method(NULL);
 
465
        exit(EXIT_SUCCESS);
 
466
}
 
467