/mandos/release

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

« 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
  • mto: (237.7.594 trunk)
  • mto: This revision was merged to the branch mainline in revision 370.
  • 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-2016 Teddy Hogeborn
13
 
 * Copyright © 2008-2016 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
 */
47
48
                                   strtof(), abort() */
48
49
#include <stdbool.h>            /* bool, false, true */
49
50
#include <string.h>             /* strcmp(), strlen(), strerror(),
50
 
                                   asprintf(), strncpy() */
 
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,
121
123
                                   gnutls_*
122
124
                                   init_gnutls_session(),
123
125
                                   GNUTLS_* */
 
126
#if GNUTLS_VERSION_NUMBER < 0x030600
124
127
#include <gnutls/openpgp.h>
125
128
                         /* gnutls_certificate_set_openpgp_key_file(),
126
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
127
135
 
128
136
/* GPGME */
129
137
#include <gpgme.h>              /* All GPGME types, constants and
137
145
#define PATHDIR "/conf/conf.d/mandos"
138
146
#define SECKEY "seckey.txt"
139
147
#define PUBKEY "pubkey.txt"
 
148
#define TLS_PRIVKEY "tls-privkey.pem"
 
149
#define TLS_PUBKEY "tls-pubkey.pem"
140
150
#define HOOKDIR "/lib/mandos/network-hooks.d"
141
151
 
142
152
bool debug = false;
270
280
  return true;
271
281
}
272
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
 
273
335
/* 
274
336
 * Initialize GPGME.
275
337
 */
295
357
      return false;
296
358
    }
297
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
 
298
410
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
299
411
    if(rc != GPG_ERR_NO_ERROR){
300
412
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
308
420
                   gpgme_strsource(rc), gpgme_strerror(rc));
309
421
      return false;
310
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
    }
311
498
    
312
499
    ret = close(fd);
313
500
    if(ret == -1){
354
541
  /* Create new GPGME "context" */
355
542
  rc = gpgme_new(&(mc->ctx));
356
543
  if(rc != GPG_ERR_NO_ERROR){
357
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
358
 
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
359
 
                 gpgme_strerror(rc));
 
544
    fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
 
545
                 gpgme_strsource(rc), gpgme_strerror(rc));
360
546
    return false;
361
547
  }
362
548
  
398
584
  /* Create new empty GPGME data buffer for the plaintext */
399
585
  rc = gpgme_data_new(&dh_plain);
400
586
  if(rc != GPG_ERR_NO_ERROR){
401
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
402
 
                 "bad gpgme_data_new: %s: %s\n",
 
587
    fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
403
588
                 gpgme_strsource(rc), gpgme_strerror(rc));
404
589
    gpgme_data_release(dh_crypto);
405
590
    return -1;
418
603
      if(result == NULL){
419
604
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
420
605
      } else {
421
 
        fprintf_plus(stderr, "Unsupported algorithm: %s\n",
422
 
                     result->unsupported_algorithm);
423
 
        fprintf_plus(stderr, "Wrong key usage: %u\n",
424
 
                     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");
425
612
        if(result->file_name != NULL){
426
613
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
427
614
        }
428
 
        gpgme_recipient_t recipient;
429
 
        recipient = result->recipients;
430
 
        while(recipient != NULL){
 
615
 
 
616
        for(gpgme_recipient_t r = result->recipients; r != NULL;
 
617
            r = r->next){
431
618
          fprintf_plus(stderr, "Public key algorithm: %s\n",
432
 
                       gpgme_pubkey_algo_name
433
 
                       (recipient->pubkey_algo));
434
 
          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);
435
621
          fprintf_plus(stderr, "Secret key available: %s\n",
436
 
                       recipient->status == GPG_ERR_NO_SECKEY
437
 
                       ? "No" : "Yes");
438
 
          recipient = recipient->next;
 
622
                       r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
439
623
        }
440
624
      }
441
625
    }
523
707
                              const char *dhparamsfilename,
524
708
                              mandos_context *mc){
525
709
  int ret;
526
 
  unsigned int uret;
527
710
  
528
711
  if(debug){
529
712
    fprintf_plus(stderr, "Initializing GnuTLS\n");
546
729
  }
547
730
  
548
731
  if(debug){
549
 
    fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
550
 
                 " 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",
551
734
                 pubkeyfilename,
552
735
                 seckeyfilename);
553
736
  }
