dbus-nonce.c 15.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* dbus-nonce.c  Nonce handling functions used by nonce-tcp (internal to D-Bus implementation)
 *
 * Copyright (C) 2009 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
 *
 * Licensed under the Academic Free License version 2.1
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

24
#include <config.h>
25 26 27 28 29 30 31 32
// major sections of this file are modified code from libassuan, (C) FSF
#include "dbus-nonce.h"
#include "dbus-internals.h"
#include "dbus-protocol.h"
#include "dbus-sysdeps.h"

#include <stdio.h>

33 34 35 36 37 38
struct DBusNonceFile
{
  DBusString path;
  DBusString dir;
};

39
static dbus_bool_t
40
do_check_nonce (DBusSocket fd, const DBusString *nonce, DBusError *error)
41 42 43 44 45 46 47
{
  DBusString buffer;
  DBusString p;
  size_t nleft;
  dbus_bool_t result;
  int n;

48 49
  _DBUS_ASSERT_ERROR_IS_CLEAR (error);

50 51
  nleft = 16;

52 53 54 55 56 57 58 59 60 61 62 63 64 65
  /* This is a trick to make it safe to call _dbus_string_free on these
   * strings during error unwinding, even if allocating memory for them
   * fails. A constant DBusString is considered to be valid to "free",
   * even though there is nothing to free (of course the free operation
   * is trivial, because it does not own its own buffer); but
   * unlike a mutable DBusString, initializing a constant DBusString
   * cannot fail.
   *
   * We must successfully re-initialize the strings to be mutable before
   * writing to them, of course.
   */
  _dbus_string_init_const (&buffer, "");
  _dbus_string_init_const (&p, "");

66 67 68 69 70
  if (   !_dbus_string_init (&buffer)
      || !_dbus_string_init (&p) ) {
        dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
        _dbus_string_free (&p);
        _dbus_string_free (&buffer);
71
        return FALSE;
72 73
      }

74 75
  while (nleft)
    {
76 77
      int saved_errno;

78
      n = _dbus_read_socket (fd, &p, nleft);
79 80 81
      saved_errno = _dbus_save_socket_errno ();

      if (n == -1 && _dbus_get_is_errno_eintr (saved_errno))
82
        ;
83
      else if (n == -1 && _dbus_get_is_errno_eagain_or_ewouldblock (saved_errno))
84 85 86
        _dbus_sleep_milliseconds (100);
      else if (n==-1)
        {
87
          dbus_set_error (error, DBUS_ERROR_IO_ERROR, "Could not read nonce from socket (fd=%" DBUS_SOCKET_FORMAT ")", _dbus_socket_printable (fd));
88 89 90 91 92 93
          _dbus_string_free (&p);
          _dbus_string_free (&buffer);
          return FALSE;
        }
      else if (!n)
        {
94 95
          _dbus_string_free (&p);
          _dbus_string_free (&buffer);
96
          dbus_set_error (error, DBUS_ERROR_IO_ERROR, "Could not read nonce from socket (fd=%" DBUS_SOCKET_FORMAT ")", _dbus_socket_printable (fd));
97 98 99 100
          return FALSE;
        }
      else
        {
101 102 103 104 105 106 107
          if (!_dbus_string_append_len (&buffer, _dbus_string_get_const_data (&p), n))
            {
              dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
              _dbus_string_free (&p);
              _dbus_string_free (&buffer);
              return FALSE;
            }
108 109 110 111 112 113
          nleft -= n;
        }
    }

  result =  _dbus_string_equal_len (&buffer, nonce, 16);
  if (!result)
114
    dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, "Nonces do not match, access denied (fd=%" DBUS_SOCKET_FORMAT ")", _dbus_socket_printable (fd));
115 116 117 118 119 120 121

  _dbus_string_free (&p);
  _dbus_string_free (&buffer);

  return result;
}

122 123 124 125 126 127 128 129
/**
 * reads the nonce from the nonce file and stores it in a string
 *
 * @param fname the file to read the nonce from
 * @param nonce returns the nonce. Must be an initialized string, the nonce will be appended.
 * @param error error object to report possible errors
 * @return FALSE iff reading the nonce fails (error is set then)
 */
