Ticket #602: authkey.c

File authkey.c, 6.8 KB (added by abouju, 3 years ago)

src/pulsecore/authkey.c

Line 
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, write to the Free Software
19  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20  USA.
21***/
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <unistd.h>
28#include <fcntl.h>
29#include <string.h>
30#include <errno.h>
31#include <stdio.h>
32#include <inttypes.h>
33#include <stdlib.h>
34#include <time.h>
35#include <limits.h>
36#include <sys/stat.h>
37
38#include <pulse/util.h>
39#include <pulsecore/core-error.h>
40#include <pulsecore/core-util.h>
41#include <pulsecore/log.h>
42#include <pulsecore/random.h>
43#include <pulsecore/macro.h>
44#include <pulse/xmalloc.h> 
45
46#include "authkey.h"
47
48/* Generate a new authorization key, store it in file fd and return it in *data  */
49static int generate(int fd, void *ret_data, size_t length) {
50    ssize_t r;
51
52    pa_assert(fd >= 0);
53    pa_assert(ret_data);
54    pa_assert(length > 0);
55
56    pa_random(ret_data, length);
57
58    lseek(fd, (off_t) 0, SEEK_SET);
59    (void) ftruncate(fd, (off_t) 0);
60
61    if ((r = pa_loop_write(fd, ret_data, length, NULL)) < 0 || (size_t) r != length) {
62        pa_log("Failed to write cookie file: %s", pa_cstrerror(errno));
63        return -1;
64    }
65
66    return 0;
67}
68
69#ifndef O_BINARY
70#define O_BINARY 0
71#endif
72
73#ifndef O_NOCTTY
74#define O_NOCTTY 0
75#endif
76
77/* Load an euthorization cookie from file fn and store it in data. If
78 * the cookie file doesn't exist, create it */
79static int load(const char *fn, void *data, size_t length) {
80    int fd = -1;
81    int writable = 1;
82    int unlock = 0, ret = -1;
83    ssize_t r;
84
85    pa_assert(fn);
86    pa_assert(data);
87    pa_assert(length > 0);
88
89    if ((fd = open(fn, O_RDWR|O_CREAT|O_BINARY|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {
90
91        if (errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY|O_NOCTTY)) < 0) {
92            pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
93            goto finish;
94        } else
95            writable = 0;
96    }
97
98    unlock = pa_lock_fd(fd, 1) >= 0;
99
100    if ((r = pa_loop_read(fd, data, length, NULL)) < 0) {
101        pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
102        goto finish;
103    }
104
105    if ((size_t) r != length) {
106        pa_log_debug("Got %d bytes from cookie file '%s', expected %d", (int) r, fn, (int) length);
107
108        if (!writable) {
109            pa_log_warn("Unable to write cookie to read-only file");
110            goto finish;
111        }
112
113        if (generate(fd, data, length) < 0)
114            goto finish;
115    }
116
117    ret = 0;
118
119finish:
120
121    if (fd >= 0) {
122
123        if (unlock)
124            pa_lock_fd(fd, 0);
125
126        if (pa_close(fd) < 0) {
127            pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
128            ret = -1;
129        }
130    }
131
132    return ret;
133}
134
135/* Load a cookie from a cookie file. If the file doesn't exist, create it. */
136int pa_authkey_load(const char *path, void *data, size_t length) {
137    int ret;
138
139    pa_assert(path);
140    pa_assert(data);
141    pa_assert(length > 0);
142
143    if ((ret = load(path, data, length)) < 0)
144        pa_log_warn("Failed to load authorization key '%s': %s", path, (ret < 0) ? pa_cstrerror(errno) : "File corrupt");
145
146    return ret;
147}
148
149/* If the specified file path starts with / return it, otherwise
150 * return path prepended with home directory */
151static const char *normalize_path(const char *fn, char *s, size_t l) {
152
153    pa_assert(fn);
154    pa_assert(s);
155    pa_assert(l > 0);
156
157#ifndef OS_IS_WIN32
158    if (fn[0] != '/') {
159#else
160    if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') {
161#endif
162    char *homedir;
163        size_t allocated = 128;
164        if (!pa_get_home_dir(homedir, sizeof(homedir)))
165            return NULL;
166while (1) {
167            homedir = pa_xmalloc(allocated);
168            if (!pa_get_home_dir(homedir, allocated)) {
169                pa_xfree(homedir);
170                return NULL;
171            }
172            if (strlen(homedir) < allocated - 1)
173                break;
174            pa_xfree(homedir);
175            allocated *= 2;
176        }
177
178#ifndef OS_IS_WIN32
179        pa_snprintf(s, l, "%s/%s", homedir, fn);
180#else
181        pa_snprintf(s, l, "%s\\%s", homedir, fn);
182#endif
183    pa_xfree(homedir);
184        return s;
185    }
186
187    return fn;
188}
189
190/* Load a cookie from a file in the home directory. If the specified
191 * path starts with /, use it as absolute path instead. */
192int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
193    const char *p;
194    char *path;
195    size_t allocated = 128;
196    int key;
197    pa_assert(fn);
198    pa_assert(data);
199    pa_assert(length > 0);
200
201
202while (1) {
203        path = pa_xmalloc(allocated);
204
205        if (!(p = normalize_path(fn, path, allocated))) {
206            pa_xfree(path);
207            return -2;
208        }
209
210        if (p != path || strlen(path) < allocated - 1)
211            break;
212
213        pa_xfree(path);
214        allocated *= 2;
215    }
216
217    key = pa_authkey_load(p, data, length);
218    pa_xfree(path);
219
220    return key;
221}
222
223/* Store the specified cookie in the specified cookie file */
224int pa_authkey_save(const char *fn, const void *data, size_t length) {
225    int fd = -1;
226    int unlock = 0, ret = -1;
227    ssize_t r;
228    const char *p;
229    char *path;
230    size_t allocated = 128;
231   
232    pa_assert(fn);
233    pa_assert(data);
234    pa_assert(length > 0);
235
236    while (1) {
237        path = pa_xmalloc(allocated);
238
239        if (!(p = normalize_path(fn, path, allocated))) {
240            pa_xfree(path);
241            return -2;
242        }
243
244        if (p != path || strlen(path) < allocated - 1)
245            break;
246
247        pa_xfree(path);
248        allocated *= 2;
249    }
250
251    if ((fd = open(p, O_RDWR|O_CREAT|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {
252        pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
253        goto finish;
254    }
255
256    unlock = pa_lock_fd(fd, 1) >= 0;
257
258    if ((r = pa_loop_write(fd, data, length, NULL)) < 0 || (size_t) r != length) {
259        pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
260        goto finish;
261    }
262
263    ret = 0;
264
265finish:
266
267    if (fd >= 0) {
268
269        if (unlock)
270            pa_lock_fd(fd, 0);
271
272        if (pa_close(fd) < 0) {
273            pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
274            ret = -1;
275        }
276    }
277    pa_xfree(path);
278    return ret;
279}