/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 at recompile
  • Date: 2020-04-05 21:30:59 UTC
  • Revision ID: teddy@recompile.se-20200405213059-fb2a61ckqynrmatk
Fix file descriptor leak in mandos-client

When the local network has Mandos servers announcing themselves using
real, globally reachable, IPv6 addresses (i.e. not link-local
addresses), but there is no router on the local network providing IPv6
RA (Router Advertisement) packets, the client cannot reach the server
by normal means, since the client only has a link-local IPv6 address,
and has no usable route to reach the server's global IPv6 address.
(This is not a common situation, and usually only happens when the
router itself reboots and runs a Mandos client, since it cannot then
give RA packets to itself.)  The client code has a solution for
this, which consists of adding a temporary local route to reach the
address of the server during communication, and removing this
temporary route afterwards.

This solution with a temporary route works, but has a file descriptor
leak; it leaks one file descriptor for each addition and for each
removal of a route.  If one server requiring an added route is present
on the network, but no servers gives a password, making the client
retry after the default ten seconds, and we furthermore assume a
default 1024 open files limit, the client runs out of file descriptors
after about 90 minutes, after which time the client process will be
useless and fail to retrieve any passwords, necessitating manual
password entry via the keyboard.

Fix this by eliminating the file descriptor leak in the client.

* plugins.d/mandos-client.c (add_delete_local_route): Do
  close(devnull) also in parent process, also if fork() fails, and on
  any failure in child process.

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-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
 */
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,
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,
916
1074
      ret = setgid(0);
917
1075
      if(ret == -1){
918
1076
        perror_plus("setgid");
 
1077
        close(devnull);
919
1078
        _exit(EX_NOPERM);
920
1079
      }
921
1080
      /* Reset supplementary groups */
923
1082
      ret = setgroups(0, NULL);
924
1083
      if(ret == -1){
925
1084
        perror_plus("setgroups");
 
1085
        close(devnull);
926
1086
        _exit(EX_NOPERM);
927
1087
      }
928
1088
    }
929
1089
    ret = dup2(devnull, STDIN_FILENO);
930
1090
    if(ret == -1){
931
1091
      perror_plus("dup2(devnull, STDIN_FILENO)");
 
1092
      close(devnull);
932
1093
      _exit(EX_OSERR);
933
1094
    }
934
 
    ret = (int)TEMP_FAILURE_RETRY(close(devnull));
 
1095
    ret = close(devnull);
935
1096
    if(ret == -1){
936
1097
      perror_plus("close");
937
 
      _exit(EX_OSERR);
938
1098
    }
939
1099
    ret = dup2(STDERR_FILENO, STDOUT_FILENO);
940
1100
    if(ret == -1){
954
1114
                                                   helper, O_RDONLY));
955
1115
    if(helper_fd == -1){
956
1116
      perror_plus("openat");
 
1117
      close(helperdir_fd);
957
1118
      _exit(EX_UNAVAILABLE);
958
1119
    }
959
 
    TEMP_FAILURE_RETRY(close(helperdir_fd));
 
1120
    close(helperdir_fd);
960
1121
#ifdef __GNUC__
961
1122
#pragma GCC diagnostic push
962
1123
#pragma GCC diagnostic ignored "-Wcast-qual"
974
1135
  }
975
1136
  if(pid == -1){
976
1137
    perror_plus("fork");
 
1138
    close(devnull);
977
1139
    return false;
978
1140
  }
 
1141
  ret = close(devnull);
 
1142
  if(ret == -1){
 
1143
    perror_plus("close");
 
1144
  }
979
1145
  int status;
980
1146
  pid_t pret = -1;
981
1147
  errno = 0;
1081
1247
    bool match = false;
