/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: 2018-08-15 09:26:02 UTC
  • Revision ID: teddy@recompile.se-20180815092602-xoyb5s6gf8376i7u
mandos-client: Set system clock if necessary

* plugins.d/mandos-client.c (init_gpgme/import_key): If the system
  clock is not set, or set to january 1970, set the system clock to
  the more plausible value that is the mtime of the key file.  This is
  required by GnuPG to be able to import the keys.  (We can't pass the
  --ignore-time-conflict or the --ignore-valid-from options though
  GPGME.)

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-2014 Teddy Hogeborn
13
 
 * Copyright © 2008-2014 Björn Påhlsson
14
 
 * 
15
 
 * This program is free software: you can redistribute it and/or
16
 
 * modify it under the terms of the GNU General Public License as
17
 
 * published by the Free Software Foundation, either version 3 of the
18
 
 * License, or (at your option) any later version.
19
 
 * 
20
 
 * This program is distributed in the hope that it will be useful, but
 
12
 * Copyright © 2008-2018 Teddy Hogeborn
 
13
 * Copyright © 2008-2018 Björn Påhlsson
 
14
 * 
 
15
 * This file is part of Mandos.
 
16
 * 
 
17
 * Mandos is free software: you can redistribute it and/or modify it
 
18
 * under the terms of the GNU General Public License as published by
 
19
 * the Free Software Foundation, either version 3 of the License, or
 
20
 * (at your option) any later version.
 
21
 * 
 
22
 * Mandos is distributed in the hope that it will be useful, but
21
23
 * WITHOUT ANY WARRANTY; without even the implied warranty of
22
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23
25
 * General Public License for more details.
24
26
 * 
25
27
 * You should have received a copy of the GNU General Public License
26
 
 * along with this program.  If not, see
27
 
 * <http://www.gnu.org/licenses/>.
 
28
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
28
29
 * 
29
30
 * Contact the authors at <mandos@recompile.se>.
30
31
 */
32
33
/* Needed by GPGME, specifically gpgme_data_seek() */
33
34
#ifndef _LARGEFILE_SOURCE
34
35
#define _LARGEFILE_SOURCE
35
 
#endif
 
36
#endif  /* not _LARGEFILE_SOURCE */
36
37
#ifndef _FILE_OFFSET_BITS
37
38
#define _FILE_OFFSET_BITS 64
38
 
#endif
 
39
#endif  /* not _FILE_OFFSET_BITS */
39
40
 
40
41
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
41
42
 
42
43
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
43
 
                                   stdout, ferror(), remove() */
 
44
                                   stdout, ferror() */
44
45
#include <stdint.h>             /* uint16_t, uint32_t, intptr_t */
45
46
#include <stddef.h>             /* NULL, size_t, ssize_t */
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() */
 
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,
73
79
                                */
74
80
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
75
81
                                   getuid(), getgid(), seteuid(),
76
 
                                   setgid(), pause(), _exit() */
 
82
                                   setgid(), pause(), _exit(),
 
83
                                   unlinkat() */
77
84
#include <arpa/inet.h>          /* inet_pton(), htons() */
78
85
#include <iso646.h>             /* not, or, and */
79
86
#include <argp.h>               /* struct argp_option, error_t, struct
141
148
static const char sys_class_net[] = "/sys/class/net";
142
149
char *connect_to = NULL;
143
150
const char *hookdir = HOOKDIR;
 
151
int hookdir_fd = -1;
144
152
uid_t uid = 65534;
145
153
gid_t gid = 65534;
146
154
 
232
240
                          .af = af };
233
241
  if(new_server->ip == NULL){
234
242
    perror_plus("strdup");
 
243
    free(new_server);
235
244
    return false;
236
245
  }
237
246
  ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
238
247
  if(ret == -1){
239
248
    perror_plus("clock_gettime");
 
249
#ifdef __GNUC__
 
250
#pragma GCC diagnostic push
 
251
#pragma GCC diagnostic ignored "-Wcast-qual"
 
252
#endif
 
253
    free((char *)(new_server->ip));
 
254
#ifdef __GNUC__
 
255
#pragma GCC diagnostic pop
 
256
#endif
 
257
    free(new_server);
240
258
    return false;
241
259
  }
242
260
  /* Special case of first server */
254
272
  return true;
255
273
}
256
274
 
 
275
/* Set effective uid to 0, return errno */
 
276
__attribute__((warn_unused_result))
 
277
int raise_privileges(void){
 
278
  int old_errno = errno;
 
279
  int ret = 0;
 
280
  if(seteuid(0) == -1){
 
281
    ret = errno;
 
282
  }
 
283
  errno = old_errno;
 
284
  return ret;
 
285
}
 
286
 
 
287
/* Set effective and real user ID to 0.  Return errno. */
 
288
__attribute__((warn_unused_result))
 
289
int raise_privileges_permanently(void){
 
290
  int old_errno = errno;
 
291
  int ret = raise_privileges();
 
292
  if(ret != 0){
 
293
    errno = old_errno;
 
294
    return ret;
 
295
  }
 
296
  if(setuid(0) == -1){
 
297
    ret = errno;
 
298
  }
 
299
  errno = old_errno;
 
300
  return ret;
 
301
}
 
302
 
 
303
/* Set effective user ID to unprivileged saved user ID */
 
304
__attribute__((warn_unused_result))
 
305
int lower_privileges(void){
 
306
  int old_errno = errno;
 
307
  int ret = 0;
 
308
  if(seteuid(uid) == -1){
 
309
    ret = errno;
 
310
  }
 
311
  errno = old_errno;
 
312
  return ret;
 
313
}
 
314
 
 
315
/* Lower privileges permanently */
 
316
__attribute__((warn_unused_result))
 
317
int lower_privileges_permanently(void){
 
318
  int old_errno = errno;
 
319
  int ret = 0;
 
320
  if(setuid(uid) == -1){
 
321
    ret = errno;
 
322
  }
 
323
  errno = old_errno;
 
324
  return ret;
 
325
}
 
326
 
257
327
/* 
258
328
 * Initialize GPGME.
259
329
 */
279
349
      return false;
280
350
    }
281
351
    
 
352
    /* Workaround for systems without a real-time clock; see also
 
353
       Debian bug #894495: <https://bugs.debian.org/894495> */
 
354
    do {
 
355
      {
 
356
        time_t currtime = time(NULL);
 
357
        if(currtime != (time_t)-1){
 
358
          struct tm tm;
 
359
          if(gmtime_r(&currtime, &tm) == NULL) {
 
360
            perror_plus("gmtime_r");
 
361
            break;
 
362
          }
 
363
          if(tm.tm_year != 70 or tm.tm_mon != 0){
 
364
            break;
 
365
          }
 
366
          if(debug){
 
367
            fprintf_plus(stderr, "System clock is January 1970");
 
368
          }
 
369
        } else {
 
370
          if(debug){
 
371
            fprintf_plus(stderr, "System clock is invalid");
 
372
          }
 
373
        }
 
374
      }
 
375
      struct stat keystat;
 
376
      ret = fstat(fd, &keystat);
 
377
      if(ret != 0){
 
378
        perror_plus("fstat");
 
379
        break;
 
380
      }
 
381
      ret = raise_privileges();
 
382
      if(ret != 0){
 
383
        errno = ret;
 
384
        perror_plus("Failed to raise privileges");
 
385
        break;
 
386
      }
 
387
      if(debug){
 
388
        fprintf_plus(stderr,
 
389
                     "Setting system clock to key file mtime");
 
390
      }
 
391
      time_t keytime = keystat.st_mtim.tv_sec;
 
392
      if(stime(&keytime) != 0){
 
393
        perror_plus("stime");
 
394
      }
 
395
      ret = lower_privileges();
 
396
      if(ret != 0){
 
397
        errno = ret;
 
398
        perror_plus("Failed to lower privileges");
 
399
      }
 
400
    } while(false);
 
401
 
282
402
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
283
403
    if(rc != GPG_ERR_NO_ERROR){
284
404
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
292
412
                   gpgme_strsource(rc), gpgme_strerror(rc));
293
413
      return false;
294
414
    }
 
