diff options
author | Drew DeVault <sir@cmpwn.com> | 2018-09-28 12:18:54 +0200 |
---|---|---|
committer | Drew DeVault <sir@cmpwn.com> | 2018-09-28 13:53:01 +0200 |
commit | c9773491207d36d6f5e651adcb7a64c7a015bba3 (patch) | |
tree | ed2d195ac03609bdb1b3132d1ef748ad59132e8a /swaylock/shadow.c | |
parent | Merge pull request #2717 from ianyfan/tablet-config (diff) | |
download | sway-c9773491207d36d6f5e651adcb7a64c7a015bba3.tar.gz sway-c9773491207d36d6f5e651adcb7a64c7a015bba3.tar.zst sway-c9773491207d36d6f5e651adcb7a64c7a015bba3.zip |
Add support for building swaylock without PAM
This involves setuid'ing swaylock, which then forks and drops perms on
the parent process. The child process remains root and listens on a pipe
for requests to validate passwords against /etc/shadow.
Diffstat (limited to 'swaylock/shadow.c')
-rw-r--r-- | swaylock/shadow.c | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/swaylock/shadow.c b/swaylock/shadow.c new file mode 100644 index 00000000..1f10514c --- /dev/null +++ b/swaylock/shadow.c | |||
@@ -0,0 +1,128 @@ | |||
1 | #define _XOPEN_SOURCE | ||
2 | #include <pwd.h> | ||
3 | #include <shadow.h> | ||
4 | #include <stdbool.h> | ||
5 | #include <sys/types.h> | ||
6 | #include <unistd.h> | ||
7 | #include <wlr/util/log.h> | ||
8 | #include "swaylock/swaylock.h" | ||
9 | |||
10 | static int comm[2][2]; | ||
11 | |||
12 | void run_child(void) { | ||
13 | /* This code runs as root */ | ||
14 | struct passwd *pwent = getpwuid(getuid()); | ||
15 | if (!pwent) { | ||
16 | wlr_log_errno(WLR_ERROR, "failed to getpwuid"); | ||
17 | exit(EXIT_FAILURE); | ||
18 | } | ||
19 | char *encpw = pwent->pw_passwd; | ||
20 | if (strcmp(encpw, "x") == 0) { | ||
21 | struct spwd *swent = getspnam(pwent->pw_name); | ||
22 | if (!swent) { | ||
23 | wlr_log_errno(WLR_ERROR, "failed to getspnam"); | ||
24 | exit(EXIT_FAILURE); | ||
25 | } | ||
26 | encpw = swent->sp_pwdp; | ||
27 | } | ||
28 | wlr_log(WLR_DEBUG, "prepared to authorize user %s", pwent->pw_name); | ||
29 | |||
30 | size_t size; | ||
31 | char *buf; | ||
32 | while (1) { | ||
33 | ssize_t amt; | ||
34 | amt = read(comm[0][0], &size, sizeof(size)); | ||
35 | if (amt == 0) { | ||
36 | break; | ||
37 | } else if (amt < 0) { | ||
38 | wlr_log_errno(WLR_ERROR, "read pw request"); | ||
39 | } | ||
40 | wlr_log(WLR_DEBUG, "received pw check request"); | ||
41 | buf = malloc(size); | ||
42 | if (!buf) { | ||
43 | wlr_log_errno(WLR_ERROR, "failed to malloc pw buffer"); | ||
44 | exit(EXIT_FAILURE); | ||
45 | } | ||
46 | size_t offs = 0; | ||
47 | do { | ||
48 | amt = read(comm[0][0], &buf[offs], size - offs); | ||
49 | if (amt <= 0) { | ||
50 | wlr_log_errno(WLR_ERROR, "failed to read pw"); | ||
51 | exit(EXIT_FAILURE); | ||
52 | } | ||
53 | offs += (size_t)amt; | ||
54 | } while (offs < size); | ||
55 | bool result = false; | ||
56 | char *c = crypt(buf, encpw); | ||
57 | if (c == NULL) { | ||
58 | wlr_log_errno(WLR_ERROR, "crypt"); | ||
59 | } | ||
60 | result = strcmp(c, encpw) == 0; | ||
61 | if (write(comm[1][1], &result, sizeof(result)) != sizeof(result)) { | ||
62 | wlr_log_errno(WLR_ERROR, "failed to write pw check result"); | ||
63 | exit(EXIT_FAILURE); | ||
64 | } | ||
65 | free(buf); | ||
66 | } | ||
67 | exit(EXIT_SUCCESS); | ||
68 | } | ||
69 | |||
70 | void initialize_pw_backend(void) { | ||
71 | if (geteuid() != 0) { | ||
72 | wlr_log(WLR_ERROR, "swaylock needs to be setuid to read /etc/shadow"); | ||
73 | exit(EXIT_FAILURE); | ||
74 | } | ||
75 | if (pipe(comm[0]) != 0) { | ||
76 | wlr_log_errno(WLR_ERROR, "failed to create pipe"); | ||
77 | exit(EXIT_FAILURE); | ||
78 | } | ||
79 | if (pipe(comm[1]) != 0) { | ||
80 | wlr_log_errno(WLR_ERROR, "failed to create pipe"); | ||
81 | exit(EXIT_FAILURE); | ||
82 | } | ||
83 | pid_t child = fork(); | ||
84 | if (child == 0) { | ||
85 | close(comm[0][1]); | ||
86 | close(comm[1][0]); | ||
87 | run_child(); | ||
88 | } else if (child < 0) { | ||
89 | wlr_log_errno(WLR_ERROR, "failed to fork"); | ||
90 | exit(EXIT_FAILURE); | ||
91 | } | ||
92 | close(comm[0][0]); | ||
93 | close(comm[1][1]); | ||
94 | if (setgid(getgid()) != 0) { | ||
95 | wlr_log_errno(WLR_ERROR, "Unable to drop root"); | ||
96 | exit(EXIT_FAILURE); | ||
97 | } | ||
98 | if (setuid(getuid()) != 0) { | ||
99 | wlr_log_errno(WLR_ERROR, "Unable to drop root"); | ||
100 | exit(EXIT_FAILURE); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | bool attempt_password(struct swaylock_password *pw) { | ||
105 | bool result = false; | ||
106 | size_t len = pw->len + 1; | ||
107 | size_t offs = 0; | ||
108 | if (write(comm[0][1], &len, sizeof(len)) < 0) { | ||
109 | wlr_log_errno(WLR_ERROR, "Failed to request pw check"); | ||
110 | goto ret; | ||
111 | } | ||
112 | do { | ||
113 | ssize_t amt = write(comm[0][1], &pw->buffer[offs], len - offs); | ||
114 | if (amt < 0) { | ||
115 | wlr_log_errno(WLR_ERROR, "Failed to write pw buffer"); | ||
116 | goto ret; | ||
117 | } | ||
118 | offs += amt; | ||
119 | } while (offs < len); | ||
120 | if (read(comm[1][0], &result, sizeof(result)) != sizeof(result)) { | ||
121 | wlr_log_errno(WLR_ERROR, "Failed to read pw result"); | ||
122 | goto ret; | ||
123 | } | ||
124 | wlr_log(WLR_DEBUG, "pw result: %d", result); | ||
125 | ret: | ||
126 | clear_password_buffer(pw); | ||
127 | return result; | ||
128 | } | ||