/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to plugins.d/mandos-client.c

  • Committer: Teddy Hogeborn
  • Date: 2019-02-09 23:23:26 UTC
  • Revision ID: teddy@recompile.se-20190209232326-z1z2kzpgfixz7iaj
Add support for using raw public keys in TLS (RFC 7250)

Since GnuTLS removed support for OpenPGP keys in TLS (RFC 6091), and
no other library supports it, we have to change the protocol to use
something else.  We choose to use "raw public keys" (RFC 7250).  Since
we still use OpenPGP keys to decrypt the secret password, this means
that each client will have two keys: One OpenPGP key and one TLS
public/private key, and the key ID of the latter key is used to
identify clients instead of the fingerprint of the OpenPGP key.

Note that this code is still compatible with GnuTLS before version
3.6.0 (when OpenPGP key support was removed).  This commit merely adds
support for using raw pulic keys instead with GnuTLS 3.6.6. or later.

* DBUS-API (Signals/ClientNotFound): Change name of first parameter
                                     from "Fingerprint" to "KeyID".
  (Mandos Client Interface/Properties/KeyID): New.
* INSTALL: Document conflict with GnuTLS 3.6.0 (which removed OpenPGP
           key support) up until 3.6.6, when support for raw public
           keys was added.  Also document new dependency of client on
           "gnutls-bin" package (for certtool).
* Makefile (run-client): Depend on TLS key files, and also pass them
                         as arguments to client.
  (keydir/tls-privkey.pem, keydir/tls-pubkey.pem): New.
  (confdir/clients.conf): Add dependency on TLS public key.
  (purge-client): Add removal of TLS key files.
* clients.conf ([foo]/key_id, [bar]/key_id): New.
* debian/control (Source: mandos/Build-Depends): Also allow
                                                 libgnutls30 (>= 3.6.6)
  (Package: mandos/Depends): - '' -
  (Package: mandos/Description): Alter description to match new
                                 design.
  (Package: mandos-client/Description): - '' -
  (Package: mandos-client/Depends): Move "gnutls-bin | openssl" to
                                    here from "Recommends".
* debian/mandos-client.README.Debian: Add --tls-privkey and
                                      --tls-pubkey options to test
                                      command.
* debian/mandos-client.postinst (create_key): Renamed to "create_keys"
                                             (all callers changed),
                                             and also create TLS key.
* debian/mandos-client.postrm (purge): Also remove TLS key files.
* intro.xml (DESCRIPTION): Describe new dual-key design.
* mandos (GnuTLS): Define different functions depending on whether
                   support for raw public keys is detected.
  (Client.key_id): New attribute.
  (ClientDBus.KeyID_dbus_property): New method.
  (ProxyClient.__init__): Take new "key_id" parameter.
  (ClientHandler.handle): Use key IDs when using raw public keys and
                          use fingerprints when using OpenPGP keys.
  (ClientHandler.peer_certificate): Also handle raw public keys.
  (ClientHandler.key_id): New.
  (MandosServer.handle_ipc): Pass key ID over the pipe IPC.  Also
                             check for key ID matches when looking up
                             clients.
  (main): Default GnuTLS priority string depends on whether we are
          using raw public keys or not.  When unpickling clients, set
          key_id if not set in the pickle.
  (main/MandosDBusService.ClientNotFound): Change name of first
                                           parameter from
                                           "Fingerprint" to "KeyID".
* mandos-clients.conf.xml (OPTIONS): Document new "key_id" option.
  (OPTIONS/secret): Mention new key ID matchning.
  (EXPANSION/RUNTIME EXPANSION): Add new "key_id" option.
  (EXAMPLE): - '' -
* mandos-ctl (tablewords, main/keywords): Add new "KeyID" property.
* mandos-keygen: Create TLS key files.  New "--tls-keytype" (-T)
                 option.  Alter help text to be more clear about key
                 types.  When in password mode, also output "key_id"
                 option.
* mandos-keygen.xml (SYNOPSIS): Add new "--tls-keytype" (-T) option.
  (DESCRIPTION): Alter to match new dual-key design.
  (OVERVIEW): - '' -
  (FILES): Add TLS key files.
* mandos-options.xml (priority): Document new default priority string
                                 when using raw public keys.
* mandos.xml (NETWORK PROTOCOL): Describe new protocol using key ID.
  (BUGS): Remove issue about checking expire times of OpenPGP keys,
          since TLS public keys do not have expiration times.
  (SECURITY/CLIENT): Alter description to match new design.
  (SEE ALSO/GnuTLS): - '' -
  (SEE ALSO): Add reference to RFC 7250, and alter description of when
              RFC 6091 is used.
* overview.xml: Alter text to match new design.
* plugin-runner.xml (EXAMPLE): Add --tls-pubkey and --tls-privkey
                               options to mandos-client options.
* plugins.d/mandos-client.c: Use raw public keys when compiling with
                             supporting GnuTLS versions. Add new
                             "--tls-pubkey" and "--tls-privkey"
                             options (which do nothing if GnuTLS
                             library does not support raw public
                             keys).  Alter text throughout to reflect
                             new design.  Only generate new DH
                             parameters (based on size of OpenPGP key)
                             when using OpenPGP in TLS.  Default
                             GnuTLS priority string depends on whether
                             we are using raw public keys or not.
* plugins.d/mandos-client.xml (SYNOPSIS): Add new "--tls-privkey" (-t)
                                          and "--tls-pubkey" (-T)
                                          options.
  (DESCRIPTION): Describe new dual-key design.
  (OPTIONS): Document new "--tls-privkey" (-t) and "--tls-pubkey" (-T)
             options.
  (OPTIONS/--dh-bits): No longer necessarily depends on OpenPGP key
                       size.
  (FILES): Add default locations for TLS public and private key files.
  (EXAMPLE): Use new --tls-pubkey and --tls-privkey options.
  (SECURITY): Alter wording slightly to reflect new dual-key design.
  (SEE ALSO/GnuTLS): Alter description to match new design.
  (SEE ALSO): Add reference to RFC 7250, and alter description of when
              RFC 6091 is used.

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
11
 * Everything else is
12
 
 * Copyright © 2008-2015 Teddy Hogeborn
13
 
 * Copyright © 2008-2015 Björn Påhlsson
14
 
 * 
15
 
 * This program is free software: you can redistribute it and/or
16
 
 * modify it under the terms of the GNU General Public License as
17
 
 * published by the Free Software Foundation, either version 3 of the
18
 
 * License, or (at your option) any later version.
19
 
 * 
20
 
 * This program is distributed in the hope that it will be useful, but
 
12
 * Copyright © 2008-2018 Teddy Hogeborn
 
13
 * Copyright © 2008-2018 Björn Påhlsson
 
14
 * 
 
15
 * This file is part of Mandos.
 
16
 * 
 
17
 * Mandos is free software: you can redistribute it and/or modify it
 
18
 * under the terms of the GNU General Public License as published by
 
19
 * the Free Software Foundation, either version 3 of the License, or
 
20
 * (at your option) any later version.
 
21
 * 
 
22
 * Mandos is distributed in the hope that it will be useful, but
21
23
 * WITHOUT ANY WARRANTY; without even the implied warranty of
22
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23
25
 * General Public License for more details.
24
26
 * 
25
27
 * You should have received a copy of the GNU General Public License
26
 
 * along with this program.  If not, see
27
 
 * <http://www.gnu.org/licenses/>.
 
28
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
28
29
 * 
29
30
 * Contact the authors at <mandos@recompile.se>.
30
31
 */
46
47
#include <stdlib.h>             /* free(), EXIT_SUCCESS, srand(),
47
48
                                   strtof(), abort() */
48
49
#include <stdbool.h>            /* bool, false, true */
49
 
#include <string.h>             /* memset(), strcmp(), strlen(),
50
 
                                   strerror(), asprintf(), strcpy() */
 
50
#include <string.h>             /* strcmp(), strlen(), strerror(),
 
51
                                   asprintf(), strncpy(), strsignal()
 
52
                                */