415
    {
 
416
      gpgme_import_result_t import_result
 
417
        = gpgme_op_import_result(mc->ctx);
 
418
      if((import_result->imported < 1
 
419
          or import_result->not_imported > 0)
 
420
         and import_result->unchanged == 0){
 
421
        fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
 
422
        fprintf_plus(stderr,
 
423
                     "The total number of considered keys: %d\n",
 
424
                     import_result->considered);
 
425
        fprintf_plus(stderr,
 
426
                     "The number of keys without user ID: %d\n",
 
427
                     import_result->no_user_id);
 
428
        fprintf_plus(stderr,
 
429
                     "The total number of imported keys: %d\n",
 
430
                     import_result->imported);
 
431
        fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
 
432
                     import_result->imported_rsa);
 
433
        fprintf_plus(stderr, "The number of unchanged keys: %d\n",
 
434
                     import_result->unchanged);
 
435
        fprintf_plus(stderr, "The number of new user IDs: %d\n",
 
436
                     import_result->new_user_ids);
 
437
        fprintf_plus(stderr, "The number of new sub keys: %d\n",
 
438
                     import_result->new_sub_keys);
 
439
        fprintf_plus(stderr, "The number of new signatures: %d\n",
 
440
                     import_result->new_signatures);
 
441
        fprintf_plus(stderr, "The number of new revocations: %d\n",
 
442
                     import_result->new_revocations);
 
443
        fprintf_plus(stderr,
 
444
                     "The total number of secret keys read: %d\n",
 
445
                     import_result->secret_read);
 
446
        fprintf_plus(stderr,
 
447
                     "The number of imported secret keys: %d\n",
 
448
                     import_result->secret_imported);
 
449
        fprintf_plus(stderr,
 
450
                     "The number of unchanged secret keys: %d\n",
 
451
                     import_result->secret_unchanged);
 
452
        fprintf_plus(stderr, "The number of keys not imported: %d\n",
 
453
                     import_result->not_imported);
 
454
        for(gpgme_import_status_t import_status
 
455
              = import_result->imports;
 
456
            import_status != NULL;
 
457
            import_status = import_status->next){
 
458
          fprintf_plus(stderr, "Import status for key: %s\n",
 
459
                       import_status->fpr);
 
460
          if(import_status->result != GPG_ERR_NO_ERROR){
 
461
            fprintf_plus(stderr, "Import result: %s: %s\n",
 
462
                         gpgme_strsource(import_status->result),
 
463
                         gpgme_strerror(import_status->result));
 
464
          }
 
465
          fprintf_plus(stderr, "Key status:\n");
 
466
          fprintf_plus(stderr,
 
467
                       import_status->status & GPGME_IMPORT_NEW
 
468
                       ? "The key was new.\n"
 
469
                       : "The key was not new.\n");
 
470
          fprintf_plus(stderr,
 
471
                       import_status->status & GPGME_IMPORT_UID
 
472
                       ? "The key contained new user IDs.\n"
 
473
                       : "The key did not contain new user IDs.\n");
 
474
          fprintf_plus(stderr,
 
475
                       import_status->status & GPGME_IMPORT_SIG
 
476
                       ? "The key contained new signatures.\n"
 
477
                       : "The key did not contain new signatures.\n");
 
478
          fprintf_plus(stderr,
 
479
                       import_status->status & GPGME_IMPORT_SUBKEY
 
480
                       ? "The key contained new sub keys.\n"
 
481
                       : "The key did not contain new sub keys.\n");
 
482
          fprintf_plus(stderr,
 
483
                       import_status->status & GPGME_IMPORT_SECRET
 
484
                       ? "The key contained a secret key.\n"
 
485
                       : "The key did not contain a secret key.\n");
 
486
        }
 
487
        return false;
 
488
      }
 
489
    }
295
490
    
296
 
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
491
    ret = close(fd);
297
492
    if(ret == -1){
298
493
      perror_plus("close");
299
494
    }
338
533
  /* Create new GPGME "context" */
339
534
  rc = gpgme_new(&(mc->ctx));
340
535
  if(rc != GPG_ERR_NO_ERROR){
341
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
342
 
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
343
 
                 gpgme_strerror(rc));
 
536
    fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
 
537
                 gpgme_strsource(rc), gpgme_strerror(rc));
344
538
    return false;
345
539
  }
346
540
  
382
576
  /* Create new empty GPGME data buffer for the plaintext */
383
577
  rc = gpgme_data_new(&dh_plain);
384
578
  if(rc != GPG_ERR_NO_ERROR){
385
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
386
 
                 "bad gpgme_data_new: %s: %s\n",
 
579
    fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
387
580
                 gpgme_strsource(rc), gpgme_strerror(rc));
388
581
    gpgme_data_release(dh_crypto);
389
582
    return -1;
402
595
      if(result == NULL){
403
596
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
404
597
      } else {
405
 
        fprintf_plus(stderr, "Unsupported algorithm: %s\n",
406
 
                     result->unsupported_algorithm);
407
 
        fprintf_plus(stderr, "Wrong key usage: %u\n",
408
 
                     result->wrong_key_usage);
 
598
        if(result->unsupported_algorithm != NULL) {
 
599
          fprintf_plus(stderr, "Unsupported algorithm: %s\n",
 
600
                       result->unsupported_algorithm);
 
601
        }
 
602
        fprintf_plus(stderr, "Wrong key usage: %s\n",
 
603
                     result->wrong_key_usage ? "Yes" : "No");
409
604
        if(result->file_name != NULL){
410
605
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
411
606
        }
412
 
        gpgme_recipient_t recipient;
413
 
        recipient = result->recipients;
414
 
        while(recipient != NULL){
 
607
 
 
608
        for(gpgme_recipient_t r = result->recipients; r != NULL;
 
609
            r = r->next){
415
610
          fprintf_plus(stderr, "Public key algorithm: %s\n",
416
 
                       gpgme_pubkey_algo_name
417
 
                       (recipient->pubkey_algo));
418
 
          fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
 
611
                       gpgme_pubkey_algo_name(r->pubkey_algo));
 
612
          fprintf_plus(stderr, "Key ID: %s\n", r->keyid);
419
613
          fprintf_plus(stderr, "Secret key available: %s\n",
420
 
                       recipient->status == GPG_ERR_NO_SECKEY
421
 
                       ? "No" : "Yes");
422
 
          recipient = recipient->next;
 
614
                       r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
423
615
        }
424
616
      }
425
617
    }
481
673
  return plaintext_length;
482
674
}
483
675
 
 
676
__attribute__((warn_unused_result, const))
 
677
static const char *safe_string(const char *str){
 
678
  if(str == NULL)
 
679
    return "(unknown)";
 
680
  return str;
 
681
}
 
682
 
484
683
__attribute__((warn_unused_result))
485
684
static const char *safer_gnutls_strerror(int value){
486
685
  const char *ret = gnutls_strerror(value);
487
 
  if(ret == NULL)
488
 
    ret = "(unknown)";
489
 
  return ret;
 
686
  return safe_string(ret);
490
687
}
491
688
 
492
689
/* GnuTLS log function callback */
496
693
  fprintf_plus(stderr, "GnuTLS: %s", string);
497
694
}
498
695
 
499
 
__attribute__((nonnull, warn_unused_result))
 