130
dbus_bool_t
131
_dbus_read_nonce (const DBusString *fname, DBusString *nonce, DBusError* error)
132 133 134 135
{
  FILE *fp;
  char buffer[17];
  size_t nread;
136

137 138
  buffer[sizeof buffer - 1] = '\0';

139 140
  _DBUS_ASSERT_ERROR_IS_CLEAR (error);

141 142 143 144 145
  _dbus_verbose ("reading nonce from file: %s\n", _dbus_string_get_const_data (fname));


  fp = fopen (_dbus_string_get_const_data (fname), "rb");
  if (!fp)
146 147 148 149 150 151 152 153 154
    {
      dbus_set_error (error,
		      _dbus_error_from_system_errno (),
		      "Failed to open %s for read: %s",
		      _dbus_string_get_const_data (fname),
		      _dbus_strerror_from_errno ());
      return FALSE;
    }

155 156 157 158
  nread = fread (buffer, 1, sizeof buffer - 1, fp);
  fclose (fp);
  if (!nread)
    {
159
      dbus_set_error (error, DBUS_ERROR_FILE_NOT_FOUND, "Could not read nonce from file %s", _dbus_string_get_const_data (fname));
160 161 162 163 164
      return FALSE;
    }

  if (!_dbus_string_append_len (nonce, buffer, sizeof buffer - 1 ))
    {
165
      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
166 167 168 169 170
      return FALSE;
    }
  return TRUE;
}

171 172
DBusSocket
_dbus_accept_with_noncefile (DBusSocket listen_fd, const DBusNonceFile *noncefile)
173
{
174
  DBusSocket fd = _dbus_socket_get_invalid ();
175 176 177
  DBusString nonce;

  _dbus_assert (noncefile != NULL);
178 179 180 181 182

  /* Make it valid to "free" this even if _dbus_string_init() runs
   * out of memory: see comment in do_check_nonce() */
  _dbus_string_init_const (&nonce, "");

183
  if (!_dbus_string_init (&nonce))
184 185
    goto out;

186 187
  //PENDING(kdab): set better errors
  if (_dbus_read_nonce (_dbus_noncefile_get_path(noncefile), &nonce, NULL) != TRUE)
188 189
    goto out;

190
  fd = _dbus_accept (listen_fd);
191

192
  if (!_dbus_socket_is_valid (fd))
193 194
    goto out;

195
  if (do_check_nonce(fd, &nonce, NULL) != TRUE) {
196 197
    _dbus_verbose ("nonce check failed. Closing socket.\n");
    _dbus_close_socket(fd, NULL);
198 199
    _dbus_socket_invalidate (&fd);
    goto out;
200 201
  }

202 203
out:
  _dbus_string_free (&nonce);
204 205 206
  return fd;
}

207 208
static dbus_bool_t
generate_and_write_nonce (const DBusString *filename, DBusError *error)
209 210
{
  DBusString nonce;
Frank Osterfeld's avatar
Frank Osterfeld committed
211 212 213
  dbus_bool_t ret;

  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
214

215 216 217 218 219
  if (!_dbus_string_init (&nonce))
    {
      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
      return FALSE;
    }
220

221
  if (!_dbus_generate_random_bytes (&nonce, 16, error))
Frank Osterfeld's avatar
Frank Osterfeld committed
222 223 224 225
    {
      _dbus_string_free (&nonce);
      return FALSE;
    }
226

Colin Walters's avatar
Colin Walters committed
227
  ret = _dbus_string_save_to_file (&nonce, filename, FALSE, error);
228 229 230 231 232 233

  _dbus_string_free (&nonce);

  return ret;
}

234 235 236 237 238 239 240 241 242
/**
 * sends the nonce over a given socket. Blocks while doing so.
 *
 * @param fd the file descriptor to write the nonce data to (usually a socket)
 * @param noncefile the noncefile location to read the nonce from
 * @param error contains error details if FALSE is returned
 * @return TRUE iff the nonce was successfully sent. Note that this does not
 * indicate whether the server accepted the nonce.
 */
243
dbus_bool_t
244 245 246
_dbus_send_nonce (DBusSocket        fd,
                  const DBusString *noncefile,
                  DBusError        *error)
247 248 249 250 251 252 253 254 255 256
{
  dbus_bool_t read_result;
  int send_result;
  DBusString nonce;

  _DBUS_ASSERT_ERROR_IS_CLEAR (error);

  if (_dbus_string_get_length (noncefile) == 0)
    return FALSE;

257
  if (!_dbus_string_init (&nonce))
258 259 260
    {
      dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
      return FALSE;
261
    }
262

263
  read_result = _dbus_read_nonce (noncefile, &nonce, error);
264 265
  if (!read_result)
    {
266
      _DBUS_ASSERT_ERROR_IS_SET (error);
267 268 269
      _dbus_string_free (&nonce);
      return FALSE;
    }
270
  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
271 272 273 274 275 276

  send_result = _dbus_write_socket (fd, &nonce, 0, _dbus_string_get_length (&nonce));

  _dbus_string_free (&nonce);

  if (send_result == -1)
277 278 279
    {
      dbus_set_error (error,
                      _dbus_error_from_system_errno (),
280 281 282
                      "Failed to send nonce (fd=%" DBUS_SOCKET_FORMAT "): %s",
                      _dbus_socket_printable (fd),
                      _dbus_strerror_from_errno ());
283 284
      return FALSE;
    }
285 286 287 288

  return TRUE;
}