51
53
#include <sys/ioctl.h>          /* ioctl */
52
54
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
53
55
                                   sockaddr_in6, PF_INET6,
57
59
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
58
60
                                   inet_pton(), connect(),
59
61
                                   getnameinfo() */
60
 
#include <fcntl.h>              /* open(), unlinkat() */
 
62
#include <fcntl.h>              /* open(), unlinkat(), AT_REMOVEDIR */
61
63
#include <dirent.h>             /* opendir(), struct dirent, readdir()
62
64
                                 */
63
65
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
64
66
                                   strtoimax() */
65
 
#include <errno.h>              /* perror(), errno,
 
67
#include <errno.h>              /* perror(), errno, EINTR, EINVAL,
 
68
                                   EAI_SYSTEM, ENETUNREACH,
 
69
                                   EHOSTUNREACH, ECONNREFUSED, EPROTO,
 
70
                                   EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
 
71
                                   ENOTEMPTY,
66
72
                                   program_invocation_short_name */
67
73
#include <time.h>               /* nanosleep(), time(), sleep() */
68
74
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
117
123
                                   gnutls_*
118
124
                                   init_gnutls_session(),
119
125
                                   GNUTLS_* */
 
126
#if GNUTLS_VERSION_NUMBER < 0x030600
120
127
#include <gnutls/openpgp.h>
121
128
                         /* gnutls_certificate_set_openpgp_key_file(),
122
129
                            GNUTLS_OPENPGP_FMT_BASE64 */
 
130
#elif GNUTLS_VERSION_NUMBER >= 0x030606
 
131
#include <gnutls/x509.h>        /* gnutls_pkcs_encrypt_flags_t,
 
132
                                 GNUTLS_PKCS_PLAIN,
 
133
                                 GNUTLS_PKCS_NULL_PASSWORD */
 
134
#endif
123
135
 
124
136
/* GPGME */
125
137
#include <gpgme.h>              /* All GPGME types, constants and
133
145
#define PATHDIR "/conf/conf.d/mandos"
134
146
#define SECKEY "seckey.txt"
135
147
#define PUBKEY "pubkey.txt"
 
148
#define TLS_PRIVKEY "tls-privkey.pem"
 
149
#define TLS_PUBKEY "tls-pubkey.pem"
136
150
#define HOOKDIR "/lib/mandos/network-hooks.d"
137
151
 
138
152
bool debug = false;
266
280
  return true;
267
281
}
268
282
 
 
283
/* Set effective uid to 0, return errno */
 
284
__attribute__((warn_unused_result))
 
285
int raise_privileges(void){
 
286
  int old_errno = errno;
 
287
  int ret = 0;
 
288
  if(seteuid(0) == -1){
 
289
    ret = errno;
 
290
  }
 
291
  errno = old_errno;
 
292
  return ret;
 
293
}
 
294
 
 
295
/* Set effective and real user ID to 0.  Return errno. */
 
296
__attribute__((warn_unused_result))
 
297
int raise_privileges_permanently(void){
 
298
  int old_errno = errno;
 
299
  int ret = raise_privileges();
 
300
  if(ret != 0){
 
301
    errno = old_errno;
 
302
    return ret;
 
303
  }
 
304
  if(setuid(0) == -1){
 
305
    ret = errno;
 
306
  }
 
307
  errno = old_errno;
 
308
  return ret;
 
309
}
 
310
 
 
311
/* Set effective user ID to unprivileged saved user ID */
 
312
__attribute__((warn_unused_result))
 
313
int lower_privileges(void){
 
314
  int old_errno = errno;
 
315
  int ret = 0;
 
316
  if(seteuid(uid) == -1){
 
317
    ret = errno;
 
318
  }
 
319
  errno = old_errno;
 
320
  return ret;
 
321
}
 
322
 
 
323
/* Lower privileges permanently */
 
324
__attribute__((warn_unused_result))
 
325
int lower_privileges_permanently(void){
 
326
  int old_errno = errno;
 
327
  int ret = 0;
 
328
  if(setuid(uid) == -1){
 
329
    ret = errno;
 
330
  }
 
331
  errno = old_errno;
 
332
  return ret;
 
333
}
 
334
 
269
335
/* 
270
336
 * Initialize GPGME.
271
337
 */
291
357
      return false;
292
358
    }
293
359
    
 
360
    /* Workaround for systems without a real-time clock; see also
 
361
       Debian bug #894495: <https://bugs.debian.org/894495> */
 
362
    do {
 
363
      {
 
364
        time_t currtime = time(NULL);
 
365
        if(currtime != (time_t)-1){
 
366
          struct tm tm;
 
367
          if(gmtime_r(&currtime, &tm) == NULL) {
 
368
            perror_plus("gmtime_r");
 
369
            break;
 
370
          }
 
371
          if(tm.tm_year != 70 or tm.tm_mon != 0){
 
372
            break;
 
373
          }
 
374
          if(debug){
 
375
            fprintf_plus(stderr, "System clock is January 1970");
 
376
          }
 
377
        } else {
 
378
          if(debug){
 
379
            fprintf_plus(stderr, "System clock is invalid");
 
380
          }
 
381
        }
 
382
      }
 
383
      struct stat keystat;
 
384
      ret = fstat(fd, &keystat);
 
385
      if(ret != 0){
 
386
        perror_plus("fstat");
 
387
        break;
 
388
      }
 
389
      ret = raise_privileges();
 
390
      if(ret != 0){
 
391
        errno = ret;
 
392
        perror_plus("Failed to raise privileges");
 
393
        break;
 
394
      }
 
395
      if(debug){
 
396
        fprintf_plus(stderr,
 
397
                     "Setting system clock to key file mtime");
 
398
      }
 
399
      time_t keytime = keystat.st_mtim.tv_sec;
 
400
      if(stime(&keytime) != 0){
 
401
        perror_plus("stime");
 
402
      }
 
403
      ret = lower_privileges();
 
404
      if(ret != 0){
 
405
        errno = ret;
 
406
        perror_plus("Failed to lower privileges");
 
407
      }
 
408
    } while(false);
 
409
 
294
410
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
295
411
    if(rc != GPG_ERR_NO_ERROR){
296
412
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
304
420
                   gpgme_strsource(rc), gpgme_strerror(rc));
305
421
      return false;
306
422
    }
 
