• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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