aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/firejail/pulseaudio.c170
1 files changed, 110 insertions, 60 deletions
diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c
index ce1692ba2..4ddaba7ed 100644
--- a/src/firejail/pulseaudio.c
+++ b/src/firejail/pulseaudio.c
@@ -102,74 +102,124 @@ 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 directory if necessary 105 // create ~/.config/pulse directory if not present
106 char *homeusercfg; 106 char *dir1;
107 if (asprintf(&homeusercfg, "%s/.config", cfg.homedir) == -1) 107 if (asprintf(&dir1, "%s/.config", cfg.homedir) == -1)
108 errExit("asprintf"); 108 errExit("asprintf");
109 create_empty_dir_as_user(homeusercfg, 0700); 109 if (lstat(dir1, &s) == -1) {
110 // set environment variable if creating ~/.config wasn't successful or if it is not a directory owned by the user 110 pid_t child = fork();
111 if (lstat(homeusercfg, &s) != 0 || !S_ISDIR(s.st_mode) || s.st_uid != getuid()) { 111 if (child < 0)
112 if (arg_debug) 112 errExit("fork");
113 printf("Setting PULSE_CLIENTCONFIG environment variable\n"); 113 if (child == 0) {
114 if (setenv("PULSE_CLIENTCONFIG", pulsecfg, 1) < 0) 114 // drop privileges
115 errExit("setenv"); 115 drop_privs(0);
116 free(homeusercfg); 116
117 free(pulsecfg); 117 int rv = mkdir(dir1, 0755);
118 return; 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);
119 } 130 }
120 free(homeusercfg); 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");
145 if (lstat(dir1, &s) == -1) {
146 pid_t child = fork();
147 if (child < 0)
148 errExit("fork");
149 if (child == 0) {
150 // drop privileges
151 drop_privs(0);
152
153 int rv = mkdir(dir1, 0700);
154 if (rv == 0) {
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 }
177 free(dir1);
121 178
122 // create ~/.config/pulse directory if necessary 179 // if we have ~/.config/pulse mount the new directory, else set environment variable.
180 char *homeusercfg;
123 if (asprintf(&homeusercfg, "%s/.config/pulse", cfg.homedir) == -1) 181 if (asprintf(&homeusercfg, "%s/.config/pulse", cfg.homedir) == -1)
124 errExit("asprintf"); 182 errExit("asprintf");
125 create_empty_dir_as_user(homeusercfg, 0700); 183 if (stat(homeusercfg, &s) == 0) {
126 // set environment variable if creating ~/.config/pulse wasn't successful or if it is not a directory owned by the user 184 // get a file descriptor for ~/.config/pulse, fails if there is any symlink
127 if (lstat(homeusercfg, &s) != 0 || !S_ISDIR(s.st_mode) || s.st_uid != getuid()) { 185 int fd = safe_fd(homeusercfg, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC);
128 if (arg_debug) 186 if (fd == -1)
129 printf("Setting PULSE_CLIENTCONFIG environment variable\n"); 187 errExit("safe_fd");
130 if (setenv("PULSE_CLIENTCONFIG", pulsecfg, 1) < 0) 188 // confirm the actual mount destination is owned by the user
131 errExit("setenv"); 189 if (fstat(fd, &s) == -1 || s.st_uid != getuid())
132 free(homeusercfg); 190 errExit("fstat");
133 free(pulsecfg); 191 // preserve a read-only mount
134 return; 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);
135 } 216 }
136 217
137 // get a file descriptor for ~/.config/pulse, fails if there is any symlink 218 else {
138 int fd = safe_fd(homeusercfg, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); 219 // set environment
139 if (fd == -1) 220 if (setenv("PULSE_CLIENTCONFIG", pulsecfg, 1) < 0)
140 errExit("safe_fd"); 221 errExit("setenv");
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);
147 } 222 }
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);
173 223
174 free(pulsecfg); 224 free(pulsecfg);
175 free(homeusercfg); 225 free(homeusercfg);