1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 2/* dbus-nonce.c Nonce handling functions used by nonce-tcp (internal to D-Bus implementation) 3 * 4 * Copyright (C) 2009 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.net 5 * 6 * Licensed under the Academic Free License version 2.1 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 * 22 */ 23 24#include <config.h> 25// major sections of this file are modified code from libassuan, (C) FSF 26#include "dbus-nonce.h" 27#include "dbus-internals.h" 28#include "dbus-protocol.h" 29#include "dbus-sysdeps.h" 30 31#include <stdio.h> 32 33static dbus_bool_t 34do_check_nonce (int fd, const DBusString *nonce, DBusError *error) 35{ 36 DBusString buffer; 37 DBusString p; 38 size_t nleft; 39 dbus_bool_t result; 40 int n; 41 42 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 43 44 nleft = 16; 45 46 if ( !_dbus_string_init (&buffer) 47 || !_dbus_string_init (&p) ) { 48 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 49 _dbus_string_free (&p); 50 _dbus_string_free (&buffer); 51 return FALSE; 52 } 53 54 while (nleft) 55 { 56 n = _dbus_read_socket (fd, &p, nleft); 57 if (n == -1 && _dbus_get_is_errno_eintr()) 58 ; 59 else if (n == -1 && _dbus_get_is_errno_eagain_or_ewouldblock()) 60 _dbus_sleep_milliseconds (100); 61 else if (n==-1) 62 { 63 dbus_set_error (error, DBUS_ERROR_IO_ERROR, "Could not read nonce from socket (fd=%d)", fd ); 64 _dbus_string_free (&p); 65 _dbus_string_free (&buffer); 66 return FALSE; 67 } 68 else if (!n) 69 { 70 _dbus_string_free (&p); 71 _dbus_string_free (&buffer); 72 dbus_set_error (error, DBUS_ERROR_IO_ERROR, "Could not read nonce from socket (fd=%d)", fd ); 73 return FALSE; 74 } 75 else 76 { 77 _dbus_string_append_len(&buffer, _dbus_string_get_const_data (&p), n); 78 nleft -= n; 79 } 80 } 81 82 result = _dbus_string_equal_len (&buffer, nonce, 16); 83 if (!result) 84 dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, "Nonces do not match, access denied (fd=%d)", fd ); 85 86 _dbus_string_free (&p); 87 _dbus_string_free (&buffer); 88 89 return result; 90} 91 92/** 93 * reads the nonce from the nonce file and stores it in a string 94 * 95 * @param fname the file to read the nonce from 96 * @param nonce returns the nonce. Must be an initialized string, the nonce will be appended. 97 * @param error error object to report possible errors 98 * @return FALSE iff reading the nonce fails (error is set then) 99 */ 100dbus_bool_t 101_dbus_read_nonce (const DBusString *fname, DBusString *nonce, DBusError* error) 102{ 103 FILE *fp; 104 char buffer[17]; 105 size_t nread; 106 107 buffer[sizeof buffer - 1] = '\0'; 108 109 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 110 111 _dbus_verbose ("reading nonce from file: %s\n", _dbus_string_get_const_data (fname)); 112 113 114 fp = fopen (_dbus_string_get_const_data (fname), "rb"); 115 if (!fp) 116 return FALSE; 117 nread = fread (buffer, 1, sizeof buffer - 1, fp); 118 fclose (fp); 119 if (!nread) 120 { 121 dbus_set_error (error, DBUS_ERROR_FILE_NOT_FOUND, "Could not read nonce from file %s", _dbus_string_get_const_data (fname)); 122 return FALSE; 123 } 124 125 if (!_dbus_string_append_len (nonce, buffer, sizeof buffer - 1 )) 126 { 127 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 128 return FALSE; 129 } 130 return TRUE; 131} 132 133int 134_dbus_accept_with_noncefile (int listen_fd, const DBusNonceFile *noncefile) 135{ 136 int fd; 137 DBusString nonce; 138 139 _dbus_assert (noncefile != NULL); 140 if (!_dbus_string_init (&nonce)) 141 return -1; 142 //PENDING(kdab): set better errors 143 if (_dbus_read_nonce (_dbus_noncefile_get_path(noncefile), &nonce, NULL) != TRUE) 144 return -1; 145 fd = _dbus_accept (listen_fd); 146 if (_dbus_socket_is_invalid (fd)) 147 return fd; 148 if (do_check_nonce(fd, &nonce, NULL) != TRUE) { 149 _dbus_verbose ("nonce check failed. Closing socket.\n"); 150 _dbus_close_socket(fd, NULL); 151 return -1; 152 } 153 154 return fd; 155} 156 157static dbus_bool_t 158generate_and_write_nonce (const DBusString *filename, DBusError *error) 159{ 160 DBusString nonce; 161 dbus_bool_t ret; 162 163 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 164 165 if (!_dbus_string_init (&nonce)) 166 { 167 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 168 return FALSE; 169 } 170 171 if (!_dbus_generate_random_bytes (&nonce, 16)) 172 { 173 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 174 _dbus_string_free (&nonce); 175 return FALSE; 176 } 177 178 ret = _dbus_string_save_to_file (&nonce, filename, FALSE, error); 179 180 _dbus_string_free (&nonce); 181 182 return ret; 183} 184 185/** 186 * sends the nonce over a given socket. Blocks while doing so. 187 * 188 * @param fd the file descriptor to write the nonce data to (usually a socket) 189 * @param noncefile the noncefile location to read the nonce from 190 * @param error contains error details if FALSE is returned 191 * @return TRUE iff the nonce was successfully sent. Note that this does not 192 * indicate whether the server accepted the nonce. 193 */ 194dbus_bool_t 195_dbus_send_nonce (int fd, const DBusString *noncefile, DBusError *error) 196{ 197 dbus_bool_t read_result; 198 int send_result; 199 DBusString nonce; 200 201 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 202 203 if (_dbus_string_get_length (noncefile) == 0) 204 return FALSE; 205 206 if (!_dbus_string_init (&nonce)) 207 { 208 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 209 return FALSE; 210 } 211 212 read_result = _dbus_read_nonce (noncefile, &nonce, error); 213 if (!read_result) 214 { 215 _DBUS_ASSERT_ERROR_IS_SET (error); 216 _dbus_string_free (&nonce); 217 return FALSE; 218 } 219 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 220 221 send_result = _dbus_write_socket (fd, &nonce, 0, _dbus_string_get_length (&nonce)); 222 223 _dbus_string_free (&nonce); 224 225 if (send_result == -1) 226 { 227 dbus_set_error (error, 228 _dbus_error_from_system_errno (), 229 "Failed to send nonce (fd=%d): %s", 230 fd, _dbus_strerror_from_errno ()); 231 return FALSE; 232 } 233 234 return TRUE; 235} 236 237static dbus_bool_t 238do_noncefile_create (DBusNonceFile *noncefile, 239 DBusError *error, 240 dbus_bool_t use_subdir) 241{ 242 DBusString randomStr; 243 244 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 245 246 _dbus_assert (noncefile); 247 248 if (!_dbus_string_init (&randomStr)) 249 { 250 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 251 goto on_error; 252 } 253 254 if (!_dbus_generate_random_ascii (&randomStr, 8)) 255 { 256 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 257 goto on_error; 258 } 259 260 if (!_dbus_string_init (&noncefile->dir) 261 || !_dbus_string_append (&noncefile->dir, _dbus_get_tmpdir())) 262 { 263 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 264 goto on_error; 265 } 266 if (use_subdir) 267 { 268 if (!_dbus_string_append (&noncefile->dir, "/dbus_nonce-") 269 || !_dbus_string_append (&noncefile->dir, _dbus_string_get_const_data (&randomStr)) ) 270 { 271 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 272 goto on_error; 273 } 274 if (!_dbus_string_init (&noncefile->path) 275 || !_dbus_string_copy (&noncefile->dir, 0, &noncefile->path, 0) 276 || !_dbus_string_append (&noncefile->path, "/nonce")) 277 { 278 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 279 goto on_error; 280 } 281 if (!_dbus_create_directory (&noncefile->dir, error)) 282 { 283 _DBUS_ASSERT_ERROR_IS_SET (error); 284 goto on_error; 285 } 286 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 287 288 } 289 else 290 { 291 if (!_dbus_string_init (&noncefile->path) 292 || !_dbus_string_copy (&noncefile->dir, 0, &noncefile->path, 0) 293 || !_dbus_string_append (&noncefile->path, "/dbus_nonce-") 294 || !_dbus_string_append (&noncefile->path, _dbus_string_get_const_data (&randomStr))) 295 { 296 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 297 goto on_error; 298 } 299 300 } 301 302 if (!generate_and_write_nonce (&noncefile->path, error)) 303 { 304 _DBUS_ASSERT_ERROR_IS_SET (error); 305 if (use_subdir) 306 _dbus_delete_directory (&noncefile->dir, NULL); //we ignore possible errors deleting the dir and return the write error instead 307 goto on_error; 308 } 309 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 310 311 _dbus_string_free (&randomStr); 312 313 return TRUE; 314 on_error: 315 if (use_subdir) 316 _dbus_delete_directory (&noncefile->dir, NULL); 317 _dbus_string_free (&noncefile->dir); 318 _dbus_string_free (&noncefile->path); 319 _dbus_string_free (&randomStr); 320 return FALSE; 321} 322 323#ifdef DBUS_WIN 324/** 325 * creates a nonce file in a user-readable location and writes a generated nonce to it 326 * 327 * @param noncefile returns the nonce file location 328 * @param error error details if creating the nonce file fails 329 * @return TRUE iff the nonce file was successfully created 330 */ 331dbus_bool_t 332_dbus_noncefile_create (DBusNonceFile *noncefile, 333 DBusError *error) 334{ 335 return do_noncefile_create (noncefile, error, /*use_subdir=*/FALSE); 336} 337 338/** 339 * deletes the noncefile and frees the DBusNonceFile object. 340 * 341 * @param noncefile the nonce file to delete. Contents will be freed. 342 * @param error error details if the nonce file could not be deleted 343 * @return TRUE 344 */ 345dbus_bool_t 346_dbus_noncefile_delete (DBusNonceFile *noncefile, 347 DBusError *error) 348{ 349 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 350 351 _dbus_delete_file (&noncefile->path, error); 352 _dbus_string_free (&noncefile->dir); 353 _dbus_string_free (&noncefile->path); 354 return TRUE; 355} 356 357#else 358/** 359 * creates a nonce file in a user-readable location and writes a generated nonce to it. 360 * Initializes the noncefile object. 361 * 362 * @param noncefile returns the nonce file location 363 * @param error error details if creating the nonce file fails 364 * @return TRUE iff the nonce file was successfully created 365 */ 366dbus_bool_t 367_dbus_noncefile_create (DBusNonceFile *noncefile, 368 DBusError *error) 369{ 370 return do_noncefile_create (noncefile, error, /*use_subdir=*/TRUE); 371} 372 373/** 374 * deletes the noncefile and frees the DBusNonceFile object. 375 * 376 * @param noncefile the nonce file to delete. Contents will be freed. 377 * @param error error details if the nonce file could not be deleted 378 * @return TRUE 379 */ 380dbus_bool_t 381_dbus_noncefile_delete (DBusNonceFile *noncefile, 382 DBusError *error) 383{ 384 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 385 386 _dbus_delete_directory (&noncefile->dir, error); 387 _dbus_string_free (&noncefile->dir); 388 _dbus_string_free (&noncefile->path); 389 return TRUE; 390} 391#endif 392 393 394/** 395 * returns the absolute file path of the nonce file 396 * 397 * @param noncefile an initialized noncefile object 398 * @return the absolute path of the nonce file 399 */ 400const DBusString* 401_dbus_noncefile_get_path (const DBusNonceFile *noncefile) 402{ 403 _dbus_assert (noncefile); 404 return &noncefile->path; 405} 406 407/** 408 * reads data from a file descriptor and checks if the received data matches 409 * the data in the given noncefile. 410 * 411 * @param fd the file descriptor to read the nonce from 412 * @param noncefile the nonce file to check the received data against 413 * @param error error details on fail 414 * @return TRUE iff a nonce could be successfully read from the file descriptor 415 * and matches the nonce from the given nonce file 416 */ 417dbus_bool_t 418_dbus_noncefile_check_nonce (int fd, 419 const DBusNonceFile *noncefile, 420 DBusError* error) 421{ 422 return do_check_nonce (fd, _dbus_noncefile_get_path (noncefile), error); 423} 424 425 426/** @} end of nonce */ 427