i3 - improved tiling WM


added --verification-script option

Patch status: rejected

Patch by Michael Ensslin

Long description:

This option lets you specify a script that is run whenever a password has been
accepted by PAM.

The entered password is given to the script's standard input.
If the script's exit code is zero, the password is accepted. otherwise, it is
rejected as if PAM would have failed.

If the can not be executed, the password is accepted as if no script was
specified.

I'm using this with a script that calls `cryptsetup luksResume` on my home
partition, which is encrypted with my login password.

To apply this patch, use:
curl http://cr.i3wm.org/patch/665/raw.patch | git am

b/i3lock.c

25
@@ -22,6 +22,7 @@
26
 #include <string.h>
27
 #include <ev.h>
28
 #include <sys/mman.h>
29
+#include <sys/wait.h>
30
 #include <xkbcommon/xkbcommon.h>
31
 #include <xkbcommon/xkbcommon-x11.h>
32
 #include <cairo.h>
33
@@ -66,6 +67,7 @@ extern unlock_state_t unlock_state;
34
 extern pam_state_t pam_state;
35
 int failed_attempts = 0;
36
 bool show_failed_attempts = false;
37
+char *verification_script = NULL;
38
 
39
 static struct xkb_state *xkb_state;
40
 static struct xkb_context *xkb_context;
41
@@ -223,13 +225,73 @@ static void discard_passwd_cb(EV_P_ ev_timer *w, int revents) {
42
     STOP_TIMER(discard_passwd_timeout);
43
 }
44
 
45
+static bool run_verification_script(void) {
46
+    if (verification_script == NULL) {
47
+        return true;
48
+    }
49
+
50
+    DEBUG("running verification script\n");
51
+
52
+    int pipefd[2];
53
+    if (pipe(pipefd) == -1) {
54
+        DEBUG("couldn't create pipe. skipping verification script\n");
55
+        return true;
56
+    }
57
+
58
+    pid_t pid = fork();
59
+    switch(pid) {
60
+    case -1:
61
+        DEBUG("couldn't fork. skipping verification script\n");
62
+        return true;
63
+        break;
64
+    case 0:
65
+        /* we're the child */
66
+        close(pipefd[1]);
67
+        dup2(pipefd[0], 0);
68
+        execl(verification_script, verification_script, NULL);
69
+        DEBUG("couldn't execute verification script\n");
70
+        exit(0);
71
+        break;
72
+    default:
73
+        /* we're the parent */
74
+        close(pipefd[0]);
75
+        size_t pos = 0;
76
+        while (pos < input_position) {
77
+            ssize_t written = write(pipefd[1], &password[pos], input_position - pos);
78
+            if (written <= 0) {
79
+                DEBUG("couldn't write password to verification script\n");
80
+                return true;
81
+            }
82
+            pos += written;
83
+        }
84
+        /* send EOF to child */
85
+        close(pipefd[1]);
86
+        int status;
87
+        if (waitpid(pid, &status, 0) == -1) {
88
+            DEBUG("couldn't wait for verification script. assuming success\n");
89
+            return true;
90
+        }
91
+
92
+        if (WEXITSTATUS(status) == 0) {
93
+            DEBUG("password accepted by verification script\n");
94
+            return true;
95
+        } else {
96
+            DEBUG("password rejected by verification script\n");
97
+            return false;
98
+        }
99
+    }
100
+}
101
+
102
 static void input_done(void) {
103
     STOP_TIMER(clear_pam_wrong_timeout);
104
     pam_state = STATE_PAM_VERIFY;
105
     redraw_screen();
106
 
107
-    if (pam_authenticate(pam_handle, 0) == PAM_SUCCESS) {
108
+    if (pam_authenticate(pam_handle, 0) == PAM_SUCCESS &&
109
+        run_verification_script()) {
110
+
111
         DEBUG("successfully authenticated\n");
112
+
113
         clear_password_memory();
114
         /* Turn the screen on, as it may have been turned off
115
          * on release of the 'enter' key. */
116
@@ -681,6 +743,7 @@ int main(int argc, char *argv[]) {
117
         {"ignore-empty-password", no_argument, NULL, 'e'},
118
         {"inactivity-timeout", required_argument, NULL, 'I'},
119
         {"show-failed-attempts", no_argument, NULL, 'f'},
120
+        {"verification-script", required_argument, NULL, 'V'},
121
         {NULL, no_argument, NULL, 0}
122
     };
123
 
124
@@ -708,6 +771,14 @@ int main(int argc, char *argv[]) {
125
             inactivity_timeout = time;
126
             break;
127
         }
128
+        case 'V': {
129
+            verification_script = optarg;
130
+            /* sanity check: is verification_script executable? */
131
+            if(access(verification_script, F_OK | X_OK) != 0) {
132
+                errx(EXIT_FAILURE, "verification script is not executable\n");
133
+            }
134
+            break;
135
+        }
136
         case 'c': {
137
             char *arg = optarg;
138
 
139
@@ -750,7 +821,7 @@ int main(int argc, char *argv[]) {
140
             break;
141
         default:
142
             errx(EXIT_FAILURE, "Syntax: i3lock [-v] [-n] [-b] [-d] [-c color] [-u] [-p win|default]"
143
-            " [-i image.png] [-t] [-e] [-I] [-f]"
144
+            " [-i image.png] [-t] [-e] [-I] [-f] [--verification-script /bin/foo]"
145
             );
146
         }
147
     }