423
    {
 
424
      gpgme_import_result_t import_result
 
425
        = gpgme_op_import_result(mc->ctx);
 
426
      if((import_result->imported < 1
 
427
          or import_result->not_imported > 0)
 
428
         and import_result->unchanged == 0){
 
429
        fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
 
430
        fprintf_plus(stderr,
 
431
                     "The total number of considered keys: %d\n",
 
432
                     import_result->considered);
 
433
        fprintf_plus(stderr,
 
434
                     "The number of keys without user ID: %d\n",
 
435
                     import_result->no_user_id);
 
436
        fprintf_plus(stderr,
 
437
                     "The total number of imported keys: %d\n",
 
438
                     import_result->imported);
 
439
        fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
 
440
                     import_result->imported_rsa);
 
441
        fprintf_plus(stderr, "The number of unchanged keys: %d\n",
 
442
                     import_result->unchanged);
 
443
        fprintf_plus(stderr, "The number of new user IDs: %d\n",
 
444
                     import_result->new_user_ids);
 
445
        fprintf_plus(stderr, "The number of new sub keys: %d\n",
 
446
                     import_result->new_sub_keys);
 
447
        fprintf_plus(stderr, "The number of new signatures: %d\n",
 
448
                     import_result->new_signatures);
 
449
        fprintf_plus(stderr, "The number of new revocations: %d\n",
 
450
                     import_result->new_revocations);
 
451
        fprintf_plus(stderr,
 
452
                     "The total number of secret keys read: %d\n",
 
453
                     import_result->secret_read);
 
454
        fprintf_plus(stderr,
 
455
                     "The number of imported secret keys: %d\n",
 
456
                     import_result->secret_imported);
 
457
        fprintf_plus(stderr,
 
458
                     "The number of unchanged secret keys: %d\n",
 
459
                     import_result->secret_unchanged);
 
460
        fprintf_plus(stderr, "The number of keys not imported: %d\n",
 
461
                     import_result->not_imported);
 
462
        for(gpgme_import_status_t import_status
 
463
              = import_result->imports;
 
464
            import_status != NULL;
 
465
            import_status = import_status->next){
 
466
          fprintf_plus(stderr, "Import status for key: %s\n",
 
467
                       import_status->fpr);
 
468
          if(import_status->result != GPG_ERR_NO_ERROR){
 
469
            fprintf_plus(stderr, "Import result: %s: %s\n",
 
470
                         gpgme_strsource(import_status->result),
 
471
                         gpgme_strerror(import_status->result));
 
472
          }
 
473
          fprintf_plus(stderr, "Key status:\n");
 
474
          fprintf_plus(stderr,
 
475
                       import_status->status & GPGME_IMPORT_NEW
 
476
                       ? "The key was new.\n"
 
477
                       : "The key was not new.\n");
 
478
          fprintf_plus(stderr,
 
479
                       import_status->status & GPGME_IMPORT_UID
 
480
                       ? "The key contained new user IDs.\n"
 
481
                       : "The key did not contain new user IDs.\n");
 
482
          fprintf_plus(stderr,
 
483
                       import_status->status & GPGME_IMPORT_SIG
 
484
                       ? "The key contained new signatures.\n"
 
485
                       : "The key did not contain new signatures.\n");
 
486
          fprintf_plus(stderr,
 
487
                       import_status->status & GPGME_IMPORT_SUBKEY
 
488
                       ? "The key contained new sub keys.\n"
 
489
                       : "The key did not contain new sub keys.\n");
 
490
          fprintf_plus(stderr,
 
491
                       import_status->status & GPGME_IMPORT_SECRET
 
492
                       ? "The key contained a secret key.\n"
 
493
                       : "The key did not contain a secret key.\n");
 
494
        }
 
495
        return false;
 
496
      }
 
497
    }
307
498
    
308
 
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
499
    ret = close(fd);
309
500
    if(ret == -1){
310
501
      perror_plus("close");
311
502
    }
350
541
  /* Create new GPGME "context" */
351
542
  rc = gpgme_new(&(mc->ctx));
352
543
  if(rc != GPG_ERR_NO_ERROR){
353
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
354
 
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
355
 
                 gpgme_strerror(rc));
 
544
    fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
 
545
                 gpgme_strsource(rc), gpgme_strerror(rc));
356
546
    return false;
357
547
  }
358
548
  
394
584
  /* Create new empty GPGME data buffer for the plaintext */
395
585
  rc = gpgme_data_new(&dh_plain);
396
586
  if(rc != GPG_ERR_NO_ERROR){
397
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
398
 
                 "bad gpgme_data_new: %s: %s\n",
 
587
    fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
399
588
                 gpgme_strsource(rc), gpgme_strerror(rc));
400
589
    gpgme_data_release(dh_crypto);
401
590
    return -1;
414
603
      if(result == NULL){
415
604
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
416
605
      } else {
417
 
        fprintf_plus(stderr, "Unsupported algorithm: %s\n",
418
 
                     result->unsupported_algorithm);
419
 
        fprintf_plus(stderr, "Wrong key usage: %u\n",
420
 
                     result->wrong_key_usage);
 
606
        if(result->unsupported_algorithm != NULL) {
 
607
          fprintf_plus(stderr, "Unsupported algorithm: %s\n",
 
608
                       result->unsupported_algorithm);
 
609
        }
 
610
        fprintf_plus(stderr, "Wrong key usage: %s\n",
 
611
                     result->wrong_key_usage ? "Yes" : "No");
421
612
        if(result->file_name != NULL){
422
613
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
423
614
        }
424
 
        gpgme_recipient_t recipient;
425
 
        recipient = result->recipients;
426
 
        while(recipient != NULL){
 
615
 
 
616
        for(gpgme_recipient_t r = result->recipients; r != NULL;
 
617
            r = r->next){
427
618
          fprintf_plus(stderr, "Public key algorithm: %s\n",
428
 
                       gpgme_pubkey_algo_name
429
 
                       (recipient->pubkey_algo));
430
 
          fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
 
619
                       gpgme_pubkey_algo_name(r->pubkey_algo));
 
620
          fprintf_plus(stderr, "Key ID: %s\n", r->keyid);
431
621
          fprintf_plus(stderr, "Secret key available: %s\n",
432
 
                       recipient->status == GPG_ERR_NO_SECKEY
433
 
                       ? "No" : "Yes");
434
 
          recipient = recipient->next;
 
622
                       r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
435
623
        }
436
624
      }
437
625
    }
513
701
  fprintf_plus(stderr, "GnuTLS: %s", string);
514
702
}
515
703
 
516
 
__attribute__((nonnull, warn_unused_result))
 
704
__attribute__((nonnull(1, 2, 4), warn_unused_result))
517
705
static int init_gnutls_global(const char *pubkeyfilename,
518
706
                              const char *seckeyfilename,
 
707
                              const char *dhparamsfilename,
519
708
                              mandos_context *mc){
520
709
  int ret;
521
 
  unsigned int uret;
522
710
  
523
711
  if(debug){
524
712
    fprintf_plus(stderr, "Initializing GnuTLS\n");
525
713
  }
526
714
  
527
 
  ret = gnutls_global_init();
528
 
  if(ret != GNUTLS_E_SUCCESS){
529
 
    fprintf_plus(stderr, "GnuTLS global_init: %s\n",
530
 
                 safer_gnutls_strerror(ret));
531
 
    return -1;
532
 
  }
533
 
  
534
715
  if(debug){
535
716
    /* "Use a log level over 10 to enable all debugging options."
536
717
     * - GnuTLS manual
544
725
  if(ret != GNUTLS_E_SUCCESS){
545
726
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
546
727
                 safer_gnutls_strerror(ret));
547
 
    gnutls_global_deinit();
548
728
    return -1;
549
729
  }
550
730
  
551
731
  if(debug){
552
 
    fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
553
 
                 " secret key %s as GnuTLS credentials\n",
 
732
    fprintf_plus(stderr, "Attempting to use public key %s and"
 
733
                 " private key %s as GnuTLS credentials\n",
554
734
                 pubkeyfilename,
555
735
                 seckeyfilename);
556
736
  }
557
737
  
 
738
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
739
  ret = gnutls_certificate_set_rawpk_key_file
 
740
    (mc->cred, pubkeyfilename, seckeyfilename,
 
741
     GNUTLS_X509_FMT_PEM,       /* format */
 
742
     NULL,                      /* pass */
 
743
     /* key_usage */
 
744
     GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
 
745
     NULL,                      /* names */
 
746
     0,                         /* names_length */
 
747
     /* privkey_flags */
 
748
     GNUTLS_PKCS_PLAIN | GNUTLS_PKCS_NULL_PASSWORD,
 
749
     0);                        /* pkcs11_flags */
 
750
#elif GNUTLS_VERSION_NUMBER < 0x030600
558
751
  ret = gnutls_certificate_set_openpgp_key_file
559
752
    (mc->cred, pubkeyfilename, seckeyfilename,
560
753
     GNUTLS_OPENPGP_FMT_BASE64);
 
754
#else
 
755
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
756
#endif
561
757
  if(ret != GNUTLS_E_SUCCESS){
562
758
    fprintf_plus(stderr,
563
 
                 "Error[%d] while reading the OpenPGP key pair ('%s',"
 
759
                 "Error[%d] while reading the key pair ('%s',"
564
760
                 " '%s')\n", ret, pubkeyfilename, seckeyfilename);
565
761
    fprintf_plus(stderr, "The GnuTLS error is: %s\n",
566
762
                 safer_gnutls_strerror(ret));
575
771
                 safer_gnutls_strerror(ret));
576
772
    goto globalfail;
577
773
  }
