/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-07-29 16:35:53 UTC
  • Revision ID: teddy@recompile.se-20190729163553-1i442i2cbx64c537
Make tests and man page examples match

Make the tests test_manual_page_example[1-5] match exactly what is
written in the manual page, and add comments to manual page as
reminders to keep tests and manual page examples in sync.

* mandos-ctl (Test_commands_from_options.test_manual_page_example_1):
  Remove "--verbose" option, since the manual does not have it as the
  first example, and change assertion to match.
* mandos-ctl.xml (EXAMPLE): Add comments to all examples documenting
  which test function they correspond to.  Also remove unnecessary
  quotes from option arguments in fourth example, and clarify language
  slightly in fifth example.

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-2019 Teddy Hogeborn
 
13
 * Copyright © 2008-2019 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,
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
499
    ret = close(fd);
309
500
    if(ret == -1){
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,
519
707
                              const char *dhparamsfilename,
520
708
                              mandos_context *mc){
521
709
  int ret;
522
 
  unsigned int uret;
523
710
  
524
711
  if(debug){
525
712
    fprintf_plus(stderr, "Initializing GnuTLS\n");
526
713
  }
527
714
  
528
 
  ret = gnutls_global_init();
529
 
  if(ret != GNUTLS_E_SUCCESS){
530
 
    fprintf_plus(stderr, "GnuTLS global_init: %s\n",
531
 
                 safer_gnutls_strerror(ret));
532
 
    return -1;
533
 
  }
534
 
  
535
715
  if(debug){
536
716
    /* "Use a log level over 10 to enable all debugging options."
537
717
     * - GnuTLS manual
545
725
  if(ret != GNUTLS_E_SUCCESS){
546
726
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
547
727
                 safer_gnutls_strerror(ret));
548
 
    gnutls_global_deinit();
549
728
    return -1;
550
729
  }
551
730
  
552
731
  if(debug){
553
 
    fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
554
 
                 " 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",
555
734
                 pubkeyfilename,
556
735
                 seckeyfilename);
557
736
  }
558
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
559
751
  ret = gnutls_certificate_set_openpgp_key_file
560
752
    (mc->cred, pubkeyfilename, seckeyfilename,
561
753
     GNUTLS_OPENPGP_FMT_BASE64);
 
754
#else
 
755
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
756
#endif
562
757
  if(ret != GNUTLS_E_SUCCESS){
563
758
    fprintf_plus(stderr,
564
 
                 "Error[%d] while reading the OpenPGP key pair ('%s',"
 
759
                 "Error[%d] while reading the key pair ('%s',"
565
760
                 " '%s')\n", ret, pubkeyfilename, seckeyfilename);
566
761
    fprintf_plus(stderr, "The GnuTLS error is: %s\n",
567
762
                 safer_gnutls_strerror(ret));
615
810
        }
616
811
        params.size += (unsigned int)bytes_read;
617
812
      }
 
813
      ret = close(dhpfile);
 
814
      if(ret == -1){
 
815
        perror_plus("close");
 
816
      }
618
817
      if(params.data == NULL){
619
818
        dhparamsfilename = NULL;
620
819
      }
629
828
                     safer_gnutls_strerror(ret));
630
829
        dhparamsfilename = NULL;
631
830
      }
 
831
      free(params.data);
632
832
    } while(false);
633
833
  }
634
834
  if(dhparamsfilename == NULL){
635
835
    if(mc->dh_bits == 0){
 
836
#if GNUTLS_VERSION_NUMBER < 0x030600
636
837
      /* Find out the optimal number of DH bits */
637
838
      /* Try to read the private key file */
638
839
      gnutls_datum_t buffer = { .data = NULL, .size = 0 };
718
919
          }
719
920
        }
720
921
      }
721
 
      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);
722
923
      if(uret != 0){
723
924
        mc->dh_bits = uret;
724
925
        if(debug){
736
937
                     safer_gnutls_strerror(ret));
737
938
        goto globalfail;
738
939
      }
