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 (at) 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 33 static dbus_bool_t 34 do_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 */ 100 dbus_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 133 int 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 _dbus_string_init (&nonce); 141 //PENDING(kdab): set better errors 142 if (_dbus_read_nonce (_dbus_noncefile_get_path(noncefile), &nonce, NULL) != TRUE) 143 return -1; 144 fd = _dbus_accept (listen_fd); 145 if (_dbus_socket_is_invalid (fd)) 146 return fd; 147 if (do_check_nonce(fd, &nonce, NULL) != TRUE) { 148 _dbus_verbose ("nonce check failed. Closing socket.\n"); 149 _dbus_close_socket(fd, NULL); 150 return -1; 151 } 152 153 return fd; 154 } 155 156 static dbus_bool_t 157 generate_and_write_nonce (const DBusString *filename, DBusError *error) 158 { 159 DBusString nonce; 160 dbus_bool_t ret; 161 162 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 163 164 _dbus_string_init (&nonce); 165 166 if (!_dbus_generate_random_bytes (&nonce, 16)) 167 { 168 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 169 _dbus_string_free (&nonce); 170 return FALSE; 171 } 172 173 ret = _dbus_string_save_to_file (&nonce, filename, FALSE, error); 174 175 _dbus_string_free (&nonce); 176 177 return ret; 178 } 179 180 /** 181 * sends the nonce over a given socket. Blocks while doing so. 182 * 183 * @param fd the file descriptor to write the nonce data to (usually a socket) 184 * @param noncefile the noncefile location to read the nonce from 185 * @param error contains error details if FALSE is returned 186 * @return TRUE iff the nonce was successfully sent. Note that this does not 187 * indicate whether the server accepted the nonce. 188 */ 189 dbus_bool_t 190 _dbus_send_nonce (int fd, const DBusString *noncefile, DBusError *error) 191 { 192 dbus_bool_t read_result; 193 int send_result; 194 size_t sendLen; 195 DBusString nonce; 196 197 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 198 199 if (_dbus_string_get_length (noncefile) == 0) 200 return FALSE; 201 202 if (!_dbus_string_init (&nonce)) 203 { 204 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 205 return FALSE; 206 } 207 208 read_result = _dbus_read_nonce (noncefile, &nonce, error); 209 if (!read_result) 210 { 211 _DBUS_ASSERT_ERROR_IS_SET (error); 212 _dbus_string_free (&nonce); 213 return FALSE; 214 } 215 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 216 217 send_result = _dbus_write_socket (fd, &nonce, 0, _dbus_string_get_length (&nonce)); 218 219 _dbus_string_free (&nonce); 220 221 if (send_result == -1) 222 { 223 dbus_set_error (error, 224 _dbus_error_from_system_errno (), 225 "Failed to send nonce (fd=%d): %s", 226 fd, _dbus_strerror_from_errno ()); 227 return FALSE; 228 } 229 230 return TRUE; 231 } 232 233 static dbus_bool_t 234 do_noncefile_create (DBusNonceFile *noncefile, 235 DBusError *error, 236 dbus_bool_t use_subdir) 237 { 238 dbus_bool_t ret; 239 DBusString randomStr; 240 241 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 242 243 _dbus_assert (noncefile); 244 245 if (!_dbus_string_init (&randomStr)) 246 { 247 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 248 goto on_error; 249 } 250 251 if (!_dbus_generate_random_ascii (&randomStr, 8)) 252 { 253 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 254 goto on_error; 255 } 256 257 if (!_dbus_string_init (&noncefile->dir) 258 || !_dbus_string_append (&noncefile->dir, _dbus_get_tmpdir())) 259 { 260 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 261 goto on_error; 262 } 263 if (use_subdir) 264 { 265 if (!_dbus_string_append (&noncefile->dir, "/dbus_nonce-") 266 || !_dbus_string_append (&noncefile->dir, _dbus_string_get_const_data (&randomStr)) ) 267 { 268 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 269 goto on_error; 270 } 271 if (!_dbus_string_init (&noncefile->path) 272 || !_dbus_string_copy (&noncefile->dir, 0, &noncefile->path, 0) 273 || !_dbus_string_append (&noncefile->dir, "/nonce")) 274 { 275 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 276 goto on_error; 277 } 278 if (!_dbus_create_directory (&noncefile->dir, error)) 279 { 280 _DBUS_ASSERT_ERROR_IS_SET (error); 281 goto on_error; 282 } 283 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 284 285 } 286 else 287 { 288 if (!_dbus_string_init (&noncefile->path) 289 || !_dbus_string_copy (&noncefile->dir, 0, &noncefile->path, 0) 290 || !_dbus_string_append (&noncefile->path, "/dbus_nonce-") 291 || !_dbus_string_append (&noncefile->path, _dbus_string_get_const_data (&randomStr))) 292 { 293 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 294 goto on_error; 295 } 296 297 } 298 299 if (!generate_and_write_nonce (&noncefile->path, error)) 300 { 301 _DBUS_ASSERT_ERROR_IS_SET (error); 302 if (use_subdir) 303 _dbus_delete_directory (&noncefile->dir, NULL); //we ignore possible errors deleting the dir and return the write error instead 304 goto on_error; 305 } 306 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 307 308 _dbus_string_free (&randomStr); 309 310 return TRUE; 311 on_error: 312 if (use_subdir) 313 _dbus_delete_directory (&noncefile->dir, NULL); 314 _dbus_string_free (&noncefile->dir); 315 _dbus_string_free (&noncefile->path); 316 _dbus_string_free (&randomStr); 317 return FALSE; 318 } 319 320 #ifdef DBUS_WIN 321 /** 322 * creates a nonce file in a user-readable location and writes a generated nonce to it 323 * 324 * @param noncefile returns the nonce file location 325 * @param error error details if creating the nonce file fails 326 * @return TRUE iff the nonce file was successfully created 327 */ 328 dbus_bool_t 329 _dbus_noncefile_create (DBusNonceFile *noncefile, 330 DBusError *error) 331 { 332 return do_noncefile_create (noncefile, error, /*use_subdir=*/FALSE); 333 } 334 335 /** 336 * deletes the noncefile and frees the DBusNonceFile object. 337 * 338 * @param noncefile the nonce file to delete. Contents will be freed. 339 * @param error error details if the nonce file could not be deleted 340 * @return TRUE 341 */ 342 dbus_bool_t 343 _dbus_noncefile_delete (DBusNonceFile *noncefile, 344 DBusError *error) 345 { 346 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 347 348 _dbus_delete_file (&noncefile->path, error); 349 _dbus_string_free (&noncefile->dir); 350 _dbus_string_free (&noncefile->path); 351 return TRUE; 352 } 353 354 #else 355 /** 356 * creates a nonce file in a user-readable location and writes a generated nonce to it. 357 * Initializes the noncefile object. 358 * 359 * @param noncefile returns the nonce file location 360 * @param error error details if creating the nonce file fails 361 * @return TRUE iff the nonce file was successfully created 362 */ 363 dbus_bool_t 364 _dbus_noncefile_create (DBusNonceFile *noncefile, 365 DBusError *error) 366 { 367 return do_noncefile_create (noncefile, error, /*use_subdir=*/TRUE); 368 } 369 370 /** 371 * deletes the noncefile and frees the DBusNonceFile object. 372 * 373 * @param noncefile the nonce file to delete. Contents will be freed. 374 * @param error error details if the nonce file could not be deleted 375 * @return TRUE 376 */ 377 dbus_bool_t 378 _dbus_noncefile_delete (DBusNonceFile *noncefile, 379 DBusError *error) 380 { 381 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 382 383 _dbus_delete_directory (&noncefile->dir, error); 384 _dbus_string_free (&noncefile->dir); 385 _dbus_string_free (&noncefile->path); 386 return TRUE; 387 } 388 #endif 389 390 391 /** 392 * returns the absolute file path of the nonce file 393 * 394 * @param noncefile an initialized noncefile object 395 * @return the absolute path of the nonce file 396 */ 397 const DBusString* 398 _dbus_noncefile_get_path (const DBusNonceFile *noncefile) 399 { 400 _dbus_assert (noncefile); 401 return &noncefile->path; 402 } 403 404 /** 405 * reads data from a file descriptor and checks if the received data matches 406 * the data in the given noncefile. 407 * 408 * @param fd the file descriptor to read the nonce from 409 * @param noncefile the nonce file to check the received data against 410 * @param error error details on fail 411 * @return TRUE iff a nonce could be successfully read from the file descriptor 412 * and matches the nonce from the given nonce file 413 */ 414 dbus_bool_t 415 _dbus_noncefile_check_nonce (int fd, 416 const DBusNonceFile *noncefile, 417 DBusError* error) 418 { 419 return do_check_nonce (fd, _dbus_noncefile_get_path (noncefile), error); 420 } 421 422 423 /** @} end of nonce */ 424