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 */ 44static 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 */ 73static 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 116finish: 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. */ 134static 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 145int 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 */ 165int 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 192finish: 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