739
 
    } else if(debug){
740
 
      fprintf_plus(stderr, "DH bits explicitly set to %u\n",
741
 
                   mc->dh_bits);
742
 
    }
743
 
    ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
744
 
    if(ret != GNUTLS_E_SUCCESS){
745
 
      fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
746
 
                   " bits): %s\n", mc->dh_bits,
747
 
                   safer_gnutls_strerror(ret));
748
 
      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);
749
954
    }
750
955
  }
751
 
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
752
956
  
753
957
  return 0;
754
958
  
755
959
 globalfail:
756
960
  
757
961
  gnutls_certificate_free_credentials(mc->cred);
758
 
  gnutls_global_deinit();
759
962
  gnutls_dh_params_deinit(mc->dh_params);
760
963
  return -1;
761
964
}
766
969
  int ret;
767
970
  /* GnuTLS session creation */
768
971
  do {
769
 
    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
                                ));
770
980
    if(quit_now){
771
981
      return -1;
772
982
    }
820
1030
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
821
1031
                      __attribute__((unused)) const char *txt){}
822
1032
 
823
 
/* Set effective uid to 0, return errno */
824
 
__attribute__((warn_unused_result))
825
 
error_t raise_privileges(void){
826
 
  error_t old_errno = errno;
827
 
  error_t ret_errno = 0;
828
 
  if(seteuid(0) == -1){
829
 
    ret_errno = errno;
830
 
  }
831
 
  errno = old_errno;
832
 
  return ret_errno;
833
 
}
834
 
 
835
 
/* Set effective and real user ID to 0.  Return errno. */
836
 
__attribute__((warn_unused_result))
837
 
error_t raise_privileges_permanently(void){
838
 
  error_t old_errno = errno;
839
 
  error_t ret_errno = raise_privileges();
840
 
  if(ret_errno != 0){
841
 
    errno = old_errno;
842
 
    return ret_errno;
843
 
  }
844
 
  if(setuid(0) == -1){
845
 
    ret_errno = errno;
846
 
  }
847
 
  errno = old_errno;
848
 
  return ret_errno;
849
 
}
850
 
 
851
 
/* Set effective user ID to unprivileged saved user ID */
852
 
__attribute__((warn_unused_result))
853
 
error_t lower_privileges(void){
854
 
  error_t old_errno = errno;
855
 
  error_t ret_errno = 0;
856
 
  if(seteuid(uid) == -1){
857
 
    ret_errno = errno;
858
 
  }
859
 
  errno = old_errno;
860
 
  return ret_errno;
861
 
}
862
 
 
863
 
/* Lower privileges permanently */
864
 
__attribute__((warn_unused_result))
865
 