578
 
  if(mc->dh_bits == 0){
579
 
    /* Find out the optimal number of DH bits */
580
 
    /* Try to read the private key file */
581
 
    gnutls_datum_t buffer = { .data = NULL, .size = 0 };
582
 
    {
583
 
      int secfile = open(seckeyfilename, O_RDONLY);
584
 
      size_t buffer_capacity = 0;
 
774
  /* If a Diffie-Hellman parameters file was given, try to use it */
 
775
  if(dhparamsfilename != NULL){
 
776
    gnutls_datum_t params = { .data = NULL, .size = 0 };
 
777
    do {
 
778
      int dhpfile = open(dhparamsfilename, O_RDONLY);
 
779
      if(dhpfile == -1){
 
780
        perror_plus("open");
 
781
        dhparamsfilename = NULL;
 
782
        break;
 
783
      }
 
784
      size_t params_capacity = 0;
585
785
      while(true){
586
 
        buffer_capacity = incbuffer((char **)&buffer.data,
587
 
                                    (size_t)buffer.size,
588
 
                                    (size_t)buffer_capacity);
589
 
        if(buffer_capacity == 0){
 
786
        params_capacity = incbuffer((char **)&params.data,
 
787
                                    (size_t)params.size,
 
788
                                    (size_t)params_capacity);
 
789
        if(params_capacity == 0){
590
790
          perror_plus("incbuffer");
591
 
          free(buffer.data);
592
 
          buffer.data = NULL;
 
791
          free(params.data);
 
792
          params.data = NULL;
 
793
          dhparamsfilename = NULL;
593
794
          break;
594
795
        }
595
 
        ssize_t bytes_read = read(secfile, buffer.data + buffer.size,
 
796
        ssize_t bytes_read = read(dhpfile,
 
797
                                  params.data + params.size,
596
798
                                  BUFFER_SIZE);
597
799
        /* EOF */
598
800
        if(bytes_read == 0){
601
803
        /* check bytes_read for failure */
602
804
        if(bytes_read < 0){
603
805
          perror_plus("read");
604
 
          free(buffer.data);
605
 
          buffer.data = NULL;
606
 
          break;
607
 
        }
608
 
        buffer.size += (unsigned int)bytes_read;
609
 
      }
610
 
      close(secfile);
611
 
    }
612
 
    /* If successful, use buffer to parse private key */
613
 
    gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
614
 
    if(buffer.data != NULL){
615
 
      {
616
 
        gnutls_openpgp_privkey_t privkey = NULL;
617
 
        ret = gnutls_openpgp_privkey_init(&privkey);
618
 
        if(ret != GNUTLS_E_SUCCESS){
619
 
          fprintf_plus(stderr, "Error initializing OpenPGP key"
620
 
                       " structure: %s", safer_gnutls_strerror(ret));
621
 
          free(buffer.data);
622
 
          buffer.data = NULL;
623
 
        } else {
624
 
          ret = gnutls_openpgp_privkey_import(privkey, &buffer,
625
 
                                            GNUTLS_OPENPGP_FMT_BASE64,
626
 
                                              "", 0);
 
806
          free(params.data);
 
807
          params.data = NULL;
 
808
          dhparamsfilename = NULL;
 
809
          break;
 
810
        }
 
811
        params.size += (unsigned int)bytes_read;
 
812
      }
 
813
      ret = close(dhpfile);
 
814
      if(ret == -1){
 
815
        perror_plus("close");
 
816
      }
 
817
      if(params.data == NULL){
 
818
        dhparamsfilename = NULL;
 
819
      }
 
820
      if(dhparamsfilename == NULL){
 
821
        break;
 
822
      }
 
823
      ret = gnutls_dh_params_import_pkcs3(mc->dh_params, &params,
 
824
                                          GNUTLS_X509_FMT_PEM);
 
825
      if(ret != GNUTLS_E_SUCCESS){
 
826
        fprintf_plus(stderr, "Failed to parse DH parameters in file"
 
827
                     " \"%s\": %s\n", dhparamsfilename,
 
828
                     safer_gnutls_strerror(ret));
 
829
        dhparamsfilename = NULL;
 
830
      }
 
831
      free(params.data);
 
832
    } while(false);
 
833
  }
 
834
  if(dhparamsfilename == NULL){
 
835
    if(mc->dh_bits == 0){
 
836
#if GNUTLS_VERSION_NUMBER < 0x030600
 
837
      /* Find out the optimal number of DH bits */
 
838
      /* Try to read the private key file */
 
839
      gnutls_datum_t buffer = { .data = NULL, .size = 0 };
 
840
      do {
 
841
        int secfile = open(seckeyfilename, O_RDONLY);
 
842
        if(secfile == -1){
 
843
          perror_plus("open");
 
844
          break;
 
845
        }
 
846
        size_t buffer_capacity = 0;
 
847
        while(true){
 
848
          buffer_capacity = incbuffer((char **)&buffer.data,
 
849
                                      (size_t)buffer.size,
 
850
                                      (size_t)buffer_capacity);
 
851
          if(buffer_capacity == 0){
 
852
            perror_plus("incbuffer");
 
853
            free(buffer.data);
 
854
            buffer.data = NULL;
 
855
            break;
 
856
          }
 
857
          ssize_t bytes_read = read(secfile,
 
858
                                    buffer.data + buffer.size,
 
859
                                    BUFFER_SIZE);
 
860
          /* EOF */
 
861
          if(bytes_read == 0){
 
862
            break;
 
863
          }
 
864
          /* check bytes_read for failure */
 
865
          if(bytes_read < 0){
 
866
            perror_plus("read");
 
867
            free(buffer.data);
 
868
            buffer.data = NULL;
 
869
            break;
 
870
          }
 
871
          buffer.size += (unsigned int)bytes_read;
 
872
        }
 
873
        close(secfile);
 
874
      } while(false);
 
875
      /* If successful, use buffer to parse private key */
 
876
      gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
877
      if(buffer.data != NULL){
 
878
        {
 
879
          gnutls_openpgp_privkey_t privkey = NULL;
 
880
          ret = gnutls_openpgp_privkey_init(&privkey);
627
881
          if(ret != GNUTLS_E_SUCCESS){
628
 
            fprintf_plus(stderr, "Error importing OpenPGP key : %s",
 
882
            fprintf_plus(stderr, "Error initializing OpenPGP key"
 
883
                         " structure: %s",
629
884
                         safer_gnutls_strerror(ret));
630
 
            privkey = NULL;
 
885
            free(buffer.data);
 
886
            buffer.data = NULL;
 
887
          } else {
 
888
            ret = gnutls_openpgp_privkey_import
 
889
              (privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
 
890
            if(ret != GNUTLS_E_SUCCESS){
 
891
              fprintf_plus(stderr, "Error importing OpenPGP key : %s",
 
892
                           safer_gnutls_strerror(ret));
 
893
              privkey = NULL;
 
894
            }
 
895
            free(buffer.data);
 
896
            buffer.data = NULL;
 
897
            if(privkey != NULL){
 
898
              /* Use private key to suggest an appropriate
 
899
                 sec_param */
 
900
              sec_param = gnutls_openpgp_privkey_sec_param(privkey);
 
901
              gnutls_openpgp_privkey_deinit(privkey);
 
902
              if(debug){
 
903
                fprintf_plus(stderr, "This OpenPGP key implies using"
 
904
                             " a GnuTLS security parameter \"%s\".\n",
 
905
                             safe_string(gnutls_sec_param_get_name
 
906
                                         (sec_param)));
 
907
              }
 
908
            }
631
909
          }
632
 
          free(buffer.data);
633
 
          buffer.data = NULL;
634
 
          if(privkey != NULL){
635
 
            /* Use private key to suggest an appropriate sec_param */
636
 
            sec_param = gnutls_openpgp_privkey_sec_param(privkey);
637
 
            gnutls_openpgp_privkey_deinit(privkey);
638
 
            if(debug){
639
 
              fprintf_plus(stderr, "This OpenPGP key implies using a"
640
 
                           " GnuTLS security parameter \"%s\".\n",
641
 
                           safe_string(gnutls_sec_param_get_name
642
 
                                       (sec_param)));
643
 
            }
 
910
        }
 
911
        if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
 
912
          /* Err on the side of caution */
 
913
          sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
914
          if(debug){
 
915
            fprintf_plus(stderr, "Falling back to security parameter"
 
916
                         " \"%s\"\n",
 
917
                         safe_string(gnutls_sec_param_get_name
 
918
                                     (sec_param)));
644
919
          }
645
920
        }
646
921
      }
647
 
      if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
648
 
        /* Err on the side of caution */
649
 
        sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
922
      unsigned int uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
 
923
      if(uret != 0){
 
924
        mc->dh_bits = uret;
650
925
        if(debug){
651
 
          fprintf_plus(stderr, "Falling back to security parameter"
652
 
                       " \"%s\"\n",
 
926
          fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
 
927
                       " implies %u DH bits; using that.\n",
653
928
                       safe_string(gnutls_sec_param_get_name
654
 
                                   (sec_param)));
 
929
                                   (sec_param)),
 
930
                       mc->dh_bits);
655
931
        }
656
 
      }
657
 
    }
658
 
    uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
659
 
    if(uret != 0){
660
 
      mc->dh_bits = uret;
661
 
      if(debug){
662
 
        fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
663
 
                     " implies %u DH bits; using that.\n",
 
932
      } else {
 
933
        fprintf_plus(stderr, "Failed to get implied number of DH"
 
934
                     " bits for security parameter \"%s\"): %s\n",
664
935
                     safe_string(gnutls_sec_param_get_name
665
936
                                 (sec_param)),
 
937
                     safer_gnutls_strerror(ret));
 
938
        goto globalfail;
 
939
      }
 
940
#endif
 
941
    } else {                    /* dh_bits != 0 */
 
942
      if(debug){
 
943
        fprintf_plus(stderr, "DH bits explicitly set to %u\n",
666
944
                     mc->dh_bits);
667
945
      }
668
 
    } else {
669
 
      fprintf_plus(stderr, "Failed to get implied number of DH"
670
 
                   " bits for security parameter \"%s\"): %s\n",
671
 
                   safe_string(gnutls_sec_param_get_name(sec_param)),
672
 
                   safer_gnutls_strerror(ret));
673
 
      goto globalfail;
 
946
      ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
 
947
      if(ret != GNUTLS_E_SUCCESS){
 
948
        fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
 
949
                     " bits): %s\n", mc->dh_bits,
 
950
                     safer_gnutls_strerror(ret));
 
951
        goto globalfail;
 
952
      }
 
953
      gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
674
954
    }
675
 
  } else if(debug){
676
 
    fprintf_plus(stderr, "DH bits explicitly set to %u\n",
677
 
                 mc->dh_bits);
678
 
  }
679
 
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
680
 
  if(ret != GNUTLS_E_SUCCESS){
681
 
    fprintf_plus(stderr, "Error in GnuTLS prime generation (%u bits):"
682
 
                 " %s\n", mc->dh_bits, safer_gnutls_strerror(ret));
683
 
    goto globalfail;
684
 
  }
685
 
  
686
 
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
 
955
  }
687
956
  
688
957
  return 0;
689
958
  
690
959
 globalfail:
691
960
  
692
961
  gnutls_certificate_free_credentials(mc->cred);
693
 
  gnutls_global_deinit();
694
962
  gnutls_dh_params_deinit(mc->dh_params);
695
963
  return -1;
696
964
}
701
969
  int ret;
702
970
  /* GnuTLS session creation */
703
971
  do {
704
 
    ret = gnutls_init(session, GNUTLS_SERVER);
 
972
    ret = gnutls_init(session, (GNUTLS_SERVER
 
973
#if GNUTLS_VERSION_NUMBER >= 0x030506
 
974
                                | GNUTLS_NO_TICKETS
 
975
#endif
 
976
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
977
                                | GNUTLS_ENABLE_RAWPK
 
978
#endif
 
979
                                ));
705
980
    if(quit_now){
706
981
      return -1;
707
982
    }
748
1023
  /* ignore client certificate if any. */
749
1024
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
750
1025
  
751
 
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
752
 
  
753
1026
  return 0;
754
1027
}
755
1028
 
757
1030
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
758
1031
                      __attribute__((unused)) const char *txt){}
759
1032
 
760
 
/* Set effective uid to 0, return errno */
761
 
__attribute__((warn_unused_result))
762
 
error_t raise_privileges(void){
763
 
  error_t old_errno = errno;
764
 
  error_t ret_errno = 0;
765
 
  if(seteuid(0) == -1){
766
 
    ret_errno = errno;
767
 
  }
768
 
  errno = old_errno;
769
 
  return ret_errno;
770
 
}
771
 
 
772
 
/* Set effective and real user ID to 0.  Return errno. */
773
 
__attribute__((warn_unused_result))
774
 
error_t raise_privileges_permanently(void){
775
 
  error_t old_errno = errno;
776
 
  error_t ret_errno = raise_privileges();
777
 
  if(ret_errno != 0){
778
 
    errno = old_errno;
779
 
    return ret_errno;
780
 
  }
781
 
  if(setuid(0) == -1){
782
 
    ret_errno = errno;
783
 
  }
784
 
  errno = old_errno;
785
 
  return ret_errno;
786
 
}
787
 
 
788
 
/* Set effective user ID to unprivileged saved user ID */
789
 
__attribute__((warn_unused_result))
790
 
error_t lower_privileges(void){
791
 
  error_t old_errno = errno;
792
 
  error_t ret_errno = 0;
793
 
  if(seteuid(uid) == -1){
794
 
    ret_errno = errno;
795
 
  }
796
 
  errno = old_errno;
797
 
  return ret_errno;
798
 
}
799
 
 
800
 
/* Lower privileges permanently */
801
 
__attribute__((warn_unused_result))
802
 
error_t lower_privileges_permanently(void){
803
 
  error_t old_errno = errno;
804
 
  error_t ret_errno = 0;
805
 
  if(setuid(uid) == -1){
806
 
    ret_errno = errno;
807
 
  }
808
 
  errno = old_errno;
809
 
  return ret_errno;
810
 
}
811
 
 
812
1033
/* Helper function to add_local_route() and delete_local_route() */
813
1034
__attribute__((nonnull, warn_unused_result))
814
1035
static bool add_delete_local_route(const bool add,
868
1089
      perror_plus("dup2(devnull, STDIN_FILENO)");
869
1090
      _exit(EX_OSERR);
870
1091
    }
871
 
    ret = (int)TEMP_FAILURE_RETRY(close(devnull));
 
1092
    ret = close(devnull);
872
1093
    if(ret == -1){
873
1094
      perror_plus("close");
874
1095
      _exit(EX_OSERR);
891
1112
                                                   helper, O_RDONLY));