554
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
555
751
  ret = gnutls_certificate_set_openpgp_key_file
556
752
    (mc->cred, pubkeyfilename, seckeyfilename,
557
753
     GNUTLS_OPENPGP_FMT_BASE64);
 
754
#else
 
755
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
756
#endif
558
757
  if(ret != GNUTLS_E_SUCCESS){
559
758
    fprintf_plus(stderr,
560
 
                 "Error[%d] while reading the OpenPGP key pair ('%s',"
 
759
                 "Error[%d] while reading the key pair ('%s',"
561
760
                 " '%s')\n", ret, pubkeyfilename, seckeyfilename);
562
761
    fprintf_plus(stderr, "The GnuTLS error is: %s\n",
563
762
                 safer_gnutls_strerror(ret));
611
810
        }
612
811
        params.size += (unsigned int)bytes_read;
613
812
      }
 
813
      ret = close(dhpfile);
 
814
      if(ret == -1){
 
815
        perror_plus("close");
 
816
      }
614
817
      if(params.data == NULL){
615
818
        dhparamsfilename = NULL;
616
819
      }
625
828
                     safer_gnutls_strerror(ret));
626
829
        dhparamsfilename = NULL;
627
830
      }
 
831
      free(params.data);
628
832
    } while(false);
629
833
  }
630
834
  if(dhparamsfilename == NULL){
631
835
    if(mc->dh_bits == 0){
 
836
#if GNUTLS_VERSION_NUMBER < 0x030600
632
837
      /* Find out the optimal number of DH bits */
633
838
      /* Try to read the private key file */
634
839
      gnutls_datum_t buffer = { .data = NULL, .size = 0 };
714
919
          }
715
920
        }
716
921
      }
717
 
      uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
 
922
      unsigned int uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
718
923
      if(uret != 0){
719
924
        mc->dh_bits = uret;
720
925
        if(debug){
732
937
                     safer_gnutls_strerror(ret));
733
938
        goto globalfail;
734
939
      }
735
 
    } else if(debug){
736
 
      fprintf_plus(stderr, "DH bits explicitly set to %u\n",
737
 
                   mc->dh_bits);
738
 
    }
739
 
    ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
740
 
    if(ret != GNUTLS_E_SUCCESS){
741
 
      fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
742
 
                   " bits): %s\n", mc->dh_bits,
743
 
                   safer_gnutls_strerror(ret));
744
 
      goto globalfail;
 
940
#endif
 
941
    } else {                    /* dh_bits != 0 */
 
942
      if(debug){
 
943
        fprintf_plus(stderr, "DH bits explicitly set to %u\n",
 
944
                     mc->dh_bits);
 
945
      }
 
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);
745
954
    }
746
955
  }
747
 
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
748
956
  
749
957
  return 0;
750
958
  
761
969
  int ret;
762
970
  /* GnuTLS session creation */
763
971
  do {
764
 
    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
                                ));
765
980
    if(quit_now){
766
981
      return -1;
767
982
    }
815
1030
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
816
1031
                      __attribute__((unused)) const char *txt){}
817
1032
 
818
 
/* Set effective uid to 0, return errno */
819
 
__attribute__((warn_unused_result))
820
 
error_t raise_privileges(void){
821
 
  error_t old_errno = errno;
822
 
  error_t ret_errno = 0;
823
 
  if(seteuid(0) == -1){
824
 
    ret_errno = errno;
825
 
  }
826
 
  errno = old_errno;
827
 
  return ret_errno;
828
 
}
829
 
 
830
 
/* Set effective and real user ID to 0.  Return errno. */
831
 
__attribute__((warn_unused_result))
832
 
error_t raise_privileges_permanently(void){
833
 
  error_t old_errno = errno;
834
 
  error_t ret_errno = raise_privileges();
835
 
  if(ret_errno != 0){
836
 
    errno = old_errno;
837
 
    return ret_errno;
838
 
  }
839
 
  if(setuid(0) == -1){
840
 
    ret_errno = errno;
841
 
  }
842
 
  errno = old_errno;
843
 
  return ret_errno;
844
 
}
845
 
 
846
 
/* Set effective user ID to unprivileged saved user ID */
847
 
__attribute__((warn_unused_result))
848
 