error_t lower_privileges_permanently(void){
866
 
  error_t old_errno = errno;
867
 
  error_t ret_errno = 0;
868
 
  if(setuid(uid) == -1){
869
 
    ret_errno = errno;
870
 
  }
871
 
  errno = old_errno;
872
 
  return ret_errno;
873
 
}
874
 
 
875
1033
/* Helper function to add_local_route() and delete_local_route() */
876
1034
__attribute__((nonnull, warn_unused_result))
877
1035
static bool add_delete_local_route(const bool add,
1082
1240
    bool match = false;
1083
1241
    {
1084
1242
      char *interface = NULL;
1085
 
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
1086
 
                                 interface))){
 
1243
      while((interface = argz_next(mc->interfaces,
 
1244
                                   mc->interfaces_size,
 
1245
                                   interface))){
1087
1246
        if(if_nametoindex(interface) == (unsigned int)if_index){
1088
1247
          match = true;
1089
1248
          break;
1242
1401
           with an explicit route added with the server's address.
1243
1402
           
1244
1403
           Avahi bug reference:
1245
 
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1404
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1246
1405
           https://bugs.debian.org/587961
1247
1406
        */
1248
1407
        if(debug){
1428
1587
                                               &decrypted_buffer, mc);
1429
1588
    if(decrypted_buffer_size >= 0){
1430
1589
      
 
1590
      clearerr(stdout);
1431
1591
      written = 0;
1432
1592
      while(written < (size_t) decrypted_buffer_size){
1433
1593
        if(quit_now){
1449
1609
        }
1450
1610
        written += (size_t)ret;
1451
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
      }
1452
1622
      retval = 0;
1453
1623
    }
1454
1624
  }
1485
1655
  return retval;
1486
1656
}
1487
1657
 
1488
 
__attribute__((nonnull))
1489
1658
static void resolve_callback(AvahiSServiceResolver *r,
1490
1659
                             AvahiIfIndex interface,
1491
1660
                             AvahiProtocol proto,
1628
1797
__attribute__((nonnull, warn_unused_result))
1629
1798
bool get_flags(const char *ifname, struct ifreq *ifr){
1630
1799
  int ret;
1631
 
  error_t ret_errno;
 
1800
  int old_errno;
1632
1801
  
1633
1802
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1634
1803
  if(s < 0){
1635
 
    ret_errno = errno;
 
1804
    old_errno = errno;
1636
1805
    perror_plus("socket");
1637
 
    errno = ret_errno;
 
1806
    errno = old_errno;
1638
1807
    return false;
1639
1808
  }
1640
1809
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1642
1811
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1643
1812
  if(ret == -1){
1644
1813
    if(debug){
1645
 
      ret_errno = errno;
 
1814
      old_errno = errno;
1646
1815
      perror_plus("ioctl SIOCGIFFLAGS");
1647
 
      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;
1648
1822
    }
1649
1823
    return false;
1650
1824
  }
 
1825
  if((close(s) == -1) and debug){
 
1826
    old_errno = errno;
 
1827
    perror_plus("close");
 
1828
    errno = old_errno;
 
1829
  }
1651
1830
  return true;
1652
1831
}
1653
1832
 
1914
2093
      return;
1915
2094
    }
1916
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
  }
1917
2101
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1918
2102
                           runnable_hook, alphasort);
1919
2103
  if(numhooks == -1){
1920
2104
    perror_plus("scandir");
 
2105
    close(devnull);
1921
2106
    return;
1922
2107
  }
1923
2108
  struct dirent *direntry;
1924
2109
  int ret;
1925
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1926
 
  if(devnull == -1){
1927
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
1928
 
    return;
1929
 
  }