289
static dbus_bool_t
290
do_noncefile_create (DBusNonceFile **noncefile_out,
291 292 293
                     DBusError *error,
                     dbus_bool_t use_subdir)
{
294
    DBusNonceFile *noncefile = NULL;
295
    DBusString randomStr;
296
    const char *tmp;
297 298 299

    _DBUS_ASSERT_ERROR_IS_CLEAR (error);

300 301 302 303
    _dbus_assert (noncefile_out != NULL);
    _dbus_assert (*noncefile_out == NULL);

    noncefile = dbus_new0 (DBusNonceFile, 1);
304 305 306 307 308
    if (noncefile == NULL)
      {
        dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
        return FALSE;
      }
309

310 311 312 313 314 315
    /* Make it valid to "free" these even if _dbus_string_init() runs
     * out of memory: see comment in do_check_nonce() */
    _dbus_string_init_const (&randomStr, "");
    _dbus_string_init_const (&noncefile->dir, "");
    _dbus_string_init_const (&noncefile->path, "");

316 317 318 319 320 321
    if (!_dbus_string_init (&randomStr))
      {
        dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
        goto on_error;
      }

322
    if (!_dbus_generate_random_ascii (&randomStr, 8, error))
323 324 325 326
      {
        goto on_error;
      }

327 328
    tmp = _dbus_get_tmpdir ();

329
    if (!_dbus_string_init (&noncefile->dir)
330 331
        || tmp == NULL
        || !_dbus_string_append (&noncefile->dir, tmp))
332 333 334 335 336 337
      {
        dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
        goto on_error;
      }
    if (use_subdir)
      {
338
        if (!_dbus_string_append (&noncefile->dir, "/dbus_nonce-")
339 340 341 342 343 344 345
            || !_dbus_string_append (&noncefile->dir, _dbus_string_get_const_data (&randomStr)) )
          {
            dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
            goto on_error;
          }
        if (!_dbus_string_init (&noncefile->path)
            || !_dbus_string_copy (&noncefile->dir, 0, &noncefile->path, 0)
346
            || !_dbus_string_append (&noncefile->path, "/nonce"))
347 348 349 350 351 352
          {
            dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
            goto on_error;
          }
        if (!_dbus_create_directory (&noncefile->dir, error))
          {
353
            _DBUS_ASSERT_ERROR_IS_SET (error);
354 355
            goto on_error;
          }
356
        _DBUS_ASSERT_ERROR_IS_CLEAR (error);
357 358 359 360 361 362

      }
    else
      {
        if (!_dbus_string_init (&noncefile->path)
            || !_dbus_string_copy (&noncefile->dir, 0, &noncefile->path, 0)
363
            || !_dbus_string_append (&noncefile->path, "/dbus_nonce-")
364 365 366 367 368 369 370 371 372 373
            || !_dbus_string_append (&noncefile->path, _dbus_string_get_const_data (&randomStr)))
          {
            dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
            goto on_error;
          }

      }

    if (!generate_and_write_nonce (&noncefile->path, error))
      {
374
        _DBUS_ASSERT_ERROR_IS_SET (error);
375 376 377 378
        if (use_subdir)
          _dbus_delete_directory (&noncefile->dir, NULL); //we ignore possible errors deleting the dir and return the write error instead
        goto on_error;
      }
379
    _DBUS_ASSERT_ERROR_IS_CLEAR (error);
380

381
    *noncefile_out = noncefile;
382 383 384 385
    _dbus_string_free (&randomStr);

    return TRUE;
  on_error:
386
    if (use_subdir && _dbus_string_get_length (&noncefile->dir) != 0)
387 388 389
      _dbus_delete_directory (&noncefile->dir, NULL);
    _dbus_string_free (&noncefile->dir);
    _dbus_string_free (&noncefile->path);
390
    dbus_free (noncefile);
391 392 393 394 395
    _dbus_string_free (&randomStr);
    return FALSE;
}