892
1113
    if(helper_fd == -1){
893
1114
      perror_plus("openat");
 
1115
      close(helperdir_fd);
894
1116
      _exit(EX_UNAVAILABLE);
895
1117
    }
896
 
    TEMP_FAILURE_RETRY(close(helperdir_fd));
 
1118
    close(helperdir_fd);
897
1119
#ifdef __GNUC__
898
1120
#pragma GCC diagnostic push
899
1121
#pragma GCC diagnostic ignored "-Wcast-qual"
1018
1240
    bool match = false;
1019
1241
    {
1020
1242
      char *interface = NULL;
1021
 
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
1022
 
                                 interface))){
 
1243
      while((interface = argz_next(mc->interfaces,
 
1244
                                   mc->interfaces_size,
 
1245
                                   interface))){
1023
1246
        if(if_nametoindex(interface) == (unsigned int)if_index){
1024
1247
          match = true;
1025
1248
          break;
1067
1290
    goto mandos_end;
1068
1291
  }
1069
1292
  
1070
 
  memset(&to, 0, sizeof(to));
1071
1293
  if(af == AF_INET6){
1072
 
    ((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
1073
 
    ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
 
1294
    struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
 
1295
    *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
 
1296
    ret = inet_pton(af, ip, &to6->sin6_addr);
1074
1297
  } else {                      /* IPv4 */
1075
 
    ((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
1076
 
    ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
 
1298
    struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
 
1299
    *to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
 
1300
    ret = inet_pton(af, ip, &to4->sin_addr);
1077
1301
  }
1078
1302
  if(ret < 0 ){
1079
1303
    int e = errno;
1158
1382
                    sizeof(struct sockaddr_in));
1159
1383
    }
1160
1384
    if(ret < 0){
1161
 
      if(errno == ENETUNREACH
 
1385
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1162
1386
         and if_index != AVAHI_IF_UNSPEC
1163
1387
         and connect_to == NULL
1164
1388
         and not route_added and
1177
1401
           with an explicit route added with the server's address.
1178
1402
           
1179
1403
           Avahi bug reference:
1180
 
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1404
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1181
1405
           https://bugs.debian.org/587961
1182
1406
        */
1183
1407
        if(debug){
1363
1587
                                               &decrypted_buffer, mc);
1364
1588
    if(decrypted_buffer_size >= 0){
1365
1589
      
 
1590
      clearerr(stdout);
1366
1591
      written = 0;
1367
1592
      while(written < (size_t) decrypted_buffer_size){
1368
1593
        if(quit_now){
1384
1609
        }
1385
1610
        written += (size_t)ret;
1386
1611
      }
 
1612
      ret = fflush(stdout);
 
1613
      if(ret != 0){
 
1614
        int e = errno;
 
1615
        if(debug){
 
1616
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
 
1617
                       strerror(errno));
 
1618
        }
 
1619
        errno = e;
 
1620
        goto mandos_end;
 
1621
      }
1387
1622
      retval = 0;
1388
1623
    }
1389
1624
  }
1402
1637
    free(decrypted_buffer);
1403
1638
    free(buffer);
1404
1639
    if(tcp_sd >= 0){
1405
 
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
1640
      ret = close(tcp_sd);
1406
1641
    }
1407
1642
    if(ret == -1){
1408
1643
      if(e == 0){
1420
1655
  return retval;
1421
1656
}
1422
1657
 
1423
 
__attribute__((nonnull))
1424
1658
static void resolve_callback(AvahiSServiceResolver *r,
1425
1659
                             AvahiIfIndex interface,
1426
1660
                             AvahiProtocol proto,
1563
1797
__attribute__((nonnull, warn_unused_result))
1564
1798
bool get_flags(const char *ifname, struct ifreq *ifr){
1565
1799
  int ret;
1566
 
  error_t ret_errno;
 
1800
  int old_errno;
1567
1801
  
1568
1802
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1569
1803
  if(s < 0){
1570
 
    ret_errno = errno;
 
1804
    old_errno = errno;
1571
1805
    perror_plus("socket");
1572
 
    errno = ret_errno;
 
1806
    errno = old_errno;
1573
1807
    return false;
1574
1808
  }
1575
 
  strcpy(ifr->ifr_name, ifname);
 
1809
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
 
1810
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1576
1811
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1577
1812
  if(ret == -1){
1578
1813
    if(debug){
1579
 
      ret_errno = errno;
 
1814
      old_errno = errno;
1580
1815
      perror_plus("ioctl SIOCGIFFLAGS");
1581
 
      errno = ret_errno;
 
1816
      errno = old_errno;
 
1817
    }
 
1818
    if((close(s) == -1) and debug){
 
1819
      old_errno = errno;
 
1820
      perror_plus("close");
 
1821
      errno = old_errno;
1582
1822
    }
1583
1823
    return false;
1584
1824
  }
 
1825
  if((close(s) == -1) and debug){
 
1826
    old_errno = errno;
 
1827
    perror_plus("close");
 
1828
    errno = old_errno;
 
1829
  }
1585
1830
  return true;
1586
1831
}
1587
1832
 
1848
2093
      return;
1849
2094
    }
1850
2095
  }
1851
 
#ifdef __GLIBC__
1852
 
#if __GLIBC_PREREQ(2, 15)
 
2096
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
2097
  if(devnull == -1){
 
2098
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
2099
    return;
 
2100
  }
1853
2101
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1854
2102
                           runnable_hook, alphasort);
1855
 
#else  /* not __GLIBC_PREREQ(2, 15) */
1856
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1857
 
                         alphasort);
1858
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
1859
 
#else   /* not __GLIBC__ */
1860
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1861
 
                         alphasort);
1862
 
#endif  /* not __GLIBC__ */
1863
2103
  if(numhooks == -1){
1864
2104
    perror_plus("scandir");
 
2105
    close(devnull);
1865
2106
    return;
1866
2107
  }
1867
2108
  struct dirent *direntry;
1868
2109
  int ret;
1869
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1870
 
  if(devnull == -1){
1871
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
1872
 
    return;
1873
 
  }
1874
2110
  for(int i = 0; i < numhooks; i++){
1875
2111
    direntry = direntries[i];
1876
2112
    if(debug){
1947
2183
        perror_plus("openat");
1948
2184
        _exit(EXIT_FAILURE);
1949
2185
      }
1950
 
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
2186
      if(close(hookdir_fd) == -1){
1951
2187
        perror_plus("close");
1952
2188
        _exit(EXIT_FAILURE);
1953
2189
      }
1956
2192
        perror_plus("dup2(devnull, STDIN_FILENO)");
1957
2193
        _exit(EX_OSERR);
1958
2194
      }
1959
 
      ret = (int)TEMP_FAILURE_RETRY(close(devnull));
 
2195
      ret = close(devnull);
1960
2196
      if(ret == -1){
1961
2197
        perror_plus("close");
1962
2198
        _exit(EX_OSERR);
2011
2247
    free(direntry);
2012
2248
  }
2013
2249
  free(direntries);
2014
 
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
2250
  if(close(hookdir_fd) == -1){
2015
2251
    perror_plus("close");
2016
2252
  } else {
2017
2253
    hookdir_fd = -1;
2020
2256
}
2021
2257
 
2022
2258
__attribute__((nonnull, warn_unused_result))
2023
 
error_t bring_up_interface(const char *const interface,
2024
 
                           const float delay){
2025
 
  error_t old_errno = errno;
 
2259
int bring_up_interface(const char *const interface,
 
2260
                       const float delay){
 
2261
  int old_errno = errno;
2026
2262
  int ret;
2027
2263
  struct ifreq network;
2028
2264
  unsigned int if_index = if_nametoindex(interface);
2038
2274
  }
2039
2275
  
2040
2276
  if(not interface_is_up(interface)){
2041
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2277
    int ret_errno = 0;
 
2278
    int ioctl_errno = 0;
2042
2279
    if(not get_flags(interface, &network)){
2043
2280
      ret_errno = errno;
2044
2281
      fprintf_plus(stderr, "Failed to get flags for interface "
2057
2294
    }
2058
2295
    
2059
2296
    if(quit_now){
2060
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2297
      ret = close(sd);
2061
2298
      if(ret == -1){
2062
2299
        perror_plus("close");
2063
2300
      }
2113
2350
    }
2114
2351
    
2115
2352
    /* Close the socket */
2116
 
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2353
    ret = close(sd);
2117
2354
    if(ret == -1){
2118
2355
      perror_plus("close");
2119
2356
    }
2131
2368
  
2132
2369
  /* Sleep checking until interface is running.
2133
2370
     Check every 0.25s, up to total time of delay */
2134
 
  for(int i=0; i < delay * 4; i++){
 
2371
  for(int i = 0; i < delay * 4; i++){
2135
2372
    if(interface_is_running(interface)){
2136
2373
      break;
2137
2374
    }
2147
2384
}
2148
2385
 
2149
2386
__attribute__((nonnull, warn_unused_result))
2150
 
error_t take_down_interface(const char *const interface){
2151
 
  error_t old_errno = errno;
 
2387
int take_down_interface(const char *const interface){
 
2388
  int old_errno = errno;
2152
2389
  struct ifreq network;
2153
2390
  unsigned int if_index = if_nametoindex(interface);
2154
2391
  if(if_index == 0){
2157
2394
    return ENXIO;
2158
2395
  }
2159
2396
  if(interface_is_up(interface)){
2160
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2397
    int ret_errno = 0;
 
2398
    int ioctl_errno = 0;
2161
2399
    if(not get_flags(interface, &network) and debug){
2162
2400
      ret_errno = errno;
2163
2401
      fprintf_plus(stderr, "Failed to get flags for interface "
2201
2439
    }
2202
2440
    
2203
2441
    /* Close the socket */
2204
 
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2442
    int ret = close(sd);
2205
2443
    if(ret == -1){
2206
2444
      perror_plus("close");
2207
2445
    }
2223
2461
 
2224
2462
int main(int argc, char *argv[]){
2225
2463
  mandos_context mc = { .server = NULL, .dh_bits = 0,
2226
 
                        .priority = "SECURE256:!CTYPE-X.509:"
2227
 
                        "+CTYPE-OPENPGP:!RSA", .current_server = NULL,
2228
 
                        .interfaces = NULL, .interfaces_size = 0 };
 
2464
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2465
                        .priority = "SECURE128:!CTYPE-X.509"
 
2466
                        ":+CTYPE-RAWPK:!RSA:!VERS-ALL:+VERS-TLS1.3"
 
2467
                        ":%PROFILE_ULTRA",
 
2468
#elif GNUTLS_VERSION_NUMBER < 0x030600
 
2469
                        .priority = "SECURE256:!CTYPE-X.509"
 
2470
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
 
2471
#else
 
2472
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
2473
#endif
 
2474
                        .current_server = NULL, .interfaces = NULL,
 
2475
                        .interfaces_size = 0 };
2229
2476
  AvahiSServiceBrowser *sb = NULL;
2230
2477
  error_t ret_errno;
2231
2478
  int ret;
2240
2487
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2241
2488
  const char *seckey = PATHDIR "/" SECKEY;
2242
2489
  const char *pubkey = PATHDIR "/" PUBKEY;
 
2490
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2491
  const char *tls_privkey = PATHDIR "/" TLS_PRIVKEY;
 
2492
  const char *tls_pubkey = PATHDIR "/" TLS_PUBKEY;
 
2493
#endif
 
2494
  const char *dh_params_file = NULL;
2243
2495
  char *interfaces_hooks = NULL;
2244
2496
  
2245
2497
  bool gnutls_initialized = false;
2292
2544
      { .name = "pubkey", .key = 'p',
2293
2545
        .arg = "FILE",
2294
2546
        .doc = "OpenPGP public key file base name",
2295
 
        .group = 2 },
 
2547
        .group = 1 },
 
2548
      { .name = "tls-privkey", .key = 't',
 
2549
        .arg = "FILE",
 
2550
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2551
        .doc = "TLS private key file base name",
 
2552
#else
 
2553
        .doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
 
2554
#endif
 
2555
        .group = 1 },
 
2556
      { .name = "tls-pubkey", .key = 'T',
 
2557
        .arg = "FILE",
 
2558
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2559
        .doc = "TLS public key file base name",
 
2560
#else
 
2561
        .doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
 
2562
#endif
 
2563
        .group = 1 },
2296
2564
      { .name = "dh-bits", .key = 129,
2297
2565
        .arg = "BITS",
2298
2566
        .doc = "Bit length of the prime number used in the"
2299
2567
        " Diffie-Hellman key exchange",
2300
2568
        .group = 2 },
 
2569
      { .name = "dh-params", .key = 134,
 
2570
        .arg = "FILE",
 
2571
        .doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
 
2572
        " for the Diffie-Hellman key exchange",
 
2573
        .group = 2 },
2301
2574
      { .name = "priority", .key = 130,
2302
2575
        .arg = "STRING",
2303
2576
        .doc = "GnuTLS priority string for the TLS handshake",
2349
2622
      case 'p':                 /* --pubkey */
2350
2623
        pubkey = arg;
2351
2624
        break;
 
2625
      case 't':                 /* --tls-privkey */
 
2626
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2627
        tls_privkey = arg;
 
2628
#endif
 
2629
        break;
 
2630
      case 'T':                 /* --tls-pubkey */
 
2631
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2632
        tls_pubkey = arg;
 
2633
#endif
 
2634
        break;
2352
2635
      case 129:                 /* --dh-bits */
2353
2636
        errno = 0;
2354
2637
        tmpmax = strtoimax(arg, &tmp, 10);
2358
2641
        }
2359
2642
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2360
2643
        break;
 
2644
      case 134:                 /* --dh-params */
 
2645
        dh_params_file = arg;
 
2646
        break;
2361
2647
      case 130:                 /* --priority */
2362
2648
        mc.priority = arg;
2363
2649
        break;
2403
2689
                         .args_doc = "",
2404
2690
                         .doc = "Mandos client -- Get and decrypt"
2405
2691
                         " passwords from a Mandos server" };
2406
 
    ret = argp_parse(&argp, argc, argv,
2407
 
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2408
 
    switch(ret){
 
2692
    ret_errno = argp_parse(&argp, argc, argv,
 
2693
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2694
    switch(ret_errno){
2409
2695
    case 0:
2410
2696
      break;
2411
2697
    case ENOMEM:
2412
2698
    default:
2413
 
      errno = ret;
 
2699
      errno = ret_errno;
2414
2700
      perror_plus("argp_parse");
2415
2701
      exitcode = EX_OSERR;
2416
2702
      goto end;
2422
2708
  
2423
2709
  {
2424
2710
    /* Work around Debian bug #633582:
2425
 
       <http://bugs.debian.org/633582> */
 
2711
       <https://bugs.debian.org/633582> */
2426
2712
    
2427
2713
    /* Re-raise privileges */
2428
 
    ret_errno = raise_privileges();
2429
 
    if(ret_errno != 0){
2430
 
      errno = ret_errno;
 
2714
    ret = raise_privileges();
 
2715
    if(ret != 0){
 
2716
      errno = ret;
2431
2717
      perror_plus("Failed to raise privileges");
2432
2718
    } else {
2433
2719
      struct stat st;
2449
2735
              }
2450
2736
            }
2451
2737
          }
2452
 
          TEMP_FAILURE_RETRY(close(seckey_fd));
 
2738
          close(seckey_fd);
2453
2739
        }
2454
2740
      }
2455
2741
      
2470
2756
              }
2471
2757
            }
2472
2758
          }
2473
 
          TEMP_FAILURE_RETRY(close(pubkey_fd));
 
2759
          close(pubkey_fd);
 
2760
        }
 
2761
      }
 
2762
      
 
2763
      if(dh_params_file != NULL
 
2764
         and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
 
2765
        int dhparams_fd = open(dh_params_file, O_RDONLY);
 
2766
        if(dhparams_fd == -1){
 
2767
          perror_plus("open");
 
2768
        } else {
 
2769
          ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
 
2770
          if(ret == -1){
 
2771
            perror_plus("fstat");
 
2772
          } else {
 
2773
            if(S_ISREG(st.st_mode)
 
2774
               and st.st_uid == 0 and st.st_gid == 0){
 
2775
              ret = fchown(dhparams_fd, uid, gid);
 
2776
              if(ret == -1){
 
2777
                perror_plus("fchown");
 
2778
              }
 
2779
            }
 
2780
          }
 
2781
          close(dhparams_fd);
2474
2782
        }
2475
2783
      }
2476
2784
      
2477
2785
      /* Lower privileges */
2478
 
      ret_errno = lower_privileges();
2479
 
      if(ret_errno != 0){
2480
 
        errno = ret_errno;
 
2786
      ret = lower_privileges();
 
2787
      if(ret != 0){
 
2788
        errno = ret;
2481
2789
        perror_plus("Failed to lower privileges");
2482
2790
      }
2483
2791
    }
2653
2961
      errno = bring_up_interface(interface, delay);
2654
2962
      if(not interface_was_up){
2655
2963
        if(errno != 0){
2656
 
          perror_plus("Failed to bring up interface");
 
2964
          fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
 
2965
                       " %s\n", interface, strerror(errno));
2657
2966
        } else {
2658
2967
          errno = argz_add(&interfaces_to_take_down,
2659
2968
                           &interfaces_to_take_down_size,
2682
2991
    goto end;
2683
2992
  }
2684
2993
  
2685
 
  ret = init_gnutls_global(pubkey, seckey, &mc);
 
2994
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2995
  ret = init_gnutls_global(tls_pubkey, tls_privkey, dh_params_file, &mc);
 
2996
#elif GNUTLS_VERSION_NUMBER < 0x030600
 
2997
  ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
 
2998
#else
 
2999
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
3000
#endif
2686
3001
  if(ret == -1){
2687
3002
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2688
3003
    exitcode = EX_UNAVAILABLE;
2810
3125
    
2811
3126
    /* Allocate a new server */
2812
3127
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2813
 
                                 &config, NULL, NULL, &ret_errno);
 
3128
                                 &config, NULL, NULL, &ret);
2814
3129
    
2815
3130
    /* Free the Avahi configuration data */
2816
3131
    avahi_server_config_free(&config);
2819
3134
  /* Check if creating the Avahi server object succeeded */
2820
3135
  if(mc.server == NULL){
2821
3136
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2822
 
                 avahi_strerror(ret_errno));
 
3137
                 avahi_strerror(ret));
2823
3138
    exitcode = EX_UNAVAILABLE;
2824
3139
    goto end;
2825
3140
  }
2860
3175
 end:
2861
3176
  
2862
3177
  if(debug){
2863
 
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3178
    if(signal_received){
 
3179
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
 
3180
                   argv[0], signal_received,
 
3181
                   strsignal(signal_received));
 
3182
    } else {
 
3183
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3184
    }
2864
3185
  }
2865
3186
  
2866
3187
  /* Cleanup things */
2877
3198
  
2878
3199
  if(gnutls_initialized){
2879
3200
    gnutls_certificate_free_credentials(mc.cred);
2880
 
    gnutls_global_deinit();
2881
3201
    gnutls_dh_params_deinit(mc.dh_params);
2882
3202
  }
2883
3203
  
2906
3226
  
2907
3227
  /* Re-raise privileges */
2908
3228
  {
2909
 
    ret_errno = raise_privileges();
2910
 
    if(ret_errno != 0){
2911
 
      errno = ret_errno;
 
3229
    ret = raise_privileges();
 
3230
    if(ret != 0){
 
3231
      errno = ret;
2912
3232
      perror_plus("Failed to raise privileges");
2913
3233
    } else {
2914
3234
      
2919
3239
      /* Take down the network interfaces which were brought up */
2920
3240
      {
2921
3241
        char *interface = NULL;
2922
 
        while((interface=argz_next(interfaces_to_take_down,
2923
 
                                   interfaces_to_take_down_size,
2924
 
                                   interface))){
2925
 
          ret_errno = take_down_interface(interface);
2926
 
          if(ret_errno != 0){
2927
 
            errno = ret_errno;
 
3242
        while((interface = argz_next(interfaces_to_take_down,
 
3243
                                     interfaces_to_take_down_size,
 
3244
                                     interface))){
 
3245
          ret = take_down_interface(interface);
 
3246
          if(ret != 0){
 
3247
            errno = ret;
2928
3248
            perror_plus("Failed to take down interface");
2929
3249
          }
2930
3250
        }
2935
3255
      }
2936
3256
    }
2937
3257
    
2938
 
    ret_errno = lower_privileges_permanently();
2939
 
    if(ret_errno != 0){
2940
 
      errno = ret_errno;
 
3258
    ret = lower_privileges_permanently();
 
3259
    if(ret != 0){
 
3260
      errno = ret;
2941
3261
      perror_plus("Failed to lower privileges permanently");
2942
3262
    }
2943
3263
  }
2945
3265
  free(interfaces_to_take_down);
2946
3266
  free(interfaces_hooks);
2947
3267
  
 
3268
  void clean_dir_at(int base, const char * const dirname,
 
3269
                    uintmax_t level){
 
3270
    struct dirent **direntries = NULL;
 
3271
    int dret;
 
3272
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
 
3273
                                                O_RDONLY
 
3274
                                                | O_NOFOLLOW
 
3275
                                                | O_DIRECTORY
 
3276
                                                | O_PATH));
 
3277
    if(dir_fd == -1){
 
3278
      perror_plus("open");
 
3279
      return;
 
3280
    }
 
3281
    int numentries = scandirat(dir_fd, ".", &direntries,
 
3282
                               notdotentries, alphasort);
 
3283
    if(numentries >= 0){
 
3284
      for(int i = 0; i < numentries; i++){
 
3285
        if(debug){
 
3286
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
 
3287
                       dirname, direntries[i]->d_name);
 
3288
        }
 
3289
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
 
3290
        if(dret == -1){
 
3291
          if(errno == EISDIR){
 
3292
              dret = unlinkat(dir_fd, direntries[i]->d_name,
 
3293
                              AT_REMOVEDIR);
 
3294
          }         
 
3295
          if((dret == -1) and (errno == ENOTEMPTY)
 
3296
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
 
3297
                  == 0) and (level == 0)){
 
3298
            /* Recurse only in this special case */
 
3299
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
 
3300
            dret = 0;
 
3301
          }
 
3302
          if((dret == -1) and (errno != ENOENT)){
 
3303
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
 
3304
                         direntries[i]->d_name, strerror(errno));
 
3305
          }
 
3306
        }
 
3307
        free(direntries[i]);
 
3308
      }
 
3309
      
 
3310
      /* need to clean even if 0 because man page doesn't specify */
 
3311
      free(direntries);
 
3312
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
 
3313
      if(dret == -1 and errno != ENOENT){
 
3314
        perror_plus("rmdir");
 
3315
      }
 
3316
    } else {
 
3317
      perror_plus("scandirat");
 
3318
    }
 
3319
    close(dir_fd);
 
3320
  }
 
3321
  
2948
3322
  /* Removes the GPGME temp directory and all files inside */
2949
3323
  if(tempdir != NULL){
2950
 
    struct dirent **direntries = NULL;
2951
 
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
2952
 
                                                  | O_NOFOLLOW
2953
 
                                                  | O_DIRECTORY
2954
 
                                                  | O_PATH));