1930
2110
  for(int i = 0; i < numhooks; i++){
1931
2111
    direntry = direntries[i];
1932
2112
    if(debug){
2076
2256
}
2077
2257
 
2078
2258
__attribute__((nonnull, warn_unused_result))
2079
 
error_t bring_up_interface(const char *const interface,
2080
 
                           const float delay){
2081
 
  error_t old_errno = errno;
 
2259
int bring_up_interface(const char *const interface,
 
2260
                       const float delay){
 
2261
  int old_errno = errno;
2082
2262
  int ret;
2083
2263
  struct ifreq network;
2084
2264
  unsigned int if_index = if_nametoindex(interface);
2094
2274
  }
2095
2275
  
2096
2276
  if(not interface_is_up(interface)){
2097
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2277
    int ret_errno = 0;
 
2278
    int ioctl_errno = 0;
2098
2279
    if(not get_flags(interface, &network)){
2099
2280
      ret_errno = errno;
2100
2281
      fprintf_plus(stderr, "Failed to get flags for interface "
2187
2368
  
2188
2369
  /* Sleep checking until interface is running.
2189
2370
     Check every 0.25s, up to total time of delay */
2190
 
  for(int i=0; i < delay * 4; i++){
 
2371
  for(int i = 0; i < delay * 4; i++){
2191
2372
    if(interface_is_running(interface)){
2192
2373
      break;
2193
2374
    }
2203
2384
}
2204
2385
 
2205
2386
__attribute__((nonnull, warn_unused_result))
2206
 
error_t take_down_interface(const char *const interface){
2207
 
  error_t old_errno = errno;
 
2387
int take_down_interface(const char *const interface){
 
2388
  int old_errno = errno;
2208
2389
  struct ifreq network;
2209
2390
  unsigned int if_index = if_nametoindex(interface);
2210
2391
  if(if_index == 0){
2213
2394
    return ENXIO;
2214
2395
  }
2215
2396
  if(interface_is_up(interface)){
2216
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2397
    int ret_errno = 0;
 
2398
    int ioctl_errno = 0;
2217
2399
    if(not get_flags(interface, &network) and debug){
2218
2400
      ret_errno = errno;
2219
2401
      fprintf_plus(stderr, "Failed to get flags for interface "
2279
2461
 
2280
2462
int main(int argc, char *argv[]){
2281
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
2282
2469
                        .priority = "SECURE256:!CTYPE-X.509"
2283
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
2284
2474
                        .current_server = NULL, .interfaces = NULL,
2285
2475
                        .interfaces_size = 0 };
2286
2476
  AvahiSServiceBrowser *sb = NULL;
2297
2487
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2298
2488
  const char *seckey = PATHDIR "/" SECKEY;
2299
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
2300
2494
  const char *dh_params_file = NULL;
2301
2495
  char *interfaces_hooks = NULL;
2302
2496
  
2350
2544
      { .name = "pubkey", .key = 'p',
2351
2545
        .arg = "FILE",
2352
2546
        .doc = "OpenPGP public key file base name",
2353
 
        .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 },
2354
2564
      { .name = "dh-bits", .key = 129,
2355
2565
        .arg = "BITS",
2356
2566
        .doc = "Bit length of the prime number used in the"
2412
2622
      case 'p':                 /* --pubkey */
2413
2623
        pubkey = arg;
2414
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;
2415
2635
      case 129:                 /* --dh-bits */
2416
2636
        errno = 0;
2417
2637
        tmpmax = strtoimax(arg, &tmp, 10);
2452
2672
        argp_state_help(state, state->out_stream,
2453
2673
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2454
2674
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
2675
        __builtin_unreachable();
2455
2676
      case -3:                  /* --usage */
2456
2677
        argp_state_help(state, state->out_stream,
2457
2678
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
2679
        __builtin_unreachable();
2458
2680
      case 'V':                 /* --version */
2459
2681
        fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2460
2682
        exit(argp_err_exit_status);
2469
2691
                         .args_doc = "",
2470
2692
                         .doc = "Mandos client -- Get and decrypt"
2471
2693
                         " passwords from a Mandos server" };
2472
 
    ret = argp_parse(&argp, argc, argv,
2473
 
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2474
 
    switch(ret){
 
2694
    ret_errno = argp_parse(&argp, argc, argv,
 
2695
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2696
    switch(ret_errno){
2475
2697
    case 0:
2476
2698
      break;
2477
2699
    case ENOMEM:
2478
2700
    default:
2479
 
      errno = ret;
 
2701
      errno = ret_errno;
2480
2702
      perror_plus("argp_parse");
2481
2703
      exitcode = EX_OSERR;
2482
2704
      goto end;
2488
2710
  
2489
2711
  {
2490
2712
    /* Work around Debian bug #633582:
2491
 
       <http://bugs.debian.org/633582> */
 
2713
       <https://bugs.debian.org/633582> */
2492
2714
    
2493
2715
    /* Re-raise privileges */
2494
 
    ret_errno = raise_privileges();
2495
 
    if(ret_errno != 0){
2496
 
      errno = ret_errno;
 
2716
    ret = raise_privileges();
 
2717
    if(ret != 0){
 
2718
      errno = ret;
2497
2719
      perror_plus("Failed to raise privileges");
2498
2720
    } else {
2499
2721
      struct stat st;
2563
2785
      }
2564
2786
      
2565
2787
      /* Lower privileges */
2566
 
      ret_errno = lower_privileges();
2567
 
      if(ret_errno != 0){
2568
 
        errno = ret_errno;
 
2788
      ret = lower_privileges();
 
2789
      if(ret != 0){
 
2790
        errno = ret;
2569
2791
        perror_plus("Failed to lower privileges");
2570
2792
      }
2571
2793
    }
2771
2993
    goto end;
2772
2994
  }
2773
2995
  
 
2996
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2997
  ret = init_gnutls_global(tls_pubkey, tls_privkey, dh_params_file, &mc);
 
2998
#elif GNUTLS_VERSION_NUMBER < 0x030600
2774
2999
  ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
 
3000
#else
 
3001
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
3002
#endif
2775
3003
  if(ret == -1){
2776
3004
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2777
3005
    exitcode = EX_UNAVAILABLE;
2899
3127
    
2900
3128
    /* Allocate a new server */
2901
3129
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2902
 
                                 &config, NULL, NULL, &ret_errno);
 
3130
                                 &config, NULL, NULL, &ret);
2903
3131
    
2904
3132
    /* Free the Avahi configuration data */
2905
3133
    avahi_server_config_free(&config);
2908
3136
  /* Check if creating the Avahi server object succeeded */
2909
3137
  if(mc.server == NULL){
2910
3138
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2911
 
                 avahi_strerror(ret_errno));
 
3139
                 avahi_strerror(ret));
2912
3140
    exitcode = EX_UNAVAILABLE;
2913
3141
    goto end;
2914
3142
  }
2949
3177
 end:
2950
3178
  
2951
3179
  if(debug){
2952
 
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3180
    if(signal_received){
 
3181
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
 
3182
                   argv[0], signal_received,
 
3183
                   strsignal(signal_received));
 
3184
    } else {
 
3185
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3186
    }
2953
3187
  }
2954
3188
  
2955
3189
  /* Cleanup things */
2966
3200
  
2967
3201
  if(gnutls_initialized){
2968
3202
    gnutls_certificate_free_credentials(mc.cred);
2969
 
    gnutls_global_deinit();
2970
3203
    gnutls_dh_params_deinit(mc.dh_params);
2971
3204
  }
2972
3205
  
2995
3228
  
2996
3229
  /* Re-raise privileges */
2997
3230
  {
2998
 
    ret_errno = raise_privileges();
2999
 
    if(ret_errno != 0){
3000
 
      errno = ret_errno;
 
3231
    ret = raise_privileges();
 
3232
    if(ret != 0){
 
3233
      errno = ret;
3001
3234
      perror_plus("Failed to raise privileges");
3002
3235
    } else {
3003
3236
      
3008
3241
      /* Take down the network interfaces which were brought up */
3009
3242
      {
3010
3243
        char *interface = NULL;
3011
 
        while((interface=argz_next(interfaces_to_take_down,
3012
 
                                   interfaces_to_take_down_size,
3013
 
                                   interface))){
3014
 
          ret_errno = take_down_interface(interface);
3015
 
          if(ret_errno != 0){
3016
 
            errno = ret_errno;
 
3244
        while((interface = argz_next(interfaces_to_take_down,
 
3245
                                     interfaces_to_take_down_size,
 
3246
                                     interface))){
 
3247
          ret = take_down_interface(interface);
 
3248
          if(ret != 0){
 
3249
            errno = ret;
3017
3250
            perror_plus("Failed to take down interface");
3018
3251
          }
3019
3252
        }
3024
3257
      }
3025
3258
    }
3026
3259
    
3027
 
    ret_errno = lower_privileges_permanently();
3028
 
    if(ret_errno != 0){
3029
 
      errno = ret_errno;
 
3260
    ret = lower_privileges_permanently();
 
3261
    if(ret != 0){
 
3262
      errno = ret;
3030
3263
      perror_plus("Failed to lower privileges permanently");
3031
3264
    }
3032
3265
  }
3034
3267
  free(interfaces_to_take_down);
3035
3268
  free(interfaces_hooks);
3036
3269
  
 
3270
  void clean_dir_at(int base, const char * const dirname,
 
3271
                    uintmax_t level){
 
3272
    struct dirent **direntries = NULL;
 
3273
    int dret;
 
3274
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
 
3275
                                                O_RDONLY
 
3276
                                                | O_NOFOLLOW
 
3277
                                                | O_DIRECTORY
 
3278
                                                | O_PATH));
 
3279
    if(dir_fd == -1){
 
3280
      perror_plus("open");
 
3281
      return;
 
3282
    }
 
3283
    int numentries = scandirat(dir_fd, ".", &direntries,
 
3284
                               notdotentries, alphasort);
 
3285
    if(numentries >= 0){
 
3286
      for(int i = 0; i < numentries; i++){
 
3287
        if(debug){
 
3288
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
 
3289
                       dirname, direntries[i]->d_name);
 
3290
        }
 
3291
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
 
3292
        if(dret == -1){
 
3293
          if(errno == EISDIR){
 
3294
              dret = unlinkat(dir_fd, direntries[i]->d_name,
 
3295
                              AT_REMOVEDIR);
 
3296
          }         
 
3297
          if((dret == -1) and (errno == ENOTEMPTY)
 
3298
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
 
3299
                  == 0) and (level == 0)){
 
3300
            /* Recurse only in this special case */
 
3301
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
 
3302
            dret = 0;
 
3303
          }
 
3304
          if((dret == -1) and (errno != ENOENT)){
 
3305
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
 
3306
                         direntries[i]->d_name, strerror(errno));
 
3307
          }
 
3308
        }
 
3309
        free(direntries[i]);
 
3310
      }
 
3311
      
 
3312
      /* need to clean even if 0 because man page doesn't specify */
 
3313
      free(direntries);
 
3314
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
 
3315
      if(dret == -1 and errno != ENOENT){
 
3316
        perror_plus("rmdir");
 
3317
      }
 
3318
    } else {
 
3319
      perror_plus("scandirat");
 
3320
    }
 
3321
    close(dir_fd);
 
3322
  }
 
3323
  
3037
3324
  /* Removes the GPGME temp directory and all files inside */
3038
3325
  if(tempdir != NULL){
3039
 
    struct dirent **direntries = NULL;
3040
 
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
3041
 
                                                  | O_NOFOLLOW
3042
 
                                                  | O_DIRECTORY
3043
 
                                                  | O_PATH));
3044
 
    if(tempdir_fd == -1){
3045
 
      perror_plus("open");
3046
 
    } else {
3047
 
      int numentries = scandirat(tempdir_fd, ".", &direntries,
3048
 
                                 notdotentries, alphasort);
3049
 
      if(numentries >= 0){
3050
 
        for(int i = 0; i < numentries; i++){
3051
 
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
3052
 
          if(ret == -1){
3053
 
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
3054
 
                         " \"%s\", 0): %s\n", tempdir,
3055
 
                         direntries[i]->d_name, strerror(errno));
3056
 
          }
3057
 
          free(direntries[i]);
3058
 
        }
3059
 
        
3060
 
        /* need to clean even if 0 because man page doesn't specify */
3061
 
        free(direntries);
3062
 
        if(numentries == -1){
3063
 
          perror_plus("scandir");
3064
 
        }
3065
 
        ret = rmdir(tempdir);
3066
 
        if(ret == -1 and errno != ENOENT){
3067
 
          perror_plus("rmdir");
3068
 
        }
3069
 
      }
3070
 
      close(tempdir_fd);
3071
 
    }
 
3326
    clean_dir_at(-1, tempdir, 0);
3072
3327
  }
3073
3328
  
3074
3329
  if(quit_now){