#ifdef DBUS_WIN
396 397 398
/**
 * creates a nonce file in a user-readable location and writes a generated nonce to it
 *
399
 * @param noncefile_out returns the nonce file location
400 401 402
 * @param error error details if creating the nonce file fails
 * @return TRUE iff the nonce file was successfully created
 */
403
dbus_bool_t
404
_dbus_noncefile_create (DBusNonceFile **noncefile_out,
405 406
                        DBusError *error)
{
407
    return do_noncefile_create (noncefile_out, error, /*use_subdir=*/FALSE);
408 409
}

410 411 412
/**
 * deletes the noncefile and frees the DBusNonceFile object.
 *
413 414 415 416
 * If noncefile_location points to #NULL, nothing is freed or deleted,
 * similar to dbus_error_free().
 *
 * @param noncefile_location the nonce file to delete. Contents will be freed and cleared to #NULL.
417 418 419
 * @param error error details if the nonce file could not be deleted
 * @return TRUE
 */
420
dbus_bool_t
421
_dbus_noncefile_delete (DBusNonceFile **noncefile_location,
422 423
                        DBusError *error)
{
424 425
    DBusNonceFile *noncefile;

426
    _DBUS_ASSERT_ERROR_IS_CLEAR (error);
427 428 429 430 431 432 433 434 435 436
    _dbus_assert (noncefile_location != NULL);

    noncefile = *noncefile_location;
    *noncefile_location = NULL;

    if (noncefile == NULL)
      {
        /* Nothing to do */
        return TRUE;
      }
437 438 439 440

    _dbus_delete_file (&noncefile->path, error);
    _dbus_string_free (&noncefile->dir);
    _dbus_string_free (&noncefile->path);
441
    dbus_free (noncefile);
442
    return TRUE;
443 444 445
}

#else
446 447 448 449
/**
 * creates a nonce file in a user-readable location and writes a generated nonce to it.
 * Initializes the noncefile object.
 *
450
 * @param noncefile_out returns the nonce file location
451 452 453
 * @param error error details if creating the nonce file fails
 * @return TRUE iff the nonce file was successfully created
 */
454
dbus_bool_t
455
_dbus_noncefile_create (DBusNonceFile **noncefile_out,
456 457
                        DBusError *error)
{
458
    return do_noncefile_create (noncefile_out, error, /*use_subdir=*/TRUE);
459 460
}

461 462 463
/**
 * deletes the noncefile and frees the DBusNonceFile object.
 *
464 465 466 467
 * If noncefile_location points to #NULL, nothing is freed or deleted,
 * similar to dbus_error_free().
 *
 * @param noncefile_location the nonce file to delete. Contents will be freed and cleared to #NULL.
468 469 470
 * @param error error details if the nonce file could not be deleted
 * @return TRUE
 */
471
dbus_bool_t
472
_dbus_noncefile_delete (DBusNonceFile **noncefile_location,
473 474
                        DBusError *error)
{
475 476
    DBusNonceFile *noncefile;

477
    _DBUS_ASSERT_ERROR_IS_CLEAR (error);
478 479 480 481 482 483 484 485 486 487
    _dbus_assert (noncefile_location != NULL);

    noncefile = *noncefile_location;
    *noncefile_location = NULL;

    if (noncefile == NULL)
      {
        /* Nothing to do */
        return TRUE;
      }
488 489 490 491

    _dbus_delete_directory (&noncefile->dir, error);
    _dbus_string_free (&noncefile->dir);
    _dbus_string_free (&noncefile->path);
492
    dbus_free (noncefile);
493
    return TRUE;
494 495 496 497
}
#endif


498 499 500 501 502 503
/**
 * returns the absolute file path of the nonce file
 *
 * @param noncefile an initialized noncefile object
 * @return the absolute path of the nonce file
 */
504 505 506 507 508 509 510
const DBusString*
_dbus_noncefile_get_path (const DBusNonceFile *noncefile)
{
    _dbus_assert (noncefile);
    return &noncefile->path;
}

511 512 513 514 515 516 517 518 519 520
/**
 * reads data from a file descriptor and checks if the received data matches
 * the data in the given noncefile.
 *
 * @param fd the file descriptor to read the nonce from
 * @param noncefile the nonce file to check the received data against
 * @param error error details on fail
 * @return TRUE iff a nonce could be successfully read from the file descriptor
 * and matches the nonce from the given nonce file
 */
521
dbus_bool_t
522
_dbus_noncefile_check_nonce (DBusSocket fd,
523 524 525 526 527 528 529
                             const DBusNonceFile *noncefile,
                             DBusError* error)
{
    return do_check_nonce (fd, _dbus_noncefile_get_path (noncefile), error);
}


530
/** @} end of nonce */