696
__attribute__((nonnull(1, 2, 4), warn_unused_result))
500
697
static int init_gnutls_global(const char *pubkeyfilename,
501
698
                              const char *seckeyfilename,
 
699
                              const char *dhparamsfilename,
502
700
                              mandos_context *mc){
503
701
  int ret;
 
702
  unsigned int uret;
504
703
  
505
704
  if(debug){
506
705
    fprintf_plus(stderr, "Initializing GnuTLS\n");
507
706
  }
508
707
  
509
 
  ret = gnutls_global_init();
510
 
  if(ret != GNUTLS_E_SUCCESS){
511
 
    fprintf_plus(stderr, "GnuTLS global_init: %s\n",
512
 
                 safer_gnutls_strerror(ret));
513
 
    return -1;
514
 
  }
515
 
  
516
708
  if(debug){
517
709
    /* "Use a log level over 10 to enable all debugging options."
518
710
     * - GnuTLS manual
526
718
  if(ret != GNUTLS_E_SUCCESS){
527
719
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
528
720
                 safer_gnutls_strerror(ret));
529
 
    gnutls_global_deinit();
530
721
    return -1;
531
722
  }
532
723
  
557
748
                 safer_gnutls_strerror(ret));
558
749
    goto globalfail;
559
750
  }
560
 
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
561
 
  if(ret != GNUTLS_E_SUCCESS){
562
 
    fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
563
 
                 safer_gnutls_strerror(ret));
564
 
    goto globalfail;
565
 
  }
566
 
  
 
751
  /* If a Diffie-Hellman parameters file was given, try to use it */
 
752
  if(dhparamsfilename != NULL){
 
753
    gnutls_datum_t params = { .data = NULL, .size = 0 };
 
754
    do {
 
755
      int dhpfile = open(dhparamsfilename, O_RDONLY);
 
756
      if(dhpfile == -1){
 
757
        perror_plus("open");
 
758
        dhparamsfilename = NULL;
 
759
        break;
 
760
      }
 
761
      size_t params_capacity = 0;
 
762
      while(true){
 
763
        params_capacity = incbuffer((char **)&params.data,
 
764
                                    (size_t)params.size,
 
765
                                    (size_t)params_capacity);
 
766
        if(params_capacity == 0){
 
767
          perror_plus("incbuffer");
 
768
          free(params.data);
 
769
          params.data = NULL;
 
770
          dhparamsfilename = NULL;
 
771
          break;
 
772
        }
 
773
        ssize_t bytes_read = read(dhpfile,
 
774
                                  params.data + params.size,
 
775
                                  BUFFER_SIZE);
 
776
        /* EOF */
 
777
        if(bytes_read == 0){
 
778
          break;
 
779
        }
 
780
        /* check bytes_read for failure */
 
781
        if(bytes_read < 0){
 
782
          perror_plus("read");
 
783
          free(params.data);
 
784
          params.data = NULL;
 
785
          dhparamsfilename = NULL;
 
786
          break;
 
787
        }
 
788
        params.size += (unsigned int)bytes_read;
 
789
      }
 
790
      ret = close(dhpfile);
 
791
      if(ret == -1){
 
792
        perror_plus("close");
 
793
      }
 
794
      if(params.data == NULL){
 
795
        dhparamsfilename = NULL;
 
796
      }
 
797
      if(dhparamsfilename == NULL){
 
798
        break;
 
799
      }
 
800
      ret = gnutls_dh_params_import_pkcs3(mc->dh_params, &params,
 
801
                                          GNUTLS_X509_FMT_PEM);
 
802
      if(ret != GNUTLS_E_SUCCESS){
 
803
        fprintf_plus(stderr, "Failed to parse DH parameters in file"
 
804
                     " \"%s\": %s\n", dhparamsfilename,
 
805
                     safer_gnutls_strerror(ret));
 
806
        dhparamsfilename = NULL;
 
807
      }
 
808
      free(params.data);
 
809
    } while(false);
 
810
  }
 
811
  if(dhparamsfilename == NULL){
 
812
    if(mc->dh_bits == 0){
 
813
      /* Find out the optimal number of DH bits */
 
814
      /* Try to read the private key file */
 
815
      gnutls_datum_t buffer = { .data = NULL, .size = 0 };
 
816
      do {
 
817
        int secfile = open(seckeyfilename, O_RDONLY);
 
818
        if(secfile == -1){
 
819
          perror_plus("open");
 
820
          break;
 
821
        }
 
822
        size_t buffer_capacity = 0;
 
823
        while(true){
 
824
          buffer_capacity = incbuffer((char **)&buffer.data,
 
825
                                      (size_t)buffer.size,
 
826
                                      (size_t)buffer_capacity);
 
827
          if(buffer_capacity == 0){
 
828
            perror_plus("incbuffer");
 
829
            free(buffer.data);
 
830
            buffer.data = NULL;
 
831
            break;
 
832
          }
 
833
          ssize_t bytes_read = read(secfile,
 
834
                                    buffer.data + buffer.size,
 
835
                                    BUFFER_SIZE);
 
836
          /* EOF */
 
837
          if(bytes_read == 0){
 
838
            break;
 
839
          }
 
840
          /* check bytes_read for failure */
 
841
          if(bytes_read < 0){
 
842
            perror_plus("read");
 
843
            free(buffer.data);
 
844
            buffer.data = NULL;
 
845
            break;
 
846
          }
 
847
          buffer.size += (unsigned int)bytes_read;
 
848
        }
 
849
        close(secfile);
 
850
      } while(false);
 
851
      /* If successful, use buffer to parse private key */
 
852
      gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
853
      if(buffer.data != NULL){
 
854
        {
 
855
          gnutls_openpgp_privkey_t privkey = NULL;
 
856
          ret = gnutls_openpgp_privkey_init(&privkey);
 
857
          if(ret != GNUTLS_E_SUCCESS){
 
858
            fprintf_plus(stderr, "Error initializing OpenPGP key"
 
859
                         " structure: %s",
 
860
                         safer_gnutls_strerror(ret));
 
861
            free(buffer.data);
 
862
            buffer.data = NULL;
 
863
          } else {
 
864
            ret = gnutls_openpgp_privkey_import
 
865
              (privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
 
866
            if(ret != GNUTLS_E_SUCCESS){
 
867
              fprintf_plus(stderr, "Error importing OpenPGP key : %s",
 
868
                           safer_gnutls_strerror(ret));
 
869
              privkey = NULL;
 
870
            }
 
871
            free(buffer.data);
 
872
            buffer.data = NULL;
 
873
            if(privkey != NULL){
 
874
              /* Use private key to suggest an appropriate
 
875
                 sec_param */
 
876
              sec_param = gnutls_openpgp_privkey_sec_param(privkey);
 
877
              gnutls_openpgp_privkey_deinit(privkey);
 
878
              if(debug){
 
879
                fprintf_plus(stderr, "This OpenPGP key implies using"
 
880
                             " a GnuTLS security parameter \"%s\".\n",
 
881
                             safe_string(gnutls_sec_param_get_name
 
882
                                         (sec_param)));
 
883
              }
 
884
            }
 
885
          }
 
886
        }
 
887
        if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
 
888
          /* Err on the side of caution */
 
889
          sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
890
          if(debug){
 
891
            fprintf_plus(stderr, "Falling back to security parameter"
 
892
                         " \"%s\"\n",
 
893
                         safe_string(gnutls_sec_param_get_name
 
894
                                     (sec_param)));
 
895
          }
 
896
        }
 
897
      }
 
898
      uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
 
899
      if(uret != 0){
 
900
        mc->dh_bits = uret;
 
901
        if(debug){
 
902
          fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
 
903
                       " implies %u DH bits; using that.\n",
 
904
                       safe_string(gnutls_sec_param_get_name
 
905
                                   (sec_param)),
 
906
                       mc->dh_bits);
 
907
        }
 
908
      } else {
 
909
        fprintf_plus(stderr, "Failed to get implied number of DH"
 
910
                     " bits for security parameter \"%s\"): %s\n",
 
911
                     safe_string(gnutls_sec_param_get_name
 
912
                                 (sec_param)),
 
913
                     safer_gnutls_strerror(ret));
 
914
        goto globalfail;
 
915
      }
 
916
    } else if(debug){
 
917
      fprintf_plus(stderr, "DH bits explicitly set to %u\n",
 
918
                   mc->dh_bits);
 
919
    }
 
920
    ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
 
921
    if(ret != GNUTLS_E_SUCCESS){
 
922
      fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
 
923
                   " bits): %s\n", mc->dh_bits,
 
924
                   safer_gnutls_strerror(ret));
 
925
      goto globalfail;
 
926
    }
 
927
  }
567
928
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
568
929
  
569
930
  return 0;
571
932
 globalfail:
572
933
  
573
934
  gnutls_certificate_free_credentials(mc->cred);
574
 
  gnutls_global_deinit();
575
935
  gnutls_dh_params_deinit(mc->dh_params);
576
936
  return -1;
577
937
}
629
989
  /* ignore client certificate if any. */
630
990
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
631
991
  
632
 
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
633
 
  
634
992
  return 0;
635
993
}
636
994
 
638
996
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
639
997
                      __attribute__((unused)) const char *txt){}
640
998
 
 
999
/* Helper function to add_local_route() and delete_local_route() */
 
1000
__attribute__((nonnull, warn_unused_result))
 
1001
static bool add_delete_local_route(const bool add,
 
1002
                                   const char *address,
 
1003
                                   AvahiIfIndex if_index){
 
1004
  int ret;
 
1005
  char helper[] = "mandos-client-iprouteadddel";
 
1006
  char add_arg[] = "add";
 
1007
  char delete_arg[] = "delete";
 
1008
  char debug_flag[] = "--debug";
 
1009
  char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
 
1010
  if(pluginhelperdir == NULL){
 
1011
    if(debug){
 
1012
      fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
 
1013
                   " variable not set; cannot run helper\n");
 
1014
    }
 
1015
    return false;
 
1016
  }
 
1017
  
 
1018
  char interface[IF_NAMESIZE];
 
1019
  if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
1020
    perror_plus("if_indextoname");
 
1021
    return false;
 
1022
  }
 
1023
  
 
1024
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
1025
  if(devnull == -1){
 
1026
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
1027
    return false;
 
1028
  }
 
1029
  pid_t pid = fork();
 
1030
  if(pid == 0){
 
1031
    /* Child */
 
1032
    /* Raise privileges */
 
1033
    errno = raise_privileges_permanently();
 
1034
    if(errno != 0){
 
1035
      perror_plus("Failed to raise privileges");
 
1036
      /* _exit(EX_NOPERM); */
 
1037
    } else {
 
1038
      /* Set group */
 
1039
      errno = 0;
 
1040
      ret = setgid(0);
 
1041
      if(ret == -1){
 
1042
        perror_plus("setgid");
 
1043
        _exit(EX_NOPERM);
 
1044
      }
 
1045
      /* Reset supplementary groups */
 
1046
      errno = 0;
 
1047
      ret = setgroups(0, NULL);
 
1048
      if(ret == -1){
 
1049
        perror_plus("setgroups");
 
1050
        _exit(EX_NOPERM);
 
1051
      }
 
1052
    }
 
1053
    ret = dup2(devnull, STDIN_FILENO);
 
1054
    if(ret == -1){
 
1055
      perror_plus("dup2(devnull, STDIN_FILENO)");
 
1056
      _exit(EX_OSERR);
 
1057
    }
 
1058
    ret = close(devnull);
 
1059
    if(ret == -1){
 
1060
      perror_plus("close");
 
1061
      _exit(EX_OSERR);
 
1062
    }
 
1063
    ret = dup2(STDERR_FILENO, STDOUT_FILENO);
 
1064
    if(ret == -1){
 
1065
      perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
 
1066
      _exit(EX_OSERR);
 
1067
    }
 
1068
    int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
 
1069
                                                    O_RDONLY
 
1070
                                                    | O_DIRECTORY
 
1071
                                                    | O_PATH
 
1072
                                                    | O_CLOEXEC));
 
