diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/pulseaudio.c | 170 |
1 files changed, 60 insertions, 110 deletions
diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c index 4ddaba7ed..ce1692ba2 100644 --- a/src/firejail/pulseaudio.c +++ b/src/firejail/pulseaudio.c | |||
@@ -102,124 +102,74 @@ void pulseaudio_init(void) { | |||
102 | if (set_perms(RUN_PULSE_DIR, getuid(), getgid(), 0700)) | 102 | if (set_perms(RUN_PULSE_DIR, getuid(), getgid(), 0700)) |
103 | errExit("set_perms"); | 103 | errExit("set_perms"); |
104 | 104 | ||
105 | // create ~/.config/pulse directory if not present | 105 | // create ~/.config directory if necessary |
106 | char *dir1; | 106 | char *homeusercfg; |
107 | if (asprintf(&dir1, "%s/.config", cfg.homedir) == -1) | 107 | if (asprintf(&homeusercfg, "%s/.config", cfg.homedir) == -1) |
108 | errExit("asprintf"); | ||
109 | if (lstat(dir1, &s) == -1) { | ||
110 | pid_t child = fork(); | ||
111 | if (child < 0) | ||
112 | errExit("fork"); | ||
113 | if (child == 0) { | ||
114 | // drop privileges | ||
115 | drop_privs(0); | ||
116 | |||
117 | int rv = mkdir(dir1, 0755); | ||
118 | if (rv == 0) { | ||
119 | if (chmod(dir1, 0755)) | ||
120 | {;} // do nothing | ||
121 | } | ||
122 | #ifdef HAVE_GCOV | ||
123 | __gcov_flush(); | ||
124 | #endif | ||
125 | _exit(0); | ||
126 | } | ||
127 | // wait for the child to finish | ||
128 | waitpid(child, NULL, 0); | ||
129 | fs_logger2("create", dir1); | ||
130 | } | ||
131 | else { | ||
132 | // we expect a user owned directory | ||
133 | if (!S_ISDIR(s.st_mode) || s.st_uid != getuid()) { | ||
134 | if (S_ISLNK(s.st_mode)) | ||
135 | fprintf(stderr, "Error: user .config is a symbolic link\n"); | ||
136 | else | ||
137 | fprintf(stderr, "Error: user .config is not a directory owned by the current user\n"); | ||
138 | exit(1); | ||
139 | } | ||
140 | } | ||
141 | free(dir1); | ||
142 | |||
143 | if (asprintf(&dir1, "%s/.config/pulse", cfg.homedir) == -1) | ||
144 | errExit("asprintf"); | 108 | errExit("asprintf"); |
145 | if (lstat(dir1, &s) == -1) { | 109 | create_empty_dir_as_user(homeusercfg, 0700); |
146 | pid_t child = fork(); | 110 | // set environment variable if creating ~/.config wasn't successful or if it is not a directory owned by the user |
147 | if (child < 0) | 111 | if (lstat(homeusercfg, &s) != 0 || !S_ISDIR(s.st_mode) || s.st_uid != getuid()) { |
148 | errExit("fork"); | 112 | if (arg_debug) |
149 | if (child == 0) { | 113 | printf("Setting PULSE_CLIENTCONFIG environment variable\n"); |
150 | // drop privileges | 114 | if (setenv("PULSE_CLIENTCONFIG", pulsecfg, 1) < 0) |
151 | drop_privs(0); | 115 | errExit("setenv"); |
152 | 116 | free(homeusercfg); | |
153 | int rv = mkdir(dir1, 0700); | 117 | free(pulsecfg); |
154 | if (rv == 0) { | 118 | return; |
155 | if (chmod(dir1, 0700)) | ||
156 | {;} // do nothing | ||
157 | } | ||
158 | #ifdef HAVE_GCOV | ||
159 | __gcov_flush(); | ||
160 | #endif | ||
161 | _exit(0); | ||
162 | } | ||
163 | // wait for the child to finish | ||
164 | waitpid(child, NULL, 0); | ||
165 | fs_logger2("create", dir1); | ||
166 | } | ||
167 | else { | ||
168 | // we expect a user owned directory | ||
169 | if (!S_ISDIR(s.st_mode) || s.st_uid != getuid()) { | ||
170 | if (S_ISLNK(s.st_mode)) | ||
171 | fprintf(stderr, "Error: user .config/pulse is a symbolic link\n"); | ||
172 | else | ||
173 | fprintf(stderr, "Error: user .config/pulse is not a directory owned by the current user\n"); | ||
174 | exit(1); | ||
175 | } | ||
176 | } | 119 | } |
177 | free(dir1); | 120 | free(homeusercfg); |
178 | 121 | ||
179 | // if we have ~/.config/pulse mount the new directory, else set environment variable. | 122 | // create ~/.config/pulse directory if necessary |
180 | char *homeusercfg; | ||
181 | if (asprintf(&homeusercfg, "%s/.config/pulse", cfg.homedir) == -1) | 123 | if (asprintf(&homeusercfg, "%s/.config/pulse", cfg.homedir) == -1) |
182 | errExit("asprintf"); | 124 | errExit("asprintf"); |
183 | if (stat(homeusercfg, &s) == 0) { | 125 | create_empty_dir_as_user(homeusercfg, 0700); |
184 | // get a file descriptor for ~/.config/pulse, fails if there is any symlink | 126 | // set environment variable if creating ~/.config/pulse wasn't successful or if it is not a directory owned by the user |
185 | int fd = safe_fd(homeusercfg, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | 127 | if (lstat(homeusercfg, &s) != 0 || !S_ISDIR(s.st_mode) || s.st_uid != getuid()) { |
186 | if (fd == -1) | 128 | if (arg_debug) |
187 | errExit("safe_fd"); | 129 | printf("Setting PULSE_CLIENTCONFIG environment variable\n"); |
188 | // confirm the actual mount destination is owned by the user | ||
189 | if (fstat(fd, &s) == -1 || s.st_uid != getuid()) | ||
190 | errExit("fstat"); | ||
191 | // preserve a read-only mount | ||
192 | struct statvfs vfs; | ||
193 | if (fstatvfs(fd, &vfs) == -1) | ||
194 | errExit("fstatvfs"); | ||
195 | if ((vfs.f_flag & MS_RDONLY) == MS_RDONLY) | ||
196 | fs_rdonly(RUN_PULSE_DIR); | ||
197 | // mount via the link in /proc/self/fd | ||
198 | char *proc; | ||
199 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
200 | errExit("asprintf"); | ||
201 | if (mount(RUN_PULSE_DIR, proc, "none", MS_BIND, NULL) < 0) | ||
202 | errExit("mount pulseaudio"); | ||
203 | fs_logger2("tmpfs", homeusercfg); | ||
204 | free(proc); | ||
205 | close(fd); | ||
206 | // check /proc/self/mountinfo to confirm the mount is ok | ||
207 | MountData *mptr = get_last_mount(); | ||
208 | if (strcmp(mptr->dir, homeusercfg) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) | ||
209 | errLogExit("invalid pulseaudio mount"); | ||
210 | |||
211 | char *p; | ||
212 | if (asprintf(&p, "%s/client.conf", homeusercfg) == -1) | ||
213 | errExit("asprintf"); | ||
214 | fs_logger2("create", p); | ||
215 | free(p); | ||
216 | } | ||
217 | |||
218 | else { | ||
219 | // set environment | ||
220 | if (setenv("PULSE_CLIENTCONFIG", pulsecfg, 1) < 0) | 130 | if (setenv("PULSE_CLIENTCONFIG", pulsecfg, 1) < 0) |
221 | errExit("setenv"); | 131 | errExit("setenv"); |
132 | free(homeusercfg); | ||
133 | free(pulsecfg); | ||
134 | return; | ||
135 | } | ||
136 | |||
137 | // get a file descriptor for ~/.config/pulse, fails if there is any symlink | ||
138 | int fd = safe_fd(homeusercfg, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); | ||
139 | if (fd == -1) | ||
140 | errExit("safe_fd"); | ||
141 | // confirm again the actual mount destination is owned by the user | ||
142 | if (fstat(fd, &s) == -1) | ||
143 | errExit("fstat"); | ||
144 | if (s.st_uid != getuid()) { | ||
145 | fprintf(stderr, "Error: %s is not owned by the current user\n", homeusercfg); | ||
146 | exit(1); | ||
222 | } | 147 | } |
148 | // preserve a read-only mount | ||
149 | struct statvfs vfs; | ||
150 | if (fstatvfs(fd, &vfs) == -1) | ||
151 | errExit("fstatvfs"); | ||
152 | if ((vfs.f_flag & MS_RDONLY) == MS_RDONLY) | ||
153 | fs_rdonly(RUN_PULSE_DIR); | ||
154 | // mount via the link in /proc/self/fd | ||
155 | char *proc; | ||
156 | if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) | ||
157 | errExit("asprintf"); | ||
158 | if (mount(RUN_PULSE_DIR, proc, "none", MS_BIND, NULL) < 0) | ||
159 | errExit("mount pulseaudio"); | ||
160 | free(proc); | ||
161 | close(fd); | ||
162 | // check /proc/self/mountinfo to confirm the mount is ok | ||
163 | MountData *mptr = get_last_mount(); | ||
164 | if (strcmp(mptr->dir, homeusercfg) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) | ||
165 | errLogExit("invalid pulseaudio mount"); | ||
166 | fs_logger2("tmpfs", homeusercfg); | ||
167 | |||
168 | char *p; | ||
169 | if (asprintf(&p, "%s/client.conf", homeusercfg) == -1) | ||
170 | errExit("asprintf"); | ||
171 | fs_logger2("create", p); | ||
172 | free(p); | ||
223 | 173 | ||
224 | free(pulsecfg); | 174 | free(pulsecfg); |
225 | free(homeusercfg); | 175 | free(homeusercfg); |