2955
 
    if(tempdir_fd == -1){
2956
 
      perror_plus("open");
2957
 
    } else {
2958
 
#ifdef __GLIBC__
2959
 
#if __GLIBC_PREREQ(2, 15)
2960
 
      int numentries = scandirat(tempdir_fd, ".", &direntries,
2961
 
                                 notdotentries, alphasort);
2962
 
#else  /* not __GLIBC_PREREQ(2, 15) */
2963
 
      int numentries = scandir(tempdir, &direntries, notdotentries,
2964
 
                               alphasort);
2965
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
2966
 
#else   /* not __GLIBC__ */
2967
 
      int numentries = scandir(tempdir, &direntries, notdotentries,
2968
 
                               alphasort);
2969
 
#endif  /* not __GLIBC__ */
2970
 
      if(numentries >= 0){
2971
 
        for(int i = 0; i < numentries; i++){
2972
 
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
2973
 
          if(ret == -1){
2974
 
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
2975
 
                         " \"%s\", 0): %s\n", tempdir,
2976
 
                         direntries[i]->d_name, strerror(errno));
2977
 
          }
2978
 
          free(direntries[i]);
2979
 
        }
2980
 
        
2981
 
        /* need to clean even if 0 because man page doesn't specify */
2982
 
        free(direntries);
2983
 
        if(numentries == -1){
2984
 
          perror_plus("scandir");
2985
 
        }
2986
 
        ret = rmdir(tempdir);
2987
 
        if(ret == -1 and errno != ENOENT){
2988
 
          perror_plus("rmdir");
2989
 
        }
2990
 
      }
2991
 
      TEMP_FAILURE_RETRY(close(tempdir_fd));
2992
 
    }
 
3324
    clean_dir_at(-1, tempdir, 0);
2993
3325
  }
2994
3326
  
2995
3327
  if(quit_now){