1073
    if(helperdir_fd == -1){
 
1074
      perror_plus("open");
 
1075
      _exit(EX_UNAVAILABLE);
 
1076
    }
 
1077
    int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
 
1078
                                                   helper, O_RDONLY));
 
1079
    if(helper_fd == -1){
 
1080
      perror_plus("openat");
 
1081
      close(helperdir_fd);
 
1082
      _exit(EX_UNAVAILABLE);
 
1083
    }
 
1084
    close(helperdir_fd);
 
1085
#ifdef __GNUC__
 
1086
#pragma GCC diagnostic push
 
1087
#pragma GCC diagnostic ignored "-Wcast-qual"
 
1088
#endif
 
1089
    if(fexecve(helper_fd, (char *const [])
 
1090
               { helper, add ? add_arg : delete_arg, (char *)address,
 
1091
                   interface, debug ? debug_flag : NULL, NULL },
 
1092
               environ) == -1){
 
1093
#ifdef __GNUC__
 
1094
#pragma GCC diagnostic pop
 
1095
#endif
 
1096
      perror_plus("fexecve");
 
1097
      _exit(EXIT_FAILURE);
 
1098
    }
 
1099
  }
 
1100
  if(pid == -1){
 
1101
    perror_plus("fork");
 
1102
    return false;
 
1103
  }
 
1104
  int status;
 
1105
  pid_t pret = -1;
 
1106
  errno = 0;
 
1107
  do {
 
1108
    pret = waitpid(pid, &status, 0);
 
1109
    if(pret == -1 and errno == EINTR and quit_now){
 
1110
      int errno_raising = 0;
 
1111
      if((errno = raise_privileges()) != 0){
 
1112
        errno_raising = errno;
 
1113
        perror_plus("Failed to raise privileges in order to"
 
1114
                    " kill helper program");
 
1115
      }
 
1116
      if(kill(pid, SIGTERM) == -1){
 
1117
        perror_plus("kill");
 
1118
      }
 
1119
      if((errno_raising == 0) and (errno = lower_privileges()) != 0){
 
1120
        perror_plus("Failed to lower privileges after killing"
 
1121
                    " helper program");
 
1122
      }
 
1123
      return false;
 
1124
    }
 
1125
  } while(pret == -1 and errno == EINTR);
 
1126
  if(pret == -1){
 
1127
    perror_plus("waitpid");
 
1128
    return false;
 
1129
  }
 
1130
  if(WIFEXITED(status)){
 
1131
    if(WEXITSTATUS(status) != 0){
 
1132
      fprintf_plus(stderr, "Error: iprouteadddel exited"
 
1133
                   " with status %d\n", WEXITSTATUS(status));
 
1134
      return false;
 
1135
    }
 
1136
    return true;
 
1137
  }
 
1138
  if(WIFSIGNALED(status)){
 
1139
    fprintf_plus(stderr, "Error: iprouteadddel died by"
 
1140
                 " signal %d\n", WTERMSIG(status));
 
1141
    return false;
 
1142
  }
 
1143
  fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
 
1144
  return false;
 
1145
}
 
1146
 
 
1147
__attribute__((nonnull, warn_unused_result))
 
1148
static bool add_local_route(const char *address,
 
1149
                            AvahiIfIndex if_index){
 
1150
  if(debug){
 
1151
    fprintf_plus(stderr, "Adding route to %s\n", address);
 
1152
  }
 
1153
  return add_delete_local_route(true, address, if_index);
 
1154
}
 
1155
 
 
1156
__attribute__((nonnull, warn_unused_result))
 
1157
static bool delete_local_route(const char *address,
 
1158
                               AvahiIfIndex if_index){
 
1159
  if(debug){
 
1160
    fprintf_plus(stderr, "Removing route to %s\n", address);
 
1161
  }
 
1162
  return add_delete_local_route(false, address, if_index);
 
1163
}
 
1164
 
641
1165
/* Called when a Mandos server is found */
642
1166
__attribute__((nonnull, warn_unused_result))
643
1167
static int start_mandos_communication(const char *ip, in_port_t port,
654
1178
  int retval = -1;
655
1179
  gnutls_session_t session;
656
1180
  int pf;                       /* Protocol family */
 
1181
  bool route_added = false;
657
1182
  
658
1183
  errno = 0;
659
1184
  
681
1206
    bool match = false;
682
1207
    {
683
1208
      char *interface = NULL;
684
 
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
685
 
                                 interface))){
 
1209
      while((interface = argz_next(mc->interfaces,
 
1210
                                   mc->interfaces_size,
 
1211
                                   interface))){
686
1212
        if(if_nametoindex(interface) == (unsigned int)if_index){
687
1213
          match = true;
688
1214
          break;
717
1243
                 PRIuMAX "\n", ip, (uintmax_t)port);
718
1244
  }
719
1245
  
720
 
  tcp_sd = socket(pf, SOCK_STREAM, 0);
 
1246
  tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
721
1247
  if(tcp_sd < 0){
722
1248
    int e = errno;
723
1249
    perror_plus("socket");
730
1256
    goto mandos_end;
731
1257
  }
732
1258
  
733
 
  memset(&to, 0, sizeof(to));
734
1259
  if(af == AF_INET6){
735
 
    ((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
736
 
    ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
 
1260
    struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
 
1261
    *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
 
1262
    ret = inet_pton(af, ip, &to6->sin6_addr);
737
1263
  } else {                      /* IPv4 */
738
 
    ((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
739
 
    ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
 
1264
    struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
 
1265
    *to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
 
1266
    ret = inet_pton(af, ip, &to4->sin_addr);
740
1267
  }
741
1268
  if(ret < 0 ){
742
1269
    int e = errno;
812
1339
    goto mandos_end;
813
1340
  }
814
1341
  
815
 
  if(af == AF_INET6){
816
 
    ret = connect(tcp_sd, (struct sockaddr *)&to,
817
 
                  sizeof(struct sockaddr_in6));
818
 
  } else {
819
 
    ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
820
 
                  sizeof(struct sockaddr_in));
821
 
  }
822
 
  if(ret < 0){
823
 
    if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
824
 
      int e = errno;
825
 
      perror_plus("connect");
826
 
      errno = e;
827
 
    }
828
 
    goto mandos_end;
829
 
  }
830
 
  
831
 
  if(quit_now){
832
 
    errno = EINTR;
833
 
    goto mandos_end;
 
1342
  while(true){
 
1343
    if(af == AF_INET6){
 
1344
      ret = connect(tcp_sd, (struct sockaddr *)&to,
 
1345
                    sizeof(struct sockaddr_in6));
 
1346
    } else {
 
1347
      ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
 
1348
                    sizeof(struct sockaddr_in));
 
1349
    }
 
1350
    if(ret < 0){
 
1351
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
 
1352
         and if_index != AVAHI_IF_UNSPEC
 
1353
         and connect_to == NULL
 
1354
         and not route_added and
 
1355
         ((af == AF_INET6 and not
 
1356
           IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
 
1357
                                    &to)->sin6_addr)))
 
1358
          or (af == AF_INET and
 
1359
              /* Not a a IPv4LL address */
 
1360
              (ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
 
1361
               & 0xFFFF0000L) != 0xA9FE0000L))){
 
1362
        /* Work around Avahi bug - Avahi does not announce link-local
 
1363
           addresses if it has a global address, so local hosts with
 
1364
           *only* a link-local address (e.g. Mandos clients) cannot
 
1365
           connect to a Mandos server announced by Avahi on a server
 
1366
           host with a global address.  Work around this by retrying
 
1367
           with an explicit route added with the server's address.
 
1368
           
 
1369
           Avahi bug reference:
 
1370
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1371
           https://bugs.debian.org/587961
 
1372
        */
 
1373
        if(debug){
 
1374
          fprintf_plus(stderr, "Mandos server unreachable, trying"
 
1375
                       " direct route\n");
 
1376
        }
 
1377
        int e = errno;
 
1378
        route_added = add_local_route(ip, if_index);
 
1379
        if(route_added){
 
1380
          continue;
 
1381
        }
 
1382
        errno = e;
 
1383
      }
 
1384
      if(errno != ECONNREFUSED or debug){
 
1385
        int e = errno;
 
1386
        perror_plus("connect");
 
1387
        errno = e;
 
1388
      }
 
1389
      goto mandos_end;
 
1390
    }
 
1391
    
 
1392
    if(quit_now){
 
1393
      errno = EINTR;
 
1394
      goto mandos_end;
 
1395
    }
 
1396
    break;
834
1397
  }
835
1398
  
836
1399
  const char *out = mandos_protocol_version;
990
1553
                                               &decrypted_buffer, mc);
