1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <sys/stat.h>
32
33 #include <pulse/util.h>
34 #include <pulse/xmalloc.h>
35 #include <pulsecore/core-error.h>
36 #include <pulsecore/core-util.h>
37 #include <pulsecore/log.h>
38 #include <pulsecore/random.h>
39 #include <pulsecore/macro.h>
40
41 #include "authkey.h"
42
43 /* Generate a new authentication key, store it in file fd and return it in *data */
generate(int fd,void * ret_data,size_t length)44 static int generate(int fd, void *ret_data, size_t length) {
45 ssize_t r;
46
47 pa_assert(fd >= 0);
48 pa_assert(ret_data);
49 pa_assert(length > 0);
50
51 pa_random(ret_data, length);
52
53 lseek(fd, (off_t) 0, SEEK_SET);
54 if (ftruncate(fd, (off_t) 0) < 0) {
55 pa_log("Failed to truncate cookie file: %s", pa_cstrerror(errno));
56 return -1;
57 }
58
59 if ((r = pa_loop_write(fd, ret_data, length, NULL)) < 0 || (size_t) r != length) {
60 pa_log("Failed to write cookie file: %s", pa_cstrerror(errno));
61 return -1;
62 }
63
64 return 0;
65 }
66
67 #ifndef O_BINARY
68 #define O_BINARY 0
69 #endif
70
71 /* Load an authentication cookie from file fn and store it in data. If
72 * the cookie file doesn't exist, create it */
load(const char * fn,bool create,void * data,size_t length)73 static int load(const char *fn, bool create, void *data, size_t length) {
74 int fd = -1;
75 int writable = 1;
76 int unlock = 0, ret = -1;
77 ssize_t r;
78
79 pa_assert(fn);
80 pa_assert(data);
81 pa_assert(length > 0);
82
83 if (create)
84 pa_make_secure_parent_dir(fn, pa_in_system_mode() ? 0755U : 0700U, -1, -1, false);
85
86 if ((fd = pa_open_cloexec(fn, (create ? O_RDWR|O_CREAT : O_RDONLY)|O_BINARY, S_IRUSR|S_IWUSR)) < 0) {
87
88 if (!create || errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY)) < 0) {
89 pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
90 goto finish;
91 } else
92 writable = 0;
93 }
94
95 unlock = pa_lock_fd(fd, 1) >= 0;
96
97 if ((r = pa_loop_read(fd, data, length, NULL)) < 0) {
98 pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
99 goto finish;
100 }
101
102 if ((size_t) r != length) {
103 pa_log_debug("Got %d bytes from cookie file '%s', expected %d", (int) r, fn, (int) length);
104
105 if (!writable) {
106 pa_log_warn("Unable to write cookie to read-only file");
107 goto finish;
108 }
109
110 if (generate(fd, data, length) < 0)
111 goto finish;
112 }
113
114 ret = 0;
115
116 finish:
117
118 if (fd >= 0) {
119
120 if (unlock)
121 pa_lock_fd(fd, 0);
122
123 if (pa_close(fd) < 0) {
124 pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
125 ret = -1;
126 }
127 }
128
129 return ret;
130 }
131
132 /* If the specified file path starts with / return it, otherwise
133 * return path prepended with the config home directory. */
normalize_path(const char * fn,char ** _r)134 static int normalize_path(const char *fn, char **_r) {
135 pa_assert(fn);
136 pa_assert(_r);
137
138 if (!pa_is_path_absolute(fn))
139 return pa_append_to_config_home_dir(fn, _r);
140
141 *_r = pa_xstrdup(fn);
142 return 0;
143 }
144
pa_authkey_load(const char * fn,bool create,void * data,size_t length)145 int pa_authkey_load(const char *fn, bool create, void *data, size_t length) {
146 char *p;
147 int ret;
148
149 pa_assert(fn);
150 pa_assert(data);
151 pa_assert(length > 0);
152
153 if ((ret = normalize_path(fn, &p)) < 0)
154 return ret;
155
156 if ((ret = load(p, create, data, length)) < 0)
157 pa_log_warn("Failed to load authentication key '%s': %s", p, (ret < 0) ? pa_cstrerror(errno) : "File corrupt");
158
159 pa_xfree(p);
160
161 return ret;
162 }
163
164 /* Store the specified cookie in the specified cookie file */
pa_authkey_save(const char * fn,const void * data,size_t length)165 int pa_authkey_save(const char *fn, const void *data, size_t length) {
166 int fd = -1;
167 int unlock = 0, ret;
168 ssize_t r;
169 char *p;
170
171 pa_assert(fn);
172 pa_assert(data);
173 pa_assert(length > 0);
174
175 if ((ret = normalize_path(fn, &p)) < 0)
176 return ret;
177
178 if ((fd = pa_open_cloexec(p, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
179 pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
180 ret = -1;
181 goto finish;
182 }
183
184 unlock = pa_lock_fd(fd, 1) >= 0;
185
186 if ((r = pa_loop_write(fd, data, length, NULL)) < 0 || (size_t) r != length) {
187 pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
188 ret = -1;
189 goto finish;
190 }
191
192 finish:
193
194 if (fd >= 0) {
195
196 if (unlock)
197 pa_lock_fd(fd, 0);
198
199 if (pa_close(fd) < 0) {
200 pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
201 ret = -1;
202 }
203 }
204
205 pa_xfree(p);
206
207 return ret;
208 }
209