error_t lower_privileges(void){
849
 
  error_t old_errno = errno;
850
 
  error_t ret_errno = 0;
851
 
  if(seteuid(uid) == -1){
852
 
    ret_errno = errno;
853
 
  }
854
 
  errno = old_errno;
855
 
  return ret_errno;
856
 
}
857
 
 
858
 
/* Lower privileges permanently */
859
 
__attribute__((warn_unused_result))
860
 
error_t lower_privileges_permanently(void){
861
 
  error_t old_errno = errno;
862
 
  error_t ret_errno = 0;
863
 
  if(setuid(uid) == -1){
864
 
    ret_errno = errno;
865
 
  }
866
 
  errno = old_errno;
867
 
  return ret_errno;
868
 
}
869
 
 
870
1033
/* Helper function to add_local_route() and delete_local_route() */
871
1034
__attribute__((nonnull, warn_unused_result))
872
1035
static bool add_delete_local_route(const bool add,
1077
1240
    bool match = false;
1078
1241
    {
1079
1242
      char *interface = NULL;
1080
 
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
1081
 
                                 interface))){
 
1243
      while((interface = argz_next(mc->interfaces,
 
1244
                                   mc->interfaces_size,
 
1245
                                   interface))){
1082
1246
        if(if_nametoindex(interface) == (unsigned int)if_index){
1083
1247
          match = true;
1084
1248
          break;
1237
1401
           with an explicit route added with the server's address.
1238
1402
           
1239
1403
           Avahi bug reference:
1240
 
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1404
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1241
1405
           https://bugs.debian.org/587961
1242
1406
        */
1243
1407
        if(debug){
1423
1587
                                               &decrypted_buffer, mc);
1424
1588
    if(decrypted_buffer_size >= 0){
1425
1589
      
 
1590
      clearerr(stdout);
1426
1591
      written = 0;
1427
1592
      while(written < (size_t) decrypted_buffer_size){
1428
1593
        if(quit_now){
1444
1609
        }
1445
1610
        written += (size_t)ret;
1446
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
      }
1447
1622
      retval = 0;
1448
1623
    }
1449
1624
  }
1480
1655
  return retval;
1481
1656
}
1482
1657
 
1483
 
__attribute__((nonnull))
1484
1658
static void resolve_callback(AvahiSServiceResolver *r,
1485
1659
                             AvahiIfIndex interface,
1486
1660
                             AvahiProtocol proto,
1623
1797
__attribute__((nonnull, warn_unused_result))
1624
1798
bool get_flags(const char *ifname, struct ifreq *ifr){
1625
1799
  int ret;
1626
 
  error_t ret_errno;
 
1800
  int old_errno;
1627
1801
  
1628
1802
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1629
1803
  if(s < 0){
1630
 
    ret_errno = errno;
 
1804
    old_errno = errno;
1631
1805
    perror_plus("socket");
1632
 
    errno = ret_errno;
 
1806
    errno = old_errno;
1633
1807
    return false;
1634
1808
  }
1635
1809
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1637
1811
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1638
1812
  if(ret == -1){
1639
1813
    if(debug){
1640
 
      ret_errno = errno;
 
1814
      old_errno = errno;
1641
1815
      perror_plus("ioctl SIOCGIFFLAGS");
1642
 
      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;
1643
1822
    }
1644
1823
    return false;
1645
1824
  }
 
1825
  if((close(s) == -1) and debug){
 
1826
    old_errno = errno;
 
1827
    perror_plus("close");
 
1828
    errno = old_errno;
 
1829
  }
1646
1830
  return true;
1647
1831
}
1648
1832
 
1909
2093
      return;
1910
2094
    }
1911
2095
  }
 
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
  }
1912
2101
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1913
2102
                           runnable_hook, alphasort);
1914
2103
  if(numhooks == -1){
1915
2104
    perror_plus("scandir");
 
2105
    close(devnull);
1916
2106
    return;
1917
2107
  }
1918
2108
  struct dirent *direntry;
1919
2109
  int ret;
1920
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1921
 
  if(devnull == -1){
1922
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
1923
 
    return;
1924
 
  }