991
1554
    if(decrypted_buffer_size >= 0){
992
1555
      
 
1556
      clearerr(stdout);
993
1557
      written = 0;
994
1558
      while(written < (size_t) decrypted_buffer_size){
995
1559
        if(quit_now){
1011
1575
        }
1012
1576
        written += (size_t)ret;
1013
1577
      }
 
1578
      ret = fflush(stdout);
 
1579
      if(ret != 0){
 
1580
        int e = errno;
 
1581
        if(debug){
 
1582
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
 
1583
                       strerror(errno));
 
1584
        }
 
1585
        errno = e;
 
1586
        goto mandos_end;
 
1587
      }
1014
1588
      retval = 0;
1015
1589
    }
1016
1590
  }
1019
1593
  
1020
1594
 mandos_end:
1021
1595
  {
 
1596
    if(route_added){
 
1597
      if(not delete_local_route(ip, if_index)){
 
1598
        fprintf_plus(stderr, "Failed to delete local route to %s on"
 
1599
                     " interface %d", ip, if_index);
 
1600
      }
 
1601
    }
1022
1602
    int e = errno;
1023
1603
    free(decrypted_buffer);
1024
1604
    free(buffer);
1025
1605
    if(tcp_sd >= 0){
1026
 
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
1606
      ret = close(tcp_sd);
1027
1607
    }
1028
1608
    if(ret == -1){
1029
1609
      if(e == 0){
1041
1621
  return retval;
1042
1622
}
1043
1623
 
1044
 
__attribute__((nonnull))
1045
1624
static void resolve_callback(AvahiSServiceResolver *r,
1046
1625
                             AvahiIfIndex interface,
1047
1626
                             AvahiProtocol proto,
1064
1643
     timed out */
1065
1644
  
1066
1645
  if(quit_now){
 
1646
    avahi_s_service_resolver_free(r);
1067
1647
    return;
1068
1648
  }
1069
1649
  
1183
1763
__attribute__((nonnull, warn_unused_result))
1184
1764
bool get_flags(const char *ifname, struct ifreq *ifr){
1185
1765
  int ret;
1186
 
  error_t ret_errno;
 
1766
  int old_errno;
1187
1767
  
1188
1768
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1189
1769
  if(s < 0){
1190
 
    ret_errno = errno;
 
1770
    old_errno = errno;
1191
1771
    perror_plus("socket");
1192
 
    errno = ret_errno;
 
1772
    errno = old_errno;
1193
1773
    return false;
1194
1774
  }
1195
 
  strcpy(ifr->ifr_name, ifname);
 
1775
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
 
1776
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1196
1777
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1197
1778
  if(ret == -1){
1198
1779
    if(debug){
1199
 
      ret_errno = errno;
 
1780
      old_errno = errno;
1200
1781
      perror_plus("ioctl SIOCGIFFLAGS");
1201
 
      errno = ret_errno;
 
1782
      errno = old_errno;
 
1783
    }
 
1784
    if((close(s) == -1) and debug){
 
1785
      old_errno = errno;
 
1786
      perror_plus("close");
 
1787
      errno = old_errno;
1202
1788
    }
1203
1789
    return false;
1204
1790
  }
 
1791
  if((close(s) == -1) and debug){
 
1792
    old_errno = errno;
 
1793
    perror_plus("close");
 
1794
    errno = old_errno;
 
1795
  }
1205
1796
  return true;
1206
1797
}
1207
1798
 
1335
1926
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1336
1927
                "abcdefghijklmnopqrstuvwxyz"
1337
1928
                "0123456789"
1338
 
                "_-");
 
1929
                "_.-");
1339
1930
  if((direntry->d_name)[sret] != '\0'){
1340
1931
    /* Contains non-allowed characters */
1341
1932
    if(debug){
1345
1936
    return 0;
1346
1937
  }
1347
1938
  
1348
 
  char *fullname = NULL;
1349
 
  ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1350
 
  if(ret < 0){
1351
 
    perror_plus("asprintf");
1352
 
    return 0;
1353
 
  }
1354
 
  
1355
 
  ret = stat(fullname, &st);
 
1939
  ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1356
1940
  if(ret == -1){
1357
1941
    if(debug){
1358
1942
      perror_plus("Could not stat hook");
1456
2040
  }
1457
2041
}
1458
2042
 
1459
 
/* Set effective uid to 0, return errno */
1460
 
__attribute__((warn_unused_result))
1461
 
error_t raise_privileges(void){
1462
 
  error_t old_errno = errno;
1463
 
  error_t ret_errno = 0;
1464
 
  if(seteuid(0) == -1){
1465
 
    ret_errno = errno;
1466
 
    perror_plus("seteuid");
1467
 
  }
1468
 
  errno = old_errno;
1469
 
  return ret_errno;
1470
 
}
1471
 
 
1472
 
/* Set effective and real user ID to 0.  Return errno. */
1473
 
__attribute__((warn_unused_result))
1474
 
error_t raise_privileges_permanently(void){
1475
 
  error_t old_errno = errno;
1476
 
  error_t ret_errno = raise_privileges();
1477
 
  if(ret_errno != 0){
1478
 
    errno = old_errno;
1479
 
    return ret_errno;
1480
 
  }
1481
 
  if(setuid(0) == -1){
1482
 
    ret_errno = errno;
1483
 
    perror_plus("seteuid");
1484
 
  }
1485
 
  errno = old_errno;
1486
 
  return ret_errno;
1487
 
}
1488
 
 
1489
 
/* Set effective user ID to unprivileged saved user ID */
1490
 
__attribute__((warn_unused_result))
1491
 
error_t lower_privileges(void){
1492
 
  error_t old_errno = errno;
1493
 
  error_t ret_errno = 0;
1494
 
  if(seteuid(uid) == -1){
1495
 
    ret_errno = errno;
1496
 
    perror_plus("seteuid");
1497
 
  }
1498
 
  errno = old_errno;
1499
 
  return ret_errno;
1500
 
}
1501
 
 
1502
 
/* Lower privileges permanently */
1503
 
__attribute__((warn_unused_result))
1504
 
error_t lower_privileges_permanently(void){
1505
 
  error_t old_errno = errno;
1506
 
  error_t ret_errno = 0;
1507
 
  if(setuid(uid) == -1){
1508
 
    ret_errno = errno;
1509
 
    perror_plus("setuid");
1510
 
  }
1511
 
  errno = old_errno;
1512
 
  return ret_errno;
1513
 
}
1514
 
 
1515
2043
__attribute__((nonnull))
1516
2044
void run_network_hooks(const char *mode, const char *interface,
1517
2045
                       const float delay){
1518
 
  struct dirent **direntries;
1519
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1520
 
                         alphasort);
 
2046
  struct dirent **direntries = NULL;
 
2047
  if(hookdir_fd == -1){
 
2048
    hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
 
2049
                      | O_CLOEXEC);
 
2050
    if(hookdir_fd == -1){
 
2051
      if(errno == ENOENT){
 
2052
        if(debug){
 
2053
          fprintf_plus(stderr, "Network hook directory \"%s\" not"
 
2054
                       " found\n", hookdir);
 
2055
        }
 
2056
      } else {
 
2057
        perror_plus("open");
 
2058
      }
 
2059
      return;
 
2060
    }
 
2061
  }
 
2062
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
2063
  if(devnull == -1){
 
2064
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
2065
    return;
 
2066
  }
 
2067
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
 
2068
                           runnable_hook, alphasort);