1082
1248
    {
1083
1249
      char *interface = NULL;
1084
 
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
1085
 
                                 interface))){
 
1250
      while((interface = argz_next(mc->interfaces,
 
1251
                                   mc->interfaces_size,
 
1252
                                   interface))){
1086
1253
        if(if_nametoindex(interface) == (unsigned int)if_index){
1087
1254
          match = true;
1088
1255
          break;
1130
1297
    goto mandos_end;
1131
1298
  }
1132
1299
  
1133
 
  memset(&to, 0, sizeof(to));
1134
1300
  if(af == AF_INET6){
1135
 
    ((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
1136
 
    ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
 
1301
    struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
 
1302
    *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
 
1303
    ret = inet_pton(af, ip, &to6->sin6_addr);
1137
1304
  } else {                      /* IPv4 */
1138
 
    ((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
1139
 
    ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
 
1305
    struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
 
1306
    *to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
 
1307
    ret = inet_pton(af, ip, &to4->sin_addr);
1140
1308
  }
1141
1309
  if(ret < 0 ){
1142
1310
    int e = errno;
1221
1389
                    sizeof(struct sockaddr_in));
1222
1390
    }
1223
1391
    if(ret < 0){
1224
 
      if(errno == ENETUNREACH
 
1392
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1225
1393
         and if_index != AVAHI_IF_UNSPEC
1226
1394
         and connect_to == NULL
1227
1395
         and not route_added and
1240
1408
           with an explicit route added with the server's address.
1241
1409
           
1242
1410
           Avahi bug reference:
1243
 
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1411
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1244
1412
           https://bugs.debian.org/587961
1245
1413
        */
1246
1414
        if(debug){
1426
1594
                                               &decrypted_buffer, mc);
1427
1595
    if(decrypted_buffer_size >= 0){
1428
1596
      
 
1597
      clearerr(stdout);
1429
1598
      written = 0;
1430
1599
      while(written < (size_t) decrypted_buffer_size){
1431
1600
        if(quit_now){
1447
1616
        }
1448
1617
        written += (size_t)ret;
1449
1618
      }
 
1619
      ret = fflush(stdout);
 
1620
      if(ret != 0){
 
1621
        int e = errno;
 
1622
        if(debug){
 
1623
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
 
1624
                       strerror(errno));
 
1625
        }
 
1626
        errno = e;
 
1627
        goto mandos_end;
 
1628
      }
1450
1629
      retval = 0;
1451
1630
    }
1452
1631
  }
1465
1644
    free(decrypted_buffer);
1466
1645
    free(buffer);
1467
1646
    if(tcp_sd >= 0){
1468
 
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
1647
      ret = close(tcp_sd);
1469
1648
    }
1470
1649
    if(ret == -1){
1471
1650
      if(e == 0){
1483
1662
  return retval;
1484
1663
}
1485
1664
 
1486
 
__attribute__((nonnull))
1487
1665
static void resolve_callback(AvahiSServiceResolver *r,
1488
1666
                             AvahiIfIndex interface,
1489
1667
                             AvahiProtocol proto,
1626
1804
__attribute__((nonnull, warn_unused_result))
1627
1805
bool get_flags(const char *ifname, struct ifreq *ifr){
1628
1806
  int ret;
1629
 
  error_t ret_errno;
 
1807
  int old_errno;
1630
1808
  
1631
1809
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1632
1810
  if(s < 0){
1633
 
    ret_errno = errno;
 
1811
    old_errno = errno;
1634
1812
    perror_plus("socket");
1635
 
    errno = ret_errno;
 
1813
    errno = old_errno;
1636
1814
    return false;
1637
1815
  }
1638
 
  strcpy(ifr->ifr_name, ifname);
 
1816
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
 
1817
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1639
1818
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1640
1819
  if(ret == -1){
1641
1820
    if(debug){
1642
 
      ret_errno = errno;
 
1821
      old_errno = errno;
1643
1822
      perror_plus("ioctl SIOCGIFFLAGS");
1644
 
      errno = ret_errno;
 
1823
      errno = old_errno;
 
1824
    }
 
1825
    if((close(s) == -1) and debug){
 
1826
      old_errno = errno;
 
1827
      perror_plus("close");
 
1828
      errno = old_errno;
1645
1829
    }
1646
1830
    return false;
1647
1831
  }
 
1832
  if((close(s) == -1) and debug){
 
1833
    old_errno = errno;
 
1834
    perror_plus("close");
 
1835
    errno = old_errno;
 
1836
  }
1648
1837
  return true;
1649
1838
}
1650
1839
 
1911
2100
      return;
1912
2101
    }
1913
2102
  }
1914
 
#ifdef __GLIBC__
1915
 
#if __GLIBC_PREREQ(2, 15)
 
2103
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
2104
  if(devnull == -1){
 
2105
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
2106
    return;
 
2107
  }
1916
2108
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1917
2109
                           runnable_hook, alphasort);
1918
 
#else  /* not __GLIBC_PREREQ(2, 15) */
1919
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1920
 
                         alphasort);
1921
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
1922
 
#else   /* not __GLIBC__ */
1923
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1924
 
                         alphasort);
1925
 
#endif  /* not __GLIBC__ */
1926
2110
  if(numhooks == -1){
1927
2111
    perror_plus("scandir");
 
2112
    close(devnull);
1928
2113
    return;
1929
2114
  }
1930
2115
  struct dirent *direntry;
1931
2116
  int ret;
1932
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1933
 
  if(devnull == -1){
1934
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
1935
 
    return;
1936
 
  }
1937
2117
  for(int i = 0; i < numhooks; i++){
1938
2118
    direntry = direntries[i];
1939
2119
    if(debug){
2010
2190
        perror_plus("openat");
2011
2191
        _exit(EXIT_FAILURE);
2012
2192
      }
2013
 
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
2193
      if(close(hookdir_fd) == -1){
2014
2194
        perror_plus("close");
2015
2195
        _exit(EXIT_FAILURE);
2016
2196
      }
2019
2199
        perror_plus("dup2(devnull, STDIN_FILENO)");
2020
2200
        _exit(EX_OSERR);
2021
2201
      }
2022
 
      ret = (int)TEMP_FAILURE_RETRY(close(devnull));
 
2202
      ret = close(devnull);
2023
2203
      if(ret == -1){
2024
2204
        perror_plus("close");
2025
2205
        _exit(EX_OSERR);
2074
2254
    free(direntry);
2075
2255
  }
2076
2256
  free(direntries);
2077
 
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
2257
  if(close(hookdir_fd) == -1){
2078
2258
    perror_plus("close");
2079
2259
  } else {
2080
2260
    hookdir_fd = -1;
2083
2263
}
2084
2264
 
2085
2265
__attribute__((nonnull, warn_unused_result))
2086
 
error_t bring_up_interface(const char *const interface,
2087
 
                           const float delay){
2088
 
  error_t old_errno = errno;
 
2266
int bring_up_interface(const char *const interface,
 
2267
                       const float delay){
 
2268
  int old_errno = errno;
2089
2269
  int ret;
2090
2270
  struct ifreq network;
2091
2271
  unsigned int if_index = if_nametoindex(interface);
2101
2281
  }
2102
2282
  
2103
2283
  if(not interface_is_up(interface)){
2104
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2284
    int ret_errno = 0;
 
2285
    int ioctl_errno = 0;
2105
2286
    if(not get_flags(interface, &network)){
2106
2287
      ret_errno = errno;
2107
2288
      fprintf_plus(stderr, "Failed to get flags for interface "
2120
2301
    }
2121
2302
    
2122
2303
    if(quit_now){
2123
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2304
      ret = close(sd);
2124
2305
      if(ret == -1){
2125
2306
        perror_plus("close");
2126
2307
      }
2176
2357
    }
2177
2358
    
2178
2359
    /* Close the socket */
2179
 
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2360
    ret = close(sd);
2180
2361
    if(ret == -1){
2181
2362
      perror_plus("close");
2182
2363
    }
2194
2375
  
2195
2376
  /* Sleep checking until interface is running.
2196
2377
     Check every 0.25s, up to total time of delay */
2197
 
  for(int i=0; i < delay * 4; i++){
 
2378
  for(int i = 0; i < delay * 4; i++){
2198
2379
    if(interface_is_running(interface)){
2199
2380
      break;
2200
2381
    }
2210
2391
}
2211
2392
 
2212
2393
__attribute__((nonnull, warn_unused_result))
2213
 
error_t take_down_interface(const char *const interface){
2214
 
  error_t old_errno = errno;
 
2394
int take_down_interface(const char *const interface){
 
2395
  int old_errno = errno;
2215
2396
  struct ifreq network;
2216
2397
  unsigned int if_index = if_nametoindex(interface);
2217
2398
  if(if_index == 0){
2220
2401
    return ENXIO;
2221
2402
  }
2222
2403
  if(interface_is_up(interface)){
2223
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2404
    int ret_errno = 0;
 
2405
    int ioctl_errno = 0;
2224
2406
    if(not get_flags(interface, &network) and debug){
2225
2407
      ret_errno = errno;
2226
2408
      fprintf_plus(stderr, "Failed to get flags for interface "
2264
2446
    }
2265
2447
    
2266
2448
    /* Close the socket */
2267
 
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2449
    int ret = close(sd);
2268
2450
    if(ret == -1){
2269
2451
      perror_plus("close");
2270
2452
    }
2286
2468
 
2287
2469
int main(int argc, char *argv[]){
2288
2470
  mandos_context mc = { .server = NULL, .dh_bits = 0,
2289
 
                        .priority = "SECURE256:!CTYPE-X.509:"
2290
 
                        "+CTYPE-OPENPGP:!RSA", .current_server = NULL,
2291
 
                        .interfaces = NULL, .interfaces_size = 0 };
 
2471
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2472
                        .priority = "SECURE128:!CTYPE-X.509"
 
2473
                        ":+CTYPE-RAWPK:!RSA:!VERS-ALL:+VERS-TLS1.3"
 
2474
                        ":%PROFILE_ULTRA",
 
2475
#elif GNUTLS_VERSION_NUMBER < 0x030600
 
2476
                        .priority = "SECURE256:!CTYPE-X.509"
 
2477
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
 
2478
#else
 
2479
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
2480
#endif
 
2481
                        .current_server = NULL, .interfaces = NULL,
 
2482
                        .interfaces_size = 0 };
2292
2483
  AvahiSServiceBrowser *sb = NULL;
2293
2484
  error_t ret_errno;
2294
2485
  int ret;
2303
2494
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2304
2495
  const char *seckey = PATHDIR "/" SECKEY;
2305
2496
  const char *pubkey = PATHDIR "/" PUBKEY;
 
2497
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2498
  const char *tls_privkey = PATHDIR "/" TLS_PRIVKEY;
 
2499
  const char *tls_pubkey = PATHDIR "/" TLS_PUBKEY;
 
2500
#endif
2306
2501
  const char *dh_params_file = NULL;
2307
2502
  char *interfaces_hooks = NULL;
2308
2503
  
2356
2551
      { .name = "pubkey", .key = 'p',
2357
2552
        .arg = "FILE",
2358
2553
        .doc = "OpenPGP public key file base name",
2359
 
        .group = 2 },
 
2554
        .group = 1 },
 
2555
      { .name = "tls-privkey", .key = 't',
 
2556
        .arg = "FILE",
 
2557
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2558
        .doc = "TLS private key file base name",
 
2559
#else
 
2560
        .doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
 
2561
#endif
 
2562
        .group = 1 },
 
2563
      { .name = "tls-pubkey", .key = 'T',
 
2564
        .arg = "FILE",
 
2565
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2566
        .doc = "TLS public key file base name",
 
2567
#else
 
2568
        .doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
 
2569
#endif
 
2570
        .group = 1 },
2360
2571
      { .name = "dh-bits", .key = 129,
2361
2572
        .arg = "BITS",
2362
2573
        .doc = "Bit length of the prime number used in the"
2418
2629
      case 'p':                 /* --pubkey */
2419
2630
        pubkey = arg;
2420
2631
        break;
 
2632
      case 't':                 /* --tls-privkey */
 
2633
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2634
        tls_privkey = arg;
 
2635
#endif
 
2636
        break;
 
2637
      case 'T':                 /* --tls-pubkey */
 
2638
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
2639
        tls_pubkey = arg;
 
2640
#endif
 
2641
        break;
2421
2642
      case 129:                 /* --dh-bits */
2422
2643
        errno = 0;
2423
2644
        tmpmax = strtoimax(arg, &tmp, 10);
2458
2679
        argp_state_help(state, state->out_stream,
2459
2680
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2460
2681
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
2682
        __builtin_unreachable();
2461
2683
      case -3:                  /* --usage */
2462
2684
        argp_state_help(state, state->out_stream,
2463
2685
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
2686
        __builtin_unreachable();
2464
2687
      case 'V':                 /* --version */
2465
2688
        fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2466
2689
        exit(argp_err_exit_status);
2475
2698
                         .args_doc = "",
2476
2699
                         .doc = "Mandos client -- Get and decrypt"
2477
2700
                         " passwords from a Mandos server" };
2478
 
    ret = argp_parse(&argp, argc, argv,
2479
 
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2480
 
    switch(ret){
 
2701
    ret_errno = argp_parse(&argp, argc, argv,
 
2702
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2703
    switch(ret_errno){
2481
2704
    case 0:
2482
2705
      break;
2483
2706
    case ENOMEM:
2484
2707
    default:
2485
 
      errno = ret;
 
2708
      errno = ret_errno;
2486
2709
      perror_plus("argp_parse");
2487
2710
      exitcode = EX_OSERR;
2488
2711
      goto end;
2494
2717
  
2495
2718
  {
2496
2719
    /* Work around Debian bug #633582:
2497
 
       <http://bugs.debian.org/633582> */
 
2720
       <https://bugs.debian.org/633582> */
2498
2721
    
2499
2722
    /* Re-raise privileges */
2500
 
    ret_errno = raise_privileges();
2501
 
    if(ret_errno != 0){
2502
 
      errno = ret_errno;
 
2723
    ret = raise_privileges();
 
2724
    if(ret != 0){
 
2725
      errno = ret;
2503
2726
      perror_plus("Failed to raise privileges");
2504
2727
    } else {
2505
2728
      struct stat st;
2521
2744
              }
2522
2745
            }
2523
2746
          }
2524
 
          TEMP_FAILURE_RETRY(close(seckey_fd));
 
2747
          close(seckey_fd);
2525
2748
        }
2526
2749
      }
2527
2750
      
2542
2765
              }
2543
2766
            }
2544
2767
          }
2545
 
          TEMP_FAILURE_RETRY(close(pubkey_fd));
 
2768
          close(pubkey_fd);
2546
2769
        }
2547
2770
      }
2548
2771
      
2549
 
      if(strcmp(dh_params_file, PATHDIR "/client-dhparams.pem" )
2550
 
         == 0){
 
2772
      if(dh_params_file != NULL
 
2773
         and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2551
2774
        int dhparams_fd = open(dh_params_file, O_RDONLY);
2552
2775
        if(dhparams_fd == -1){
2553
2776
          perror_plus("open");
2564
2787
              }
2565
2788
            }
2566
2789
          }
2567
 
          TEMP_FAILURE_RETRY(close(dhparams_fd));
 
2790
          close(dhparams_fd);
2568
2791
        }
2569
2792
      }
2570
2793
      
2571
2794
      /* Lower privileges */
2572
 
      ret_errno = lower_privileges();
2573
 
      if(ret_errno != 0){
2574
 
        errno = ret_errno;
 
2795
      ret = lower_privileges();
 
2796
      if(ret != 0){
 
2797
        errno = ret;
2575
2798
        perror_plus("Failed to lower privileges");
2576
2799
      }
2577
2800
    }
2747
2970
      errno = bring_up_interface(interface, delay);
2748
2971
      if(not interface_was_up){
2749
2972
        if(errno != 0){
2750
 
          perror_plus("Failed to bring up interface");
 
2973
          fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
 
2974
                       " %s\n", interface, strerror(errno));
2751
2975
        } else {
2752
2976
          errno = argz_add(&interfaces_to_take_down,
2753
2977
                           &interfaces_to_take_down_size,
2776
3000
    goto end;
2777
3001
  }
2778
3002
  
 
3003
#if GNUTLS_VERSION_NUMBER >= 0x030606
 
3004
  ret = init_gnutls_global(tls_pubkey, tls_privkey, dh_params_file, &mc);
 
3005
#elif GNUTLS_VERSION_NUMBER < 0x030600
2779
3006
  ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
 
3007
#else
 
3008
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
 
3009
#endif
2780
3010
  if(ret == -1){
2781
3011
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2782
3012
    exitcode = EX_UNAVAILABLE;
2904
3134
    
2905
3135
    /* Allocate a new server */
2906
3136
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2907
 
                                 &config, NULL, NULL, &ret_errno);
 
3137
                                 &config, NULL, NULL, &ret);
2908
3138
    
2909
3139
    /* Free the Avahi configuration data */
2910
3140
    avahi_server_config_free(&config);
2913
3143
  /* Check if creating the Avahi server object succeeded */
2914
3144
  if(mc.server == NULL){
2915
3145
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2916
 
                 avahi_strerror(ret_errno));
 
3146
                 avahi_strerror(ret));
2917
3147
    exitcode = EX_UNAVAILABLE;
2918
3148
    goto end;
2919
3149
  }
2954
3184
 end:
2955
3185
  
2956
3186
  if(debug){
2957
 
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3187
    if(signal_received){
 
3188
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
 
3189
                   argv[0], signal_received,
 
3190
                   strsignal(signal_received));
 
3191
    } else {
 
3192
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3193
    }
2958
3194
  }
2959
3195
  
2960
3196
  /* Cleanup things */
2971
3207
  
2972
3208
  if(gnutls_initialized){
2973
3209
    gnutls_certificate_free_credentials(mc.cred);
2974
 
    gnutls_global_deinit();
2975
3210
    gnutls_dh_params_deinit(mc.dh_params);
2976
3211
  }
2977
3212
  
3000
3235
  
3001
3236
  /* Re-raise privileges */
3002
3237
  {
3003
 
    ret_errno = raise_privileges();
3004
 
    if(ret_errno != 0){
3005
 
      errno = ret_errno;
 
3238
    ret = raise_privileges();
 
3239
    if(ret != 0){
 
3240
      errno = ret;
3006
3241
      perror_plus("Failed to raise privileges");
3007
3242
    } else {
3008
3243
      
3013
3248
      /* Take down the network interfaces which were brought up */
3014
3249
      {
3015
3250
        char *interface = NULL;
3016
 
        while((interface=argz_next(interfaces_to_take_down,
3017
 
                                   interfaces_to_take_down_size,
3018
 
                                   interface))){
3019
 
          ret_errno = take_down_interface(interface);
3020
 
          if(ret_errno != 0){
3021
 
            errno = ret_errno;
 
3251
        while((interface = argz_next(interfaces_to_take_down,
 
3252
                                     interfaces_to_take_down_size,
 
3253
                                     interface))){
 
3254
          ret = take_down_interface(interface);
 
3255
          if(ret != 0){
 
3256
            errno = ret;
3022
3257
            perror_plus("Failed to take down interface");
3023
3258
          }
3024
3259
        }
3029
3264
      }
3030
3265
    }
3031
3266
    
3032
 
    ret_errno = lower_privileges_permanently();
3033
 
    if(ret_errno != 0){
3034
 
      errno = ret_errno;
 
3267
    ret = lower_privileges_permanently();
 
3268
    if(ret != 0){
 
3269
      errno = ret;
3035
3270
      perror_plus("Failed to lower privileges permanently");
3036
3271
    }
3037
3272
  }
3039
3274
  free(interfaces_to_take_down);
3040
3275
  free(interfaces_hooks);
3041
3276
  
 
3277
  void clean_dir_at(int base, const char * const dirname,
 
3278
                    uintmax_t level){
 
3279
    struct dirent **direntries = NULL;
 
3280
    int dret;
 
3281
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
 
3282
                                                O_RDONLY
 
3283
                                                | O_NOFOLLOW
 
3284
                                                | O_DIRECTORY
 
3285
                                                | O_PATH));
 
3286
    if(dir_fd == -1){
 
3287
      perror_plus("open");
 
3288
      return;
 
3289
    }
 
3290
    int numentries = scandirat(dir_fd, ".", &direntries,
 
3291
                               notdotentries, alphasort);
 
3292
    if(numentries >= 0){
 
3293
      for(int i = 0; i < numentries; i++){
 
3294
        if(debug){
 
3295
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
 
3296
                       dirname, direntries[i]->d_name);
 
3297
        }
 
3298
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
 
3299
        if(dret == -1){
 
3300
          if(errno == EISDIR){
 
3301
              dret = unlinkat(dir_fd, direntries[i]->d_name,
 
3302
                              AT_REMOVEDIR);
 
3303
          }         
 
3304
          if((dret == -1) and (errno == ENOTEMPTY)
 
3305
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
 
3306
                  == 0) and (level == 0)){
 
3307
            /* Recurse only in this special case */
 
3308
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
 
3309
            dret = 0;
 
3310
          }
 
3311
          if((dret == -1) and (errno != ENOENT)){
 
3312
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
 
3313
                         direntries[i]->d_name, strerror(errno));
 
3314
          }
 
3315
        }
 
3316
        free(direntries[i]);
 
3317
      }
 
3318
      
 
3319
      /* need to clean even if 0 because man page doesn't specify */
 
3320
      free(direntries);
 
3321
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
 
3322
      if(dret == -1 and errno != ENOENT){
 
3323
        perror_plus("rmdir");
 
3324
      }
 
3325
    } else {
 
3326
      perror_plus("scandirat");
 
3327
    }
 
3328
    close(dir_fd);
 
3329
  }
 
3330
  
3042
3331
  /* Removes the GPGME temp directory and all files inside */
3043
3332
  if(tempdir != NULL){
3044
 
    struct dirent **direntries = NULL;
3045
 
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
3046
 
                                                  | O_NOFOLLOW
3047
 
                                                  | O_DIRECTORY
3048
 
                                                  | O_PATH));
3049
 
    if(tempdir_fd == -1){
3050
 
      perror_plus("open");
3051
 
    } else {
3052
 
#ifdef __GLIBC__
3053
 
#if __GLIBC_PREREQ(2, 15)
3054
 
      int numentries = scandirat(tempdir_fd, ".", &direntries,
3055
 
                                 notdotentries, alphasort);
3056
 
#else  /* not __GLIBC_PREREQ(2, 15) */
3057
 
      int numentries = scandir(tempdir, &direntries, notdotentries,
3058
 
                               alphasort);
3059
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
3060
 
#else   /* not __GLIBC__ */
3061
 
      int numentries = scandir(tempdir, &direntries, notdotentries,
3062
 
                               alphasort);
3063
 
#endif  /* not __GLIBC__ */
3064
 
      if(numentries >= 0){
3065
 
        for(int i = 0; i < numentries; i++){
3066
 
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
3067
 
          if(ret == -1){
3068
 
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
3069
 
                         " \"%s\", 0): %s\n", tempdir,
3070
 
                         direntries[i]->d_name, strerror(errno));
3071
 
          }
3072
 
          free(direntries[i]);
3073
 
        }
3074
 
        
3075
 
        /* need to clean even if 0 because man page doesn't specify */
3076
 
        free(direntries);
3077
 
        if(numentries == -1){
3078
 
          perror_plus("scandir");
3079
 
        }
3080
 
        ret = rmdir(tempdir);
3081
 
        if(ret == -1 and errno != ENOENT){
3082
 
          perror_plus("rmdir");
3083
 
        }
3084
 
      }
3085
 
      TEMP_FAILURE_RETRY(close(tempdir_fd));
3086
 
    }
 
3333
    clean_dir_at(-1, tempdir, 0);
3087
3334
  }
3088
3335
  
3089
3336
  if(quit_now){