Merge branch 'master' of ssh://rootserver/home/lennart/git/public/pulseaudio
[platform/upstream/pulseaudio.git] / src / pulsecore / authkey.c
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 <pulse/xmalloc.h>
40 #include <pulsecore/core-error.h>
41 #include <pulsecore/core-util.h>
42 #include <pulsecore/log.h>
43 #include <pulsecore/random.h>
44 #include <pulsecore/macro.h>
45
46 #include "authkey.h"
47
48 /* Generate a new authorization key, store it in file fd and return it in *data  */
49 static 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 /* Load an euthorization cookie from file fn and store it in data. If
74  * the cookie file doesn't exist, create it */
75 static int load(const char *fn, void *data, size_t length) {
76     int fd = -1;
77     int writable = 1;
78     int unlock = 0, ret = -1;
79     ssize_t r;
80
81     pa_assert(fn);
82     pa_assert(data);
83     pa_assert(length > 0);
84
85     if ((fd = pa_open_cloexec(fn, O_RDWR|O_CREAT|O_BINARY, S_IRUSR|S_IWUSR)) < 0) {
86
87         if (errno != EACCES || (fd = open(fn, O_RDONLY|O_BINARY)) < 0) {
88             pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
89             goto finish;
90         } else
91             writable = 0;
92     }
93
94     unlock = pa_lock_fd(fd, 1) >= 0;
95
96     if ((r = pa_loop_read(fd, data, length, NULL)) < 0) {
97         pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
98         goto finish;
99     }
100
101     if ((size_t) r != length) {
102         pa_log_debug("Got %d bytes from cookie file '%s', expected %d", (int) r, fn, (int) length);
103
104         if (!writable) {
105             pa_log_warn("Unable to write cookie to read-only file");
106             goto finish;
107         }
108
109         if (generate(fd, data, length) < 0)
110             goto finish;
111     }
112
113     ret = 0;
114
115 finish:
116
117     if (fd >= 0) {
118
119         if (unlock)
120             pa_lock_fd(fd, 0);
121
122         if (pa_close(fd) < 0) {
123             pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
124             ret = -1;
125         }
126     }
127
128     return ret;
129 }
130
131 /* Load a cookie from a cookie file. If the file doesn't exist, create it. */
132 int pa_authkey_load(const char *path, void *data, size_t length) {
133     int ret;
134
135     pa_assert(path);
136     pa_assert(data);
137     pa_assert(length > 0);
138
139     if ((ret = load(path, data, length)) < 0)
140         pa_log_warn("Failed to load authorization key '%s': %s", path, (ret < 0) ? pa_cstrerror(errno) : "File corrupt");
141
142     return ret;
143 }
144
145 /* If the specified file path starts with / return it, otherwise
146  * return path prepended with home directory */
147 static char *normalize_path(const char *fn) {
148
149     pa_assert(fn);
150
151 #ifndef OS_IS_WIN32
152     if (fn[0] != '/') {
153 #else
154     if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') {
155 #endif
156         char *homedir, *s;
157
158         if (!(homedir = pa_get_home_dir_malloc()))
159             return NULL;
160
161         s = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", homedir, fn);
162         pa_xfree(homedir);
163
164         return s;
165     }
166
167     return pa_xstrdup(fn);
168 }
169
170 /* Load a cookie from a file in the home directory. If the specified
171  * path starts with /, use it as absolute path instead. */
172 int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
173     char *p;
174     int ret;
175
176     pa_assert(fn);
177     pa_assert(data);
178     pa_assert(length > 0);
179
180     if (!(p = normalize_path(fn)))
181         return -2;
182
183     ret = pa_authkey_load(p, data, length);
184     pa_xfree(p);
185
186     return ret;
187 }
188
189 /* Store the specified cookie in the specified cookie file */
190 int pa_authkey_save(const char *fn, const void *data, size_t length) {
191     int fd = -1;
192     int unlock = 0, ret = -1;
193     ssize_t r;
194     char *p;
195
196     pa_assert(fn);
197     pa_assert(data);
198     pa_assert(length > 0);
199
200     if (!(p = normalize_path(fn)))
201         return -2;
202
203     if ((fd = pa_open_cloexec(p, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
204         pa_log_warn("Failed to open cookie file '%s': %s", fn, pa_cstrerror(errno));
205         goto finish;
206     }
207
208     unlock = pa_lock_fd(fd, 1) >= 0;
209
210     if ((r = pa_loop_write(fd, data, length, NULL)) < 0 || (size_t) r != length) {
211         pa_log("Failed to read cookie file '%s': %s", fn, pa_cstrerror(errno));
212         goto finish;
213     }
214
215     ret = 0;
216
217 finish:
218
219     if (fd >= 0) {
220
221         if (unlock)
222             pa_lock_fd(fd, 0);
223
224         if (pa_close(fd) < 0) {
225             pa_log_warn("Failed to close cookie file: %s", pa_cstrerror(errno));
226             ret = -1;
227         }
228     }
229
230     pa_xfree(p);
231
232     return ret;
233 }