1521
2069
  if(numhooks == -1){
1522
 
    if(errno == ENOENT){
1523
 
      if(debug){
1524
 
        fprintf_plus(stderr, "Network hook directory \"%s\" not"
1525
 
                     " found\n", hookdir);
1526
 
      }
1527
 
    } else {
1528
 
      perror_plus("scandir");
 
2070
    perror_plus("scandir");
 
2071
    close(devnull);
 
2072
    return;
 
2073
  }
 
2074
  struct dirent *direntry;
 
2075
  int ret;
 
2076
  for(int i = 0; i < numhooks; i++){
 
2077
    direntry = direntries[i];
 
2078
    if(debug){
 
2079
      fprintf_plus(stderr, "Running network hook \"%s\"\n",
 
2080
                   direntry->d_name);
1529
2081
    }
1530
 
  } else {
1531
 
    struct dirent *direntry;
1532
 
    int ret;
1533
 
    int devnull = open("/dev/null", O_RDONLY);
1534
 
    for(int i = 0; i < numhooks; i++){
1535
 
      direntry = direntries[i];
1536
 
      char *fullname = NULL;
1537
 
      ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1538
 
      if(ret < 0){
 
2082
    pid_t hook_pid = fork();
 
2083
    if(hook_pid == 0){
 
2084
      /* Child */
 
2085
      /* Raise privileges */
 
2086
      errno = raise_privileges_permanently();
 
2087
      if(errno != 0){
 
2088
        perror_plus("Failed to raise privileges");
 
2089
        _exit(EX_NOPERM);
 
2090
      }
 
2091
      /* Set group */
 
2092
      errno = 0;
 
2093
      ret = setgid(0);
 
2094
      if(ret == -1){
 
2095
        perror_plus("setgid");
 
2096
        _exit(EX_NOPERM);
 
2097
      }
 
2098
      /* Reset supplementary groups */
 
2099
      errno = 0;
 
2100
      ret = setgroups(0, NULL);
 
2101
      if(ret == -1){
 
2102
        perror_plus("setgroups");
 
2103
        _exit(EX_NOPERM);
 
2104
      }
 
2105
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
 
2106
      if(ret == -1){
 
2107
        perror_plus("setenv");
 
2108
        _exit(EX_OSERR);
 
2109
      }
 
2110
      ret = setenv("DEVICE", interface, 1);
 
2111
      if(ret == -1){
 
2112
        perror_plus("setenv");
 
2113
        _exit(EX_OSERR);
 
2114
      }
 
2115
      ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
 
2116
      if(ret == -1){
 
2117
        perror_plus("setenv");
 
2118
        _exit(EX_OSERR);
 
2119
      }
 
2120
      ret = setenv("MODE", mode, 1);
 
2121
      if(ret == -1){
 
2122
        perror_plus("setenv");
 
2123
        _exit(EX_OSERR);
 
2124
      }
 
2125
      char *delaystring;
 
2126
      ret = asprintf(&delaystring, "%f", (double)delay);
 
2127
      if(ret == -1){
1539
2128
        perror_plus("asprintf");
1540
 
        continue;
1541
 
      }
1542
 
      if(debug){
1543
 
        fprintf_plus(stderr, "Running network hook \"%s\"\n",
1544
 
                     direntry->d_name);
1545
 
      }
1546
 
      pid_t hook_pid = fork();
1547
 
      if(hook_pid == 0){
1548
 
        /* Child */
1549
 
        /* Raise privileges */
1550
 
        if(raise_privileges_permanently() != 0){
1551
 
          perror_plus("Failed to raise privileges");
1552
 
          _exit(EX_NOPERM);
1553
 
        }
1554
 
        /* Set group */
1555
 
        errno = 0;
1556
 
        ret = setgid(0);
1557
 
        if(ret == -1){
1558
 
          perror_plus("setgid");
1559
 
          _exit(EX_NOPERM);
1560
 
        }
1561
 
        /* Reset supplementary groups */
1562
 
        errno = 0;
1563
 
        ret = setgroups(0, NULL);
1564
 
        if(ret == -1){
1565
 
          perror_plus("setgroups");
1566
 
          _exit(EX_NOPERM);
1567
 
        }
1568
 
        ret = dup2(devnull, STDIN_FILENO);
1569
 
        if(ret == -1){
1570
 
          perror_plus("dup2(devnull, STDIN_FILENO)");
1571
 
          _exit(EX_OSERR);
1572
 
        }
1573
 
        ret = close(devnull);
1574
 
        if(ret == -1){
1575
 
          perror_plus("close");
1576
 
          _exit(EX_OSERR);
1577
 
        }
1578
 
        ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1579
 
        if(ret == -1){
1580
 
          perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1581
 
          _exit(EX_OSERR);
1582
 
        }
1583
 
        ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1584
 
        if(ret == -1){
1585
 
          perror_plus("setenv");
1586
 
          _exit(EX_OSERR);
1587
 
        }
1588
 
        ret = setenv("DEVICE", interface, 1);
1589
 
        if(ret == -1){
1590
 
          perror_plus("setenv");
1591
 
          _exit(EX_OSERR);
1592
 
        }
1593
 
        ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1594
 
        if(ret == -1){
1595
 
          perror_plus("setenv");
1596
 
          _exit(EX_OSERR);
1597
 
        }
1598
 
        ret = setenv("MODE", mode, 1);
1599
 
        if(ret == -1){
1600
 
          perror_plus("setenv");
1601
 
          _exit(EX_OSERR);
1602
 
        }
1603
 
        char *delaystring;
1604
 
        ret = asprintf(&delaystring, "%f", (double)delay);
1605
 
        if(ret == -1){
1606
 
          perror_plus("asprintf");
1607
 
          _exit(EX_OSERR);
1608
 
        }
1609
 
        ret = setenv("DELAY", delaystring, 1);
1610
 
        if(ret == -1){
1611
 
          free(delaystring);
1612
 
          perror_plus("setenv");
1613
 
          _exit(EX_OSERR);
1614
 
        }
 
2129
        _exit(EX_OSERR);
 
2130
      }
 
2131
      ret = setenv("DELAY", delaystring, 1);
 
2132
      if(ret == -1){
1615
2133
        free(delaystring);
1616
 
        if(connect_to != NULL){
1617
 
          ret = setenv("CONNECT", connect_to, 1);
1618
 
          if(ret == -1){
1619
 
            perror_plus("setenv");
1620
 
            _exit(EX_OSERR);
1621
 
          }
1622
 
        }
1623
 
        if(execl(fullname, direntry->d_name, mode, NULL) == -1){
1624
 
          perror_plus("execl");
1625
 
          _exit(EXIT_FAILURE);
1626
 
        }
 
2134
        perror_plus("setenv");
 
2135
        _exit(EX_OSERR);
 
2136
      }
 
2137
      free(delaystring);
 
2138
      if(connect_to != NULL){
 
2139
        ret = setenv("CONNECT", connect_to, 1);
 
2140
        if(ret == -1){
 
2141
          perror_plus("setenv");
 
2142
          _exit(EX_OSERR);
 
2143
        }
 
2144
      }
 
2145
      int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
 
2146
                                                   direntry->d_name,
 
2147
                                                   O_RDONLY));
 
2148
      if(hook_fd == -1){
 
2149
        perror_plus("openat");
 
2150
        _exit(EXIT_FAILURE);
 
2151
      }
 
2152
      if(close(hookdir_fd) == -1){
 
2153
        perror_plus("close");
 
2154
        _exit(EXIT_FAILURE);
 
2155
      }
 
2156
      ret = dup2(devnull, STDIN_FILENO);
 
2157
      if(ret == -1){
 
2158
        perror_plus("dup2(devnull, STDIN_FILENO)");
 
2159
        _exit(EX_OSERR);
 
2160
      }
 
2161
      ret = close(devnull);
 
2162
      if(ret == -1){
 
2163
        perror_plus("close");
 
2164
        _exit(EX_OSERR);
 
2165
      }
 
2166
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
 
2167
      if(ret == -1){
 
2168
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
 
2169
        _exit(EX_OSERR);
 
2170
      }
 
2171
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
 
2172
                 environ) == -1){
 
2173
        perror_plus("fexecve");
 
2174
        _exit(EXIT_FAILURE);
 
2175
      }
 
2176
    } else {
 
2177
      if(hook_pid == -1){
 
2178
        perror_plus("fork");
 
2179
        free(direntry);
 
2180
        continue;
 
2181
      }
 
2182
      int status;
 
2183
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
 
2184
        perror_plus("waitpid");
 
2185
        free(direntry);
 
2186
        continue;
 
2187
      }
 
2188
      if(WIFEXITED(status)){
 
2189
        if(WEXITSTATUS(status) != 0){
 
2190
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
 
2191
                       " with status %d\n", direntry->d_name,
 
2192
                       WEXITSTATUS(status));
 
2193
          free(direntry);
 
2194
          continue;
 
2195
        }
 
2196
      } else if(WIFSIGNALED(status)){
 
2197
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
 
2198
                     " signal %d\n", direntry->d_name,
 
2199
                     WTERMSIG(status));
 
2200
        free(direntry);
 
2201
        continue;
1627
2202
      } else {
1628
 
        int status;
1629
 
        if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1630
 
          perror_plus("waitpid");
1631
 
          free(fullname);
1632
 
          continue;
1633
 
        }
1634
 
        if(WIFEXITED(status)){
1635
 
          if(WEXITSTATUS(status) != 0){
1636
 
            fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1637
 
                         " with status %d\n", direntry->d_name,
1638
 
                         WEXITSTATUS(status));
1639
 
            free(fullname);
1640
 
            continue;
1641
 
          }
1642
 
        } else if(WIFSIGNALED(status)){
1643
 
          fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1644
 
                       " signal %d\n", direntry->d_name,
1645
 
                       WTERMSIG(status));
1646
 
          free(fullname);
1647
 
          continue;
1648
 
        } else {
1649
 
          fprintf_plus(stderr, "Warning: network hook \"%s\""
1650
 
                       " crashed\n", direntry->d_name);
1651
 
          free(fullname);
1652
 
          continue;
1653
 
        }
1654
 
      }
1655
 
      free(fullname);
1656
 
      if(debug){
1657
 
        fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1658
 
                     direntry->d_name);
1659
 
      }
1660
 
    }
1661
 
    close(devnull);
1662
 
  }
 
2203
        fprintf_plus(stderr, "Warning: network hook \"%s\""
 
2204
                     " crashed\n", direntry->d_name);
 
2205
        free(direntry);
 
2206
        continue;
 
2207
      }
 
2208
    }
 
2209
    if(debug){
 
2210
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
 
2211
                   direntry->d_name);
 
2212
    }
 
2213
    free(direntry);
 
2214
  }
 
2215
  free(direntries);
 
2216
  if(close(hookdir_fd) == -1){
 
2217
    perror_plus("close");
 
2218
  } else {
 
2219
    hookdir_fd = -1;
 
2220
  }
 
2221
  close(devnull);