1925
2110
  for(int i = 0; i < numhooks; i++){
1926
2111
    direntry = direntries[i];
1927
2112
    if(debug){
2071
2256
}
2072
2257
 
2073
2258
__attribute__((nonnull, warn_unused_result))
2074
 
error_t bring_up_interface(const char *const interface,
2075
 
                           const float delay){
2076
 
  error_t old_errno = errno;
 
2259
int bring_up_interface(const char *const interface,
 
2260
                       const float delay){
 
2261
  int old_errno = errno;
2077
2262
  int ret;
2078
2263
  struct ifreq network;
2079
2264
  unsigned int if_index = if_nametoindex(interface);
2089
2274
  }
2090
2275
  
2091
2276
  if(not interface_is_up(interface)){
2092
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2277
    int ret_errno = 0;
 
2278
    int ioctl_errno = 0;
2093
2279
    if(not get_flags(interface, &network)){
2094
2280
      ret_errno = errno;
2095
2281
      fprintf_plus(stderr, "Failed to get flags for interface "
2182
2368
  
2183
2369
  /* Sleep checking until interface is running.
2184
2370
     Check every 0.25s, up to total time of delay */
2185
 
  for(int i=0; i < delay * 4; i++){
 
2371
  for(int i = 0; i < delay * 4; i++){
2186
2372
    if(interface_is_running(interface)){
2187
2373
      break;
2188
2374
    }
2198
2384
}
2199
2385
 
2200
2386
__attribute__((nonnull, warn_unused_result))
2201
 
error_t take_down_interface(const char *const interface){
2202
 
  error_t old_errno = errno;
 
2387
int take_down_interface(const char *const interface){
 
2388
  int old_errno = errno;
2203
2389
  struct ifreq network;
2204
2390
  unsigned int if_index = if_nametoindex(interface);
2205
2391
  if(if_index == 0){
2208
2394
    return ENXIO;
2209
2395
  }
2210
2396
  if(interface_is_up(interface)){
2211
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2397
    int ret_errno = 0;
 
2398
    int ioctl_errno = 0;
2212
2399
    if(not get_flags(interface, &network) and debug){
2213
2400
      ret_errno = errno;
2214
2401
      fprintf_plus(stderr, "Failed to get flags for interface "
2274
2461
 
2275
2462
int main(int argc, char *argv[]){
2276
2463
  mandos_context mc = { .server = NULL, .dh_bits = 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
2277
2469
                        .priority = "SECURE256:!CTYPE-X.509"
2278
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
2279
2474
                        .current_server = NULL, .interfaces = NULL,
2280
2475
                        .interfaces_size = 0 };
2281
2476
  AvahiSServiceBrowser *sb = NULL;
2292
2487
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2293
2488
  const char *seckey = PATHDIR "/" SECKEY;
2294
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
2295
2494
  const char *dh_params_file = NULL;
2296
2495
  char *interfaces_hooks = NULL;
2297
2496
  
2345
2544
      { .name = "pubkey", .key = 'p',
2346
2545
        .arg = "FILE",
2347
2546
        .doc = "OpenPGP public key file base name",
2348
 
        .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 },
2349
2564
      { .name = "dh-bits", .key = 129,
2350
2565
        .arg = "BITS",
2351
2566
        .doc = "Bit length of the prime number used in the"
2407
2622
      case 'p':                 /* --pubkey */
2408
2623
        pubkey = arg;
2409
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;
2410
2635
      case 129:                 /* --dh-bits */
2411
2636
        errno = 0;
2412
2637
        tmpmax = strtoimax(arg, &tmp, 10);
2464
2689
                         .args_doc = "",
2465
2690
                         .doc = "Mandos client -- Get and decrypt"
2466
2691
                         " passwords from a Mandos server" };
2467
 
    ret = argp_parse(&argp, argc, argv,
2468
 
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2469
 
    switch(ret){
 
2692
    ret_errno = argp_parse(&argp, argc, argv,
 
2693
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2694
    switch(ret_errno){
2470
2695
    case 0:
2471
2696
      break;
2472
2697
    case ENOMEM:
2473
2698
    default:
2474
 
      errno = ret;
 
2699
      errno = ret_errno;
2475
2700
      perror_plus("argp_parse");
2476
2701
      exitcode = EX_OSERR;
2477
2702
      goto end;
2483
2708
  
2484
2709
  {
2485
2710
    /* Work around Debian bug #633582:
2486
 
       <http://bugs.debian.org/633582> */
 
2711
       <https://bugs.debian.org/633582> */
2487
2712
    
2488
2713
    /* Re-raise privileges */
2489
 
    ret_errno = raise_privileges();
2490
 
    if(ret_errno != 0){
2491
 
      errno = ret_errno;
 
2714
    ret = raise_privileges();
 
2715
    if(ret != 0){
 
2716
      errno = ret;
2492
2717
      perror_plus("Failed to raise privileges");
2493
2718
    } else {
2494
2719
      struct stat st;
2558
2783
      }
2559
2784
      
2560
2785
      /* Lower privileges */
2561
 
      ret_errno = lower_privileges();
2562
 
      if(ret_errno != 0){
2563
 
        errno = ret_errno;
 
2786
      ret = lower_privileges();
 
2787
      if(ret != 0){
 
2788
        errno = ret;
2564
2789
        perror_plus("Failed to lower privileges");
2565
2790
      }
2566
2791
    }
2766
2991
    goto end;
2767
2992
  }
2768
2993
  
 
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
2769
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
2770
3001
  if(ret == -1){
2771
3002
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2772
3003
    exitcode = EX_UNAVAILABLE;
2894
3125
    
2895
3126
    /* Allocate a new server */
2896
3127
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2897
 
                                 &config, NULL, NULL, &ret_errno);
 
3128
                                 &config, NULL, NULL, &ret);
2898
3129
    
2899
3130
    /* Free the Avahi configuration data */
2900
3131
    avahi_server_config_free(&config);
2903
3134
  /* Check if creating the Avahi server object succeeded */
2904
3135
  if(mc.server == NULL){
2905
3136
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2906
 
                 avahi_strerror(ret_errno));
 
3137
                 avahi_strerror(ret));
2907
3138
    exitcode = EX_UNAVAILABLE;
2908
3139
    goto end;
2909
3140
  }
2944
3175
 end:
2945
3176
  
2946
3177
  if(debug){
2947
 
    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
    }
2948
3185
  }
2949
3186
  
2950
3187
  /* Cleanup things */
2989
3226
  
2990
3227
  /* Re-raise privileges */
2991
3228
  {
2992
 
    ret_errno = raise_privileges();
2993
 
    if(ret_errno != 0){
2994
 
      errno = ret_errno;
 
3229
    ret = raise_privileges();
 
3230
    if(ret != 0){
 
3231
      errno = ret;
2995
3232
      perror_plus("Failed to raise privileges");
2996
3233
    } else {
2997
3234
      
3002
3239
      /* Take down the network interfaces which were brought up */
3003
3240
      {
3004
3241
        char *interface = NULL;
3005
 
        while((interface=argz_next(interfaces_to_take_down,
3006
 
                                   interfaces_to_take_down_size,
3007
 
                                   interface))){
3008
 
          ret_errno = take_down_interface(interface);
3009
 
          if(ret_errno != 0){
3010
 
            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;
3011
3248
            perror_plus("Failed to take down interface");
3012
3249
          }
3013
3250
        }
3018
3255
      }
3019
3256
    }
3020
3257
    
3021
 
    ret_errno = lower_privileges_permanently();
3022
 
    if(ret_errno != 0){
3023
 
      errno = ret_errno;
 
3258
    ret = lower_privileges_permanently();
 
3259
    if(ret != 0){
 
3260
      errno = ret;
3024
3261
      perror_plus("Failed to lower privileges permanently");
3025
3262
    }
3026
3263
  }
3039
3276
                                                | O_PATH));
3040
3277
    if(dir_fd == -1){
3041
3278
      perror_plus("open");
 
3279
      return;
3042
3280
    }
3043
3281
    int numentries = scandirat(dir_fd, ".", &direntries,
3044
3282
                               notdotentries, alphasort);
3061
3299
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3062
3300
            dret = 0;
3063
3301
          }
3064
 
          if(dret == -1){
 
3302
          if((dret == -1) and (errno != ENOENT)){
3065
3303
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3066
3304
                         direntries[i]->d_name, strerror(errno));
3067
3305
          }
3071
3309
      
3072
3310
      /* need to clean even if 0 because man page doesn't specify */
3073
3311
      free(direntries);
3074
 
      if(numentries == -1){
3075
 
        perror_plus("scandirat");
3076
 
      }
3077
3312
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
3078
3313
      if(dret == -1 and errno != ENOENT){
3079
3314
        perror_plus("rmdir");