1663
2222
}
1664
2223
 
1665
2224
__attribute__((nonnull, warn_unused_result))
1666
 
error_t bring_up_interface(const char *const interface,
1667
 
                           const float delay){
1668
 
  error_t old_errno = errno;
 
2225
int bring_up_interface(const char *const interface,
 
2226
                       const float delay){
 
2227
  int old_errno = errno;
1669
2228
  int ret;
1670
2229
  struct ifreq network;
1671
2230
  unsigned int if_index = if_nametoindex(interface);
1681
2240
  }
1682
2241
  
1683
2242
  if(not interface_is_up(interface)){
1684
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2243
    int ret_errno = 0;
 
2244
    int ioctl_errno = 0;
1685
2245
    if(not get_flags(interface, &network)){
1686
2246
      ret_errno = errno;
1687
2247
      fprintf_plus(stderr, "Failed to get flags for interface "
1700
2260
    }
1701
2261
    
1702
2262
    if(quit_now){
1703
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2263
      ret = close(sd);
1704
2264
      if(ret == -1){
1705
2265
        perror_plus("close");
1706
2266
      }
1716
2276
    /* Raise privileges */
1717
2277
    ret_errno = raise_privileges();
1718
2278
    if(ret_errno != 0){
 
2279
      errno = ret_errno;
1719
2280
      perror_plus("Failed to raise privileges");
1720
2281
    }
1721
2282
    
1755
2316
    }
1756
2317
    
1757
2318
    /* Close the socket */
1758
 
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2319
    ret = close(sd);
1759
2320
    if(ret == -1){
1760
2321
      perror_plus("close");
1761
2322
    }
1773
2334
  
1774
2335
  /* Sleep checking until interface is running.
1775
2336
     Check every 0.25s, up to total time of delay */
1776
 
  for(int i=0; i < delay * 4; i++){
 
2337
  for(int i = 0; i < delay * 4; i++){
1777
2338
    if(interface_is_running(interface)){
1778
2339
      break;
1779
2340
    }
1789
2350
}
1790
2351
 
1791
2352
__attribute__((nonnull, warn_unused_result))
1792
 
error_t take_down_interface(const char *const interface){
1793
 
  error_t old_errno = errno;
 
2353
int take_down_interface(const char *const interface){
 
2354
  int old_errno = errno;
1794
2355
  struct ifreq network;
1795
2356
  unsigned int if_index = if_nametoindex(interface);
1796
2357
  if(if_index == 0){
1799
2360
    return ENXIO;
1800
2361
  }
1801
2362
  if(interface_is_up(interface)){
1802
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2363
    int ret_errno = 0;
 
2364
    int ioctl_errno = 0;
1803
2365
    if(not get_flags(interface, &network) and debug){
1804
2366
      ret_errno = errno;
1805
2367
      fprintf_plus(stderr, "Failed to get flags for interface "
1825
2387
    /* Raise privileges */
1826
2388
    ret_errno = raise_privileges();
1827
2389
    if(ret_errno != 0){
 
2390
      errno = ret_errno;
1828
2391
      perror_plus("Failed to raise privileges");
1829
2392
    }
1830
2393
    
1842
2405
    }
1843
2406
    
1844
2407
    /* Close the socket */
1845
 
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2408
    int ret = close(sd);
1846
2409
    if(ret == -1){
1847
2410
      perror_plus("close");
1848
2411
    }
1863
2426
}
1864
2427
 
1865
2428
int main(int argc, char *argv[]){
1866
 
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
1867
 
                        .priority = "SECURE256:!CTYPE-X.509:"
1868
 
                        "+CTYPE-OPENPGP", .current_server = NULL,
1869
 
                        .interfaces = NULL, .interfaces_size = 0 };
 
2429
  mandos_context mc = { .server = NULL, .dh_bits = 0,
 
2430
                        .priority = "SECURE256:!CTYPE-X.509"
 
2431
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
 
2432
                        .current_server = NULL, .interfaces = NULL,
 
2433
                        .interfaces_size = 0 };
1870
2434
  AvahiSServiceBrowser *sb = NULL;
1871
2435
  error_t ret_errno;
1872
2436
  int ret;
1881
2445
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1882
2446
  const char *seckey = PATHDIR "/" SECKEY;
1883
2447
  const char *pubkey = PATHDIR "/" PUBKEY;
 
2448
  const char *dh_params_file = NULL;
1884
2449
  char *interfaces_hooks = NULL;
1885
2450
  
1886
2451
  bool gnutls_initialized = false;
1939
2504
        .doc = "Bit length of the prime number used in the"
1940
2505
        " Diffie-Hellman key exchange",
1941
2506
        .group = 2 },
 
2507
      { .name = "dh-params", .key = 134,
 
2508
        .arg = "FILE",
 
2509
        .doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
 
2510
        " for the Diffie-Hellman key exchange",
 
2511
        .group = 2 },
1942
2512
      { .name = "priority", .key = 130,
1943
2513
        .arg = "STRING",
1944
2514
        .doc = "GnuTLS priority string for the TLS handshake",
1999
2569
        }
2000
2570
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2001
2571
        break;
 
2572
      case 134:                 /* --dh-params */
 
2573
        dh_params_file = arg;
 
2574
        break;
2002
2575
      case 130:                 /* --priority */
2003
2576
        mc.priority = arg;
2004
2577
        break;
2044
2617
                         .args_doc = "",
2045
2618
                         .doc = "Mandos client -- Get and decrypt"
2046
2619
                         " passwords from a Mandos server" };
2047
 
    ret = argp_parse(&argp, argc, argv,
2048
 
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2049
 
    switch(ret){
 
2620
    ret_errno = argp_parse(&argp, argc, argv,
 
2621
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2622
    switch(ret_errno){
2050
2623
    case 0:
2051
2624
      break;
2052
2625
    case ENOMEM:
2053
2626
    default:
2054
 
      errno = ret;
 
2627
      errno = ret_errno;
2055
2628
      perror_plus("argp_parse");
2056
2629
      exitcode = EX_OSERR;
2057
2630
      goto end;
2060
2633
      goto end;
2061
2634
    }
2062
2635
  }
2063
 
    
 
2636
  
2064
2637
  {
2065
2638
    /* Work around Debian bug #633582:
2066
 
       <http://bugs.debian.org/633582> */
 
2639
       <https://bugs.debian.org/633582> */
2067
2640
    
2068
2641
    /* Re-raise privileges */
2069
 
    ret_errno = raise_privileges();
2070
 
    if(ret_errno != 0){
2071
 
      errno = ret_errno;
 
2642
    ret = raise_privileges();
 
2643
    if(ret != 0){
 
2644
      errno = ret;
2072
2645
      perror_plus("Failed to raise privileges");
2073
2646
    } else {
2074
2647
      struct stat st;
2090
2663
              }
2091
2664
            }
2092
2665
          }
2093
 
          TEMP_FAILURE_RETRY(close(seckey_fd));
 
2666
          close(seckey_fd);
2094
2667
        }
2095
2668
      }
2096
 
    
 
2669
      
2097
2670
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2098
2671
        int pubkey_fd = open(pubkey, O_RDONLY);
2099
2672
        if(pubkey_fd == -1){
2111
2684
              }
2112
2685
            }
2113
2686
          }
2114
 
          TEMP_FAILURE_RETRY(close(pubkey_fd));
2115
 
        }
2116
 
      }
2117
 
    
 
2687
          close(pubkey_fd);
 
2688
        }
 
2689
      }
 
2690
      
 
2691
      if(dh_params_file != NULL
 
2692
         and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
 
2693
        int dhparams_fd = open(dh_params_file, O_RDONLY);
 
2694
        if(dhparams_fd == -1){
 
2695
          perror_plus("open");
 
2696
        } else {
 
2697
          ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
 
2698
          if(ret == -1){
 
2699
            perror_plus("fstat");
 
2700
          } else {
 
2701
            if(S_ISREG(st.st_mode)
 
2702
               and st.st_uid == 0 and st.st_gid == 0){
 
2703
              ret = fchown(dhparams_fd, uid, gid);
 
2704
              if(ret == -1){
 
2705
                perror_plus("fchown");
 
2706
              }
 
2707
            }
 
2708
          }
 
2709
          close(dhparams_fd);
 
2710
        }
 
2711
      }
 
2712
      
2118
2713
      /* Lower privileges */
2119
 
      ret_errno = lower_privileges();
2120
 
      if(ret_errno != 0){
2121
 
        errno = ret_errno;
 
2714
      ret = lower_privileges();
 
2715
      if(ret != 0){
 
2716
        errno = ret;
2122
2717
        perror_plus("Failed to lower privileges");
2123
2718
      }
2124
2719
    }
2237
2832
  
2238
2833
  /* If no interfaces were specified, make a list */
2239
2834
  if(mc.interfaces == NULL){
2240
 
    struct dirent **direntries;
 
2835
    struct dirent **direntries = NULL;
2241
2836
    /* Look for any good interfaces */
2242
2837
    ret = scandir(sys_class_net, &direntries, good_interface,
2243
2838
                  alphasort);
2249
2844
        if(ret_errno != 0){
2250
2845
          errno = ret_errno;
2251
2846
          perror_plus("argz_add");
 
2847
          free(direntries[i]);
2252
2848
          continue;
2253
2849
        }
2254
2850
        if(debug){
2255
2851
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
2256
2852
                       direntries[i]->d_name);
2257
2853
        }
 
2854
        free(direntries[i]);
2258
2855
      }
2259
2856
      free(direntries);
2260
2857
    } else {
2261
 
      free(direntries);
 
2858
      if(ret == 0){
 
2859
        free(direntries);
 
2860
      }
2262
2861
      fprintf_plus(stderr, "Could not find a network interface\n");
2263
2862
      exitcode = EXIT_FAILURE;
2264
2863
      goto end;
2290
2889
      errno = bring_up_interface(interface, delay);
2291
2890
      if(not interface_was_up){
2292
2891
        if(errno != 0){
2293
 
          perror_plus("Failed to bring up interface");
 
2892
          fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
 
2893
                       " %s\n", interface, strerror(errno));
2294
2894
        } else {
2295
2895
          errno = argz_add(&interfaces_to_take_down,
2296
2896
                           &interfaces_to_take_down_size,
2319
2919
    goto end;
2320
2920
  }
2321
2921
  
2322
 
  ret = init_gnutls_global(pubkey, seckey, &mc);
 
2922
  ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
2323
2923
  if(ret == -1){
2324
2924
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2325
2925
    exitcode = EX_UNAVAILABLE;
2447
3047
    
2448
3048
    /* Allocate a new server */
2449
3049
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2450
 
                                 &config, NULL, NULL, &ret_errno);
 
3050
                                 &config, NULL, NULL, &ret);
2451
3051
    
2452
3052
    /* Free the Avahi configuration data */
2453
3053
    avahi_server_config_free(&config);
2456
3056
  /* Check if creating the Avahi server object succeeded */
2457
3057
  if(mc.server == NULL){
2458
3058
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2459
 
                 avahi_strerror(ret_errno));
 
3059
                 avahi_strerror(ret));
2460
3060
    exitcode = EX_UNAVAILABLE;
2461
3061
    goto end;
2462
3062
  }
2486
3086
  if(debug){
2487
3087
    fprintf_plus(stderr, "Starting Avahi loop search\n");
2488
3088
  }
2489
 
 
 
3089
  
2490
3090
  ret = avahi_loop_with_timeout(simple_poll,
2491
3091
                                (int)(retry_interval * 1000), &mc);
2492
3092
  if(debug){
2497
3097
 end:
2498
3098
  
2499
3099
  if(debug){
2500
 
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3100
    if(signal_received){
 
3101
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
 
3102
                   argv[0], signal_received,
 
3103
                   strsignal(signal_received));
 
3104
    } else {
 
3105
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3106
    }
2501
3107
  }
2502
3108
  
2503
3109
  /* Cleanup things */
2514
3120
  
2515
3121
  if(gnutls_initialized){
2516
3122
    gnutls_certificate_free_credentials(mc.cred);
2517
 
    gnutls_global_deinit();
2518
3123
    gnutls_dh_params_deinit(mc.dh_params);
2519
3124
  }
2520
3125
  
2528
3133
    mc.current_server->prev->next = NULL;
2529
3134
    while(mc.current_server != NULL){
2530
3135
      server *next = mc.current_server->next;
 
3136
#ifdef __GNUC__
 
3137
#pragma GCC diagnostic push
 
3138
#pragma GCC diagnostic ignored "-Wcast-qual"
 
3139
#endif
 
3140
      free((char *)(mc.current_server->ip));
 
3141
#ifdef __GNUC__
 
3142
#pragma GCC diagnostic pop
 
3143
#endif
2531
3144
      free(mc.current_server);
2532
3145
      mc.current_server = next;
2533
3146
    }
2535
3148
  
2536
3149
  /* Re-raise privileges */
2537
3150
  {
2538
 
    ret_errno = raise_privileges();
2539
 
    if(ret_errno != 0){
 
3151
    ret = raise_privileges();
 
3152
    if(ret != 0){
 
3153
      errno = ret;
2540
3154
      perror_plus("Failed to raise privileges");
2541
3155
    } else {
2542
3156
      
2547
3161
      /* Take down the network interfaces which were brought up */
2548
3162
      {
2549
3163
        char *interface = NULL;
2550
 
        while((interface=argz_next(interfaces_to_take_down,
2551
 
                                   interfaces_to_take_down_size,
2552
 
                                   interface))){
2553
 
          ret_errno = take_down_interface(interface);
2554
 
          if(ret_errno != 0){
2555
 
            errno = ret_errno;
 
3164
        while((interface = argz_next(interfaces_to_take_down,
 
3165
                                     interfaces_to_take_down_size,
 
3166
                                     interface))){
 
3167
          ret = take_down_interface(interface);
 
3168
          if(ret != 0){
 
3169
            errno = ret;
2556
3170
            perror_plus("Failed to take down interface");
2557
3171
          }
2558
3172
        }
2563
3177
      }
2564
3178
    }
2565
3179
    
2566
 
    ret_errno = lower_privileges_permanently();
2567
 
    if(ret_errno != 0){
 
3180
    ret = lower_privileges_permanently();
 
3181
    if(ret != 0){
 
3182
      errno = ret;
2568
3183
      perror_plus("Failed to lower privileges permanently");
2569
3184
    }
2570
3185
  }
2572
3187
  free(interfaces_to_take_down);
2573
3188
  free(interfaces_hooks);
2574
3189
  
 
3190
  void clean_dir_at(int base, const char * const dirname,
 
3191
                    uintmax_t level){
 
3192
    struct dirent **direntries = NULL;
 
3193
    int dret;
 
3194
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
 
3195
                                                O_RDONLY
 
3196
                                                | O_NOFOLLOW
 
3197
                                                | O_DIRECTORY
 
3198
                                                | O_PATH));
 
3199
    if(dir_fd == -1){
 
3200
      perror_plus("open");
 
3201
      return;
 
3202
    }
 
3203
    int numentries = scandirat(dir_fd, ".", &direntries,
 
3204
                               notdotentries, alphasort);
 
3205
    if(numentries >= 0){
 
3206
      for(int i = 0; i < numentries; i++){
 
3207
        if(debug){
 
3208
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
 
3209
                       dirname, direntries[i]->d_name);
 
3210
        }
 
3211
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
 
3212
        if(dret == -1){
 
3213
          if(errno == EISDIR){
 
3214
              dret = unlinkat(dir_fd, direntries[i]->d_name,
 
3215
                              AT_REMOVEDIR);
 
3216
          }         
 
3217
          if((dret == -1) and (errno == ENOTEMPTY)
 
3218
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
 
3219
                  == 0) and (level == 0)){
 
3220
            /* Recurse only in this special case */
 
3221
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
 
3222
            dret = 0;
 
3223
          }
 
3224
          if((dret == -1) and (errno != ENOENT)){
 
3225
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
 
3226
                         direntries[i]->d_name, strerror(errno));
 
3227
          }
 
3228
        }
 
3229
        free(direntries[i]);
 
3230
      }
 
3231
      
 
3232
      /* need to clean even if 0 because man page doesn't specify */
 
3233
      free(direntries);
 
3234
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
 
3235
      if(dret == -1 and errno != ENOENT){
 
3236
        perror_plus("rmdir");
 
3237
      }
 
3238
    } else {
 
3239
      perror_plus("scandirat");
 
3240
    }
 
3241
    close(dir_fd);
 
3242
  }
 
3243
  
2575
3244
  /* Removes the GPGME temp directory and all files inside */
2576
3245
  if(tempdir != NULL){
2577
 
    struct dirent **direntries = NULL;
2578
 
    struct dirent *direntry = NULL;
2579
 
    int numentries = scandir(tempdir, &direntries, notdotentries,
2580
 
                             alphasort);
2581
 
    if(numentries > 0){
2582
 
      for(int i = 0; i < numentries; i++){
2583
 
        direntry = direntries[i];
2584
 
        char *fullname = NULL;
2585
 
        ret = asprintf(&fullname, "%s/%s", tempdir,
2586
 
                       direntry->d_name);
2587
 
        if(ret < 0){
2588
 
          perror_plus("asprintf");
2589
 
          continue;
2590
 
        }
2591
 
        ret = remove(fullname);
2592
 
        if(ret == -1){
2593
 
          fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2594
 
                       strerror(errno));
2595
 
        }
2596
 
        free(fullname);
2597
 
      }
2598
 
    }
2599
 
 
2600
 
    /* need to clean even if 0 because man page doesn't specify */
2601
 
    free(direntries);
2602
 
    if(numentries == -1){
2603
 
      perror_plus("scandir");
2604
 
    }
2605
 
    ret = rmdir(tempdir);
2606
 
    if(ret == -1 and errno != ENOENT){
2607
 
      perror_plus("rmdir");
2608
 
    }
 
3246
    clean_dir_at(-1, tempdir, 0);
2609
3247
  }
2610
3248
  
2611
3249
  if(quit_now){