/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: 2014-07-25 22:44:20 UTC
  • mto: This revision was merged to the branch mainline in revision 724.
  • Revision ID: teddy@recompile.se-20140725224420-4a5ct2ptt0hsc92z
Require Python 2.7.

This is in preparation for the eventual move to Python 3, which will
happen as soon as all Python modules required by Mandos are available.
The mandos-ctl and mandos-monitor programs are already portable
between Python 2.6 and Python 3 without changes; this change will
bring the requirement up to Python 2.7.

* INSTALL (Prerequisites/Libraries/Mandos Server): Document
                                                   requirement of
                                                   Python 2.7; remove
                                                   Python-argparse
                                                   which is in the
                                                   Python 2.7 standard
                                                   library.
* debian/control (Source: mandos/Build-Depends-Indep): Depend on
                                                       exactly the
                                                       python2.7
                                                       package and all
                                                       the Python 2.7
                                                       versions of the
                                                       python modules.
  (Package: mandos/Depends): - '' - but still depend on python (<=2.7)
                            and the generic versions of the Python
                            modules; this is for mandos-ctl and
                            mandos-monitor, both of which are
                            compatible with Python 3, and use
                            #!/usr/bin/python.
* mandos: Use #!/usr/bin/python2.7 instead of #!/usr/bin/python.

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-2011 Teddy Hogeborn
13
 
 * Copyright © 2008-2011 Björn Påhlsson
 
12
 * Copyright © 2008-2014 Teddy Hogeborn
 
13
 * Copyright © 2008-2014 Björn Påhlsson
14
14
 * 
15
15
 * This program is free software: you can redistribute it and/or
16
16
 * modify it under the terms of the GNU General Public License as
32
32
/* Needed by GPGME, specifically gpgme_data_seek() */
33
33
#ifndef _LARGEFILE_SOURCE
34
34
#define _LARGEFILE_SOURCE
35
 
#endif
 
35
#endif  /* not _LARGEFILE_SOURCE */
36
36
#ifndef _FILE_OFFSET_BITS
37
37
#define _FILE_OFFSET_BITS 64
38
 
#endif
 
38
#endif  /* not _FILE_OFFSET_BITS */
39
39
 
40
40
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
41
41
 
42
42
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
43
 
                                   stdout, ferror(), remove() */
44
 
#include <stdint.h>             /* uint16_t, uint32_t */
 
43
                                   stdout, ferror() */
 
44
#include <stdint.h>             /* uint16_t, uint32_t, intptr_t */
45
45
#include <stddef.h>             /* NULL, size_t, ssize_t */
46
46
#include <stdlib.h>             /* free(), EXIT_SUCCESS, srand(),
47
47
                                   strtof(), abort() */
55
55
                                   opendir(), DIR */
56
56
#include <sys/stat.h>           /* open(), S_ISREG */
57
57
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
58
 
                                   inet_pton(), connect() */
59
 
#include <fcntl.h>              /* open() */
 
58
                                   inet_pton(), connect(),
 
59
                                   getnameinfo() */
 
60
#include <fcntl.h>              /* open(), unlinkat() */
60
61
#include <dirent.h>             /* opendir(), struct dirent, readdir()
61
62
                                 */
62
63
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
63
64
                                   strtoimax() */
64
 
#include <assert.h>             /* assert() */
65
65
#include <errno.h>              /* perror(), errno,
66
66
                                   program_invocation_short_name */
67
67
#include <time.h>               /* nanosleep(), time(), sleep() */
73
73
                                */
74
74
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
75
75
                                   getuid(), getgid(), seteuid(),
76
 
                                   setgid(), pause() */
77
 
#include <arpa/inet.h>          /* inet_pton(), htons, inet_ntop() */
 
76
                                   setgid(), pause(), _exit(),
 
77
                                   unlinkat() */
 
78
#include <arpa/inet.h>          /* inet_pton(), htons() */
78
79
#include <iso646.h>             /* not, or, and */
79
80
#include <argp.h>               /* struct argp_option, error_t, struct
80
81
                                   argp_state, struct argp,
87
88
                                   EX_NOHOST, EX_IOERR, EX_PROTOCOL */
88
89
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
89
90
                                   WEXITSTATUS(), WTERMSIG() */
 
91
#include <grp.h>                /* setgroups() */
 
92
#include <argz.h>               /* argz_add_sep(), argz_next(),
 
93
                                   argz_delete(), argz_append(),
 
94
                                   argz_stringify(), argz_add(),
 
95
                                   argz_count() */
 
96
#include <netdb.h>              /* getnameinfo(), NI_NUMERICHOST,
 
97
                                   EAI_SYSTEM, gai_strerror() */
90
98
 
91
99
#ifdef __linux__
92
100
#include <sys/klog.h>           /* klogctl() */
134
142
static const char sys_class_net[] = "/sys/class/net";
135
143
char *connect_to = NULL;
136
144
const char *hookdir = HOOKDIR;
 
145
int hookdir_fd = -1;
 
146
uid_t uid = 65534;
 
147
gid_t gid = 65534;
137
148
 
138
149
/* Doubly linked list that need to be circularly linked when used */
139
150
typedef struct server{
140
151
  const char *ip;
141
 
  uint16_t port;
 
152
  in_port_t port;
142
153
  AvahiIfIndex if_index;
143
154
  int af;
144
155
  struct timespec last_seen;
148
159
 
149
160
/* Used for passing in values through the Avahi callback functions */
150
161
typedef struct {
151
 
  AvahiSimplePoll *simple_poll;
152
162
  AvahiServer *server;
153
163
  gnutls_certificate_credentials_t cred;
154
164
  unsigned int dh_bits;
156
166
  const char *priority;
157
167
  gpgme_ctx_t ctx;
158
168
  server *current_server;
 
169
  char *interfaces;
 
170
  size_t interfaces_size;
159
171
} mandos_context;
160
172
 
161
 
/* global context so signal handler can reach it*/
162
 
mandos_context mc = { .simple_poll = NULL, .server = NULL,
163
 
                      .dh_bits = 1024, .priority = "SECURE256"
164
 
                      ":!CTYPE-X.509:+CTYPE-OPENPGP",
165
 
                      .current_server = NULL };
 
173
/* global so signal handler can reach it*/
 
174
AvahiSimplePoll *simple_poll;
166
175
 
167
176
sig_atomic_t quit_now = 0;
168
177
int signal_received = 0;
169
178
 
170
179
/* Function to use when printing errors */
171
180
void perror_plus(const char *print_text){
 
181
  int e = errno;
172
182
  fprintf(stderr, "Mandos plugin %s: ",
173
183
          program_invocation_short_name);
 
184
  errno = e;
174
185
  perror(print_text);
175
186
}
176
187
 
 
188
__attribute__((format (gnu_printf, 2, 3), nonnull))
177
189
int fprintf_plus(FILE *stream, const char *format, ...){
178
190
  va_list ap;
179
191
  va_start (ap, format);
180
192
  
181
193
  TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
182
194
                             program_invocation_short_name));
183
 
  return TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
 
195
  return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
184
196
}
185
197
 
186
198
/*
188
200
 * bytes. "buffer_capacity" is how much is currently allocated,
189
201
 * "buffer_length" is how much is already used.
190
202
 */
 
203
__attribute__((nonnull, warn_unused_result))
191
204
size_t incbuffer(char **buffer, size_t buffer_length,
192
205
                 size_t buffer_capacity){
193
206
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
194
 
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
195
 
    if(buffer == NULL){
 
207
    char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
208
    if(new_buf == NULL){
 
209
      int old_errno = errno;
 
210
      free(*buffer);
 
211
      errno = old_errno;
 
212
      *buffer = NULL;
196
213
      return 0;
197
214
    }
 
215
    *buffer = new_buf;
198
216
    buffer_capacity += BUFFER_SIZE;
199
217
  }
200
218
  return buffer_capacity;
201
219
}
202
220
 
203
221
/* Add server to set of servers to retry periodically */
204
 
int add_server(const char *ip, uint16_t port, AvahiIfIndex if_index,
205
 
               int af){
 
222
__attribute__((nonnull, warn_unused_result))
 
223
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
 
224
                int af, server **current_server){
206
225
  int ret;
207
226
  server *new_server = malloc(sizeof(server));
208
227
  if(new_server == NULL){
209
228
    perror_plus("malloc");
210
 
    return -1;
 
229
    return false;
211
230
  }
212
231
  *new_server = (server){ .ip = strdup(ip),
213
232
                          .port = port,
215
234
                          .af = af };
216
235
  if(new_server->ip == NULL){
217
236
    perror_plus("strdup");
218
 
    return -1;
 
237
    free(new_server);
 
238
    return false;
 
239
  }
 
240
  ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
 
241
  if(ret == -1){
 
242
    perror_plus("clock_gettime");
 
243
#ifdef __GNUC__
 
244
#pragma GCC diagnostic push
 
245
#pragma GCC diagnostic ignored "-Wcast-qual"
 
246
#endif
 
247
    free((char *)(new_server->ip));
 
248
#ifdef __GNUC__
 
249
#pragma GCC diagnostic pop
 
250
#endif
 
251
    free(new_server);
 
252
    return false;
219
253
  }
220
254
  /* Special case of first server */
221
 
  if (mc.current_server == NULL){
 
255
  if(*current_server == NULL){
222
256
    new_server->next = new_server;
223
257
    new_server->prev = new_server;
224
 
    mc.current_server = new_server;
225
 
  /* Place the new server last in the list */
 
258
    *current_server = new_server;
226
259
  } else {
227
 
    new_server->next = mc.current_server;
228
 
    new_server->prev = mc.current_server->prev;
 
260
    /* Place the new server last in the list */
 
261
    new_server->next = *current_server;
 
262
    new_server->prev = (*current_server)->prev;
229
263
    new_server->prev->next = new_server;
230
 
    mc.current_server->prev = new_server;
231
 
  }
232
 
  ret = clock_gettime(CLOCK_MONOTONIC, &mc.current_server->last_seen);
233
 
  if(ret == -1){
234
 
    perror_plus("clock_gettime");
235
 
    return -1;
236
 
  }
237
 
  return 0;
 
264
    (*current_server)->prev = new_server;
 
265
  }
 
266
  return true;
238
267
}
239
268
 
240
269
/* 
241
270
 * Initialize GPGME.
242
271
 */
243
 
static bool init_gpgme(const char *seckey, const char *pubkey,
244
 
                       const char *tempdir){
 
272
__attribute__((nonnull, warn_unused_result))
 
273
static bool init_gpgme(const char * const seckey,
 
274
                       const char * const pubkey,
 
275
                       const char * const tempdir,
 
276
                       mandos_context *mc){
245
277
  gpgme_error_t rc;
246
278
  gpgme_engine_info_t engine_info;
247
279
  
248
 
  
249
280
  /*
250
281
   * Helper function to insert pub and seckey to the engine keyring.
251
282
   */
252
 
  bool import_key(const char *filename){
 
283
  bool import_key(const char * const filename){
253
284
    int ret;
254
285
    int fd;
255
286
    gpgme_data_t pgp_data;
267
298
      return false;
268
299
    }
269
300
    
270
 
    rc = gpgme_op_import(mc.ctx, pgp_data);
 
301
    rc = gpgme_op_import(mc->ctx, pgp_data);
271
302
    if(rc != GPG_ERR_NO_ERROR){
272
303
      fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
273
304
                   gpgme_strsource(rc), gpgme_strerror(rc));
317
348
  }
318
349
  
319
350
  /* Create new GPGME "context" */
320
 
  rc = gpgme_new(&(mc.ctx));
 
351
  rc = gpgme_new(&(mc->ctx));
321
352
  if(rc != GPG_ERR_NO_ERROR){
322
353
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
323
354
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
336
367
 * Decrypt OpenPGP data.
337
368
 * Returns -1 on error
338
369
 */
 
370
__attribute__((nonnull, warn_unused_result))
339
371
static ssize_t pgp_packet_decrypt(const char *cryptotext,
340
372
                                  size_t crypto_size,
341
 
                                  char **plaintext){
 
373
                                  char **plaintext,
 
374
                                  mandos_context *mc){
342
375
  gpgme_data_t dh_crypto, dh_plain;
343
376
  gpgme_error_t rc;
344
377
  ssize_t ret;
370
403
  
371
404
  /* Decrypt data from the cryptotext data buffer to the plaintext
372
405
     data buffer */
373
 
  rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
 
406
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
374
407
  if(rc != GPG_ERR_NO_ERROR){
375
408
    fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
376
409
                 gpgme_strsource(rc), gpgme_strerror(rc));
377
410
    plaintext_length = -1;
378
411
    if(debug){
379
412
      gpgme_decrypt_result_t result;
380
 
      result = gpgme_op_decrypt_result(mc.ctx);
 
413
      result = gpgme_op_decrypt_result(mc->ctx);
381
414
      if(result == NULL){
382
415
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
383
416
      } else {
460
493
  return plaintext_length;
461
494
}
462
495
 
463
 
static const char * safer_gnutls_strerror(int value){
464
 
  const char *ret = gnutls_strerror(value); /* Spurious warning from
465
 
                                               -Wunreachable-code */
 
496
__attribute__((warn_unused_result))
 
497
static const char *safer_gnutls_strerror(int value){
 
498
  const char *ret = gnutls_strerror(value);
466
499
  if(ret == NULL)
467
500
    ret = "(unknown)";
468
501
  return ret;
469
502
}
470
503
 
471
504
/* GnuTLS log function callback */
 
505
__attribute__((nonnull))
472
506
static void debuggnutls(__attribute__((unused)) int level,
473
507
                        const char* string){
474
508
  fprintf_plus(stderr, "GnuTLS: %s", string);
475
509
}
476
510
 
 
511
__attribute__((nonnull, warn_unused_result))
477
512
static int init_gnutls_global(const char *pubkeyfilename,
478
 
                              const char *seckeyfilename){
 
513
                              const char *seckeyfilename,
 
514
                              mandos_context *mc){
479
515
  int ret;
480
516
  
481
517
  if(debug){
498
534
  }
499
535
  
500
536
  /* OpenPGP credentials */
501
 
  ret = gnutls_certificate_allocate_credentials(&mc.cred);
 
537
  ret = gnutls_certificate_allocate_credentials(&mc->cred);
502
538
  if(ret != GNUTLS_E_SUCCESS){
503
539
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
504
540
                 safer_gnutls_strerror(ret));
514
550
  }
515
551
  
516
552
  ret = gnutls_certificate_set_openpgp_key_file
517
 
    (mc.cred, pubkeyfilename, seckeyfilename,
 
553
    (mc->cred, pubkeyfilename, seckeyfilename,
518
554
     GNUTLS_OPENPGP_FMT_BASE64);
519
555
  if(ret != GNUTLS_E_SUCCESS){
520
556
    fprintf_plus(stderr,
526
562
  }
527
563
  
528
564
  /* GnuTLS server initialization */
529
 
  ret = gnutls_dh_params_init(&mc.dh_params);
 
565
  ret = gnutls_dh_params_init(&mc->dh_params);
530
566
  if(ret != GNUTLS_E_SUCCESS){
531
567
    fprintf_plus(stderr, "Error in GnuTLS DH parameter"
532
568
                 " initialization: %s\n",
533
569
                 safer_gnutls_strerror(ret));
534
570
    goto globalfail;
535
571
  }
536
 
  ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
 
572
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
537
573
  if(ret != GNUTLS_E_SUCCESS){
538
574
    fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
539
575
                 safer_gnutls_strerror(ret));
540
576
    goto globalfail;
541
577
  }
542
578
  
543
 
  gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
 
579
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
544
580
  
545
581
  return 0;
546
582
  
547
583
 globalfail:
548
584
  
549
 
  gnutls_certificate_free_credentials(mc.cred);
 
585
  gnutls_certificate_free_credentials(mc->cred);
550
586
  gnutls_global_deinit();
551
 
  gnutls_dh_params_deinit(mc.dh_params);
 
587
  gnutls_dh_params_deinit(mc->dh_params);
552
588
  return -1;
553
589
}
554
590
 
555
 
static int init_gnutls_session(gnutls_session_t *session){
 
591
__attribute__((nonnull, warn_unused_result))
 
592
static int init_gnutls_session(gnutls_session_t *session,
 
593
                               mandos_context *mc){
556
594
  int ret;
557
595
  /* GnuTLS session creation */
558
596
  do {
570
608
  {
571
609
    const char *err;
572
610
    do {
573
 
      ret = gnutls_priority_set_direct(*session, mc.priority, &err);
 
611
      ret = gnutls_priority_set_direct(*session, mc->priority, &err);
574
612
      if(quit_now){
575
613
        gnutls_deinit(*session);
576
614
        return -1;
587
625
  
588
626
  do {
589
627
    ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
590
 
                                 mc.cred);
 
628
                                 mc->cred);
591
629
    if(quit_now){
592
630
      gnutls_deinit(*session);
593
631
      return -1;
603
641
  /* ignore client certificate if any. */
604
642
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
605
643
  
606
 
  gnutls_dh_set_prime_bits(*session, mc.dh_bits);
 
644
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
607
645
  
608
646
  return 0;
609
647
}
613
651
                      __attribute__((unused)) const char *txt){}
614
652
 
615
653
/* Called when a Mandos server is found */
616
 
static int start_mandos_communication(const char *ip, uint16_t port,
 
654
__attribute__((nonnull, warn_unused_result))
 
655
static int start_mandos_communication(const char *ip, in_port_t port,
617
656
                                      AvahiIfIndex if_index,
618
 
                                      int af){
 
657
                                      int af, mandos_context *mc){
619
658
  int ret, tcp_sd = -1;
620
659
  ssize_t sret;
621
 
  union {
622
 
    struct sockaddr_in in;
623
 
    struct sockaddr_in6 in6;
624
 
  } to;
 
660
  struct sockaddr_storage to;
625
661
  char *buffer = NULL;
626
662
  char *decrypted_buffer = NULL;
627
663
  size_t buffer_length = 0;
651
687
    return -1;
652
688
  }
653
689
  
654
 
  ret = init_gnutls_session(&session);
 
690
  /* If the interface is specified and we have a list of interfaces */
 
691
  if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
 
692
    /* Check if the interface is one of the interfaces we are using */
 
693
    bool match = false;
 
694
    {
 
695
      char *interface = NULL;
 
696
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
 
697
                                 interface))){
 
698
        if(if_nametoindex(interface) == (unsigned int)if_index){
 
699
          match = true;
 
700
          break;
 
701
        }
 
702
      }
 
703
    }
 
704
    if(not match){
 
705
      /* This interface does not match any in the list, so we don't
 
706
         connect to the server */
 
707
      if(debug){
 
708
        char interface[IF_NAMESIZE];
 
709
        if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
710
          perror_plus("if_indextoname");
 
711
        } else {
 
712
          fprintf_plus(stderr, "Skipping server on non-used interface"
 
713
                       " \"%s\"\n",
 
714
                       if_indextoname((unsigned int)if_index,
 
715
                                      interface));
 
716
        }
 
717
      }
 
718
      return -1;
 
719
    }
 
720
  }
 
721
  
 
722
  ret = init_gnutls_session(&session, mc);
655
723
  if(ret != 0){
656
724
    return -1;
657
725
  }
658
726
  
659
727
  if(debug){
660
728
    fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
661
 
                 PRIu16 "\n", ip, port);
 
729
                 PRIuMAX "\n", ip, (uintmax_t)port);
662
730
  }
663
731
  
664
732
  tcp_sd = socket(pf, SOCK_STREAM, 0);
676
744
  
677
745
  memset(&to, 0, sizeof(to));
678
746
  if(af == AF_INET6){
679
 
    to.in6.sin6_family = (sa_family_t)af;
680
 
    ret = inet_pton(af, ip, &to.in6.sin6_addr);
 
747
    ((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
 
748
    ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
681
749
  } else {                      /* IPv4 */
682
 
    to.in.sin_family = (sa_family_t)af;
683
 
    ret = inet_pton(af, ip, &to.in.sin_addr);
 
750
    ((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
 
751
    ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
684
752
  }
685
753
  if(ret < 0 ){
686
754
    int e = errno;
695
763
    goto mandos_end;
696
764
  }
697
765
  if(af == AF_INET6){
698
 
    to.in6.sin6_port = htons(port); /* Spurious warnings from
699
 
                                       -Wconversion and
700
 
                                       -Wunreachable-code */
701
 
    
702
 
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
703
 
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
704
 
                                -Wunreachable-code*/
 
766
    ((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
 
767
    if(IN6_IS_ADDR_LINKLOCAL
 
768
       (&((struct sockaddr_in6 *)&to)->sin6_addr)){
705
769
      if(if_index == AVAHI_IF_UNSPEC){
706
770
        fprintf_plus(stderr, "An IPv6 link-local address is"
707
771
                     " incomplete without a network interface\n");
709
773
        goto mandos_end;
710
774
      }
711
775
      /* Set the network interface number as scope */
712
 
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
776
      ((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
713
777
    }
714
778
  } else {
715
 
    to.in.sin_port = htons(port); /* Spurious warnings from
716
 
                                     -Wconversion and
717
 
                                     -Wunreachable-code */
 
779
    ((struct sockaddr_in *)&to)->sin_port = htons(port);
718
780
  }
719
781
  
720
782
  if(quit_now){
728
790
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
729
791
        perror_plus("if_indextoname");
730
792
      } else {
731
 
        fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIu16
732
 
                     "\n", ip, interface, port);
 
793
        fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
 
794
                     "\n", ip, interface, (uintmax_t)port);
733
795
      }
734
796
    } else {
735
 
      fprintf_plus(stderr, "Connection to: %s, port %" PRIu16 "\n",
736
 
                   ip, port);
 
797
      fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
 
798
                   ip, (uintmax_t)port);
737
799
    }
738
800
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
739
801
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
740
 
    const char *pcret;
741
802
    if(af == AF_INET6){
742
 
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
743
 
                        sizeof(addrstr));
 
803
      ret = getnameinfo((struct sockaddr *)&to,
 
804
                        sizeof(struct sockaddr_in6),
 
805
                        addrstr, sizeof(addrstr), NULL, 0,
 
806
                        NI_NUMERICHOST);
744
807
    } else {
745
 
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
746
 
                        sizeof(addrstr));
 
808
      ret = getnameinfo((struct sockaddr *)&to,
 
809
                        sizeof(struct sockaddr_in),
 
810
                        addrstr, sizeof(addrstr), NULL, 0,
 
811
                        NI_NUMERICHOST);
747
812
    }
748
 
    if(pcret == NULL){
749
 
      perror_plus("inet_ntop");
750
 
    } else {
751
 
      if(strcmp(addrstr, ip) != 0){
752
 
        fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
753
 
      }
 
813
    if(ret == EAI_SYSTEM){
 
814
      perror_plus("getnameinfo");
 
815
    } else if(ret != 0) {
 
816
      fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
 
817
    } else if(strcmp(addrstr, ip) != 0){
 
818
      fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
754
819
    }
755
820
  }
756
821
  
760
825
  }
761
826
  
762
827
  if(af == AF_INET6){
763
 
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
828
    ret = connect(tcp_sd, (struct sockaddr *)&to,
 
829
                  sizeof(struct sockaddr_in6));
764
830
  } else {
765
 
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
831
    ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
 
832
                  sizeof(struct sockaddr_in));
766
833
  }
767
834
  if(ret < 0){
768
 
    if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
 
835
    if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
769
836
      int e = errno;
770
837
      perror_plus("connect");
771
838
      errno = e;
817
884
    goto mandos_end;
818
885
  }
819
886
  
820
 
  /* Spurious warning from -Wint-to-pointer-cast */
821
 
  gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
 
887
  /* This casting via intptr_t is to eliminate warning about casting
 
888
     an int to a pointer type.  This is exactly how the GnuTLS Guile
 
889
     function "set-session-transport-fd!" does it. */
 
890
  gnutls_transport_set_ptr(session,
 
891
                           (gnutls_transport_ptr_t)(intptr_t)tcp_sd);
822
892
  
823
893
  if(quit_now){
824
894
    errno = EINTR;
929
999
  if(buffer_length > 0){
930
1000
    ssize_t decrypted_buffer_size;
931
1001
    decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
932
 
                                               &decrypted_buffer);
 
1002
                                               &decrypted_buffer, mc);
933
1003
    if(decrypted_buffer_size >= 0){
934
1004
      
935
1005
      written = 0;
983
1053
  return retval;
984
1054
}
985
1055
 
 
1056
__attribute__((nonnull))
986
1057
static void resolve_callback(AvahiSServiceResolver *r,
987
1058
                             AvahiIfIndex interface,
988
1059
                             AvahiProtocol proto,
996
1067
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
997
1068
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
998
1069
                             flags,
999
 
                             AVAHI_GCC_UNUSED void* userdata){
1000
 
  assert(r);
 
1070
                             void *mc){
 
1071
  if(r == NULL){
 
1072
    return;
 
1073
  }
1001
1074
  
1002
1075
  /* Called whenever a service has been resolved successfully or
1003
1076
     timed out */
1004
1077
  
1005
1078
  if(quit_now){
 
1079
    avahi_s_service_resolver_free(r);
1006
1080
    return;
1007
1081
  }
1008
1082
  
1012
1086
    fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1013
1087
                 "'%s' of type '%s' in domain '%s': %s\n", name, type,
1014
1088
                 domain,
1015
 
                 avahi_strerror(avahi_server_errno(mc.server)));
 
1089
                 avahi_strerror(avahi_server_errno
 
1090
                                (((mandos_context*)mc)->server)));
1016
1091
    break;
1017
1092
    
1018
1093
  case AVAHI_RESOLVER_FOUND:
1024
1099
                     PRIdMAX ") on port %" PRIu16 "\n", name,
1025
1100
                     host_name, ip, (intmax_t)interface, port);
1026
1101
      }
1027
 
      int ret = start_mandos_communication(ip, port, interface,
1028
 
                                           avahi_proto_to_af(proto));
 
1102
      int ret = start_mandos_communication(ip, (in_port_t)port,
 
1103
                                           interface,
 
1104
                                           avahi_proto_to_af(proto),
 
1105
                                           mc);
1029
1106
      if(ret == 0){
1030
 
        avahi_simple_poll_quit(mc.simple_poll);
 
1107
        avahi_simple_poll_quit(simple_poll);
1031
1108
      } else {
1032
 
        ret = add_server(ip, port, interface,
1033
 
                         avahi_proto_to_af(proto));
 
1109
        if(not add_server(ip, (in_port_t)port, interface,
 
1110
                          avahi_proto_to_af(proto),
 
1111
                          &((mandos_context*)mc)->current_server)){
 
1112
          fprintf_plus(stderr, "Failed to add server \"%s\" to server"
 
1113
                       " list\n", name);
 
1114
        }
1034
1115
      }
1035
1116
    }
1036
1117
  }
1046
1127
                            const char *domain,
1047
1128
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
1048
1129
                            flags,
1049
 
                            AVAHI_GCC_UNUSED void* userdata){
1050
 
  assert(b);
 
1130
                            void *mc){
 
1131
  if(b == NULL){
 
1132
    return;
 
1133
  }
1051
1134
  
1052
1135
  /* Called whenever a new services becomes available on the LAN or
1053
1136
     is removed from the LAN */
1061
1144
  case AVAHI_BROWSER_FAILURE:
1062
1145
    
1063
1146
    fprintf_plus(stderr, "(Avahi browser) %s\n",
1064
 
                 avahi_strerror(avahi_server_errno(mc.server)));
1065
 
    avahi_simple_poll_quit(mc.simple_poll);
 
1147
                 avahi_strerror(avahi_server_errno
 
1148
                                (((mandos_context*)mc)->server)));
 
1149
    avahi_simple_poll_quit(simple_poll);
1066
1150
    return;
1067
1151
    
1068
1152
  case AVAHI_BROWSER_NEW:
1071
1155
       the callback function is called the Avahi server will free the
1072
1156
       resolver for us. */
1073
1157
    
1074
 
    if(avahi_s_service_resolver_new(mc.server, interface, protocol,
1075
 
                                    name, type, domain, protocol, 0,
1076
 
                                    resolve_callback, NULL) == NULL)
 
1158
    if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
 
1159
                                    interface, protocol, name, type,
 
1160
                                    domain, protocol, 0,
 
1161
                                    resolve_callback, mc) == NULL)
1077
1162
      fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1078
1163
                   " %s\n", name,
1079
 
                   avahi_strerror(avahi_server_errno(mc.server)));
 
1164
                   avahi_strerror(avahi_server_errno
 
1165
                                  (((mandos_context*)mc)->server)));
1080
1166
    break;
1081
1167
    
1082
1168
  case AVAHI_BROWSER_REMOVE:
1101
1187
  signal_received = sig;
1102
1188
  int old_errno = errno;
1103
1189
  /* set main loop to exit */
1104
 
  if(mc.simple_poll != NULL){
1105
 
    avahi_simple_poll_quit(mc.simple_poll);
 
1190
  if(simple_poll != NULL){
 
1191
    avahi_simple_poll_quit(simple_poll);
1106
1192
  }
1107
1193
  errno = old_errno;
1108
1194
}
1109
1195
 
 
1196
__attribute__((nonnull, warn_unused_result))
1110
1197
bool get_flags(const char *ifname, struct ifreq *ifr){
1111
1198
  int ret;
 
1199
  error_t ret_errno;
1112
1200
  
1113
1201
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1114
1202
  if(s < 0){
 
1203
    ret_errno = errno;
1115
1204
    perror_plus("socket");
 
1205
    errno = ret_errno;
1116
1206
    return false;
1117
1207
  }
1118
1208
  strcpy(ifr->ifr_name, ifname);
1119
1209
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1120
1210
  if(ret == -1){
1121
1211
    if(debug){
 
1212
      ret_errno = errno;
1122
1213
      perror_plus("ioctl SIOCGIFFLAGS");
 
1214
      errno = ret_errno;
1123
1215
    }
1124
1216
    return false;
1125
1217
  }
1126
1218
  return true;
1127
1219
}
1128
1220
 
 
1221
__attribute__((nonnull, warn_unused_result))
1129
1222
bool good_flags(const char *ifname, const struct ifreq *ifr){
1130
1223
  
1131
1224
  /* Reject the loopback device */
1173
1266
 * corresponds to an acceptable network device.
1174
1267
 * (This function is passed to scandir(3) as a filter function.)
1175
1268
 */
 
1269
__attribute__((nonnull, warn_unused_result))
1176
1270
int good_interface(const struct dirent *if_entry){
1177
1271
  if(if_entry->d_name[0] == '.'){
1178
1272
    return 0;
1194
1288
}
1195
1289
 
1196
1290
/* 
1197
 
 * This function determines if a directory entry in /sys/class/net
1198
 
 * corresponds to an acceptable network device which is up.
1199
 
 * (This function is passed to scandir(3) as a filter function.)
1200
 
 */
1201
 
int up_interface(const struct dirent *if_entry){
1202
 
  if(if_entry->d_name[0] == '.'){
1203
 
    return 0;
1204
 
  }
1205
 
  
1206
 
  struct ifreq ifr;
1207
 
  if(not get_flags(if_entry->d_name, &ifr)){
1208
 
    if(debug){
1209
 
      fprintf_plus(stderr, "Failed to get flags for interface "
1210
 
                   "\"%s\"\n", if_entry->d_name);
1211
 
    }
1212
 
    return 0;
1213
 
  }
1214
 
  
1215
 
  /* Reject down interfaces */
1216
 
  if(not (ifr.ifr_flags & IFF_UP)){
1217
 
    if(debug){
1218
 
      fprintf_plus(stderr, "Rejecting down interface \"%s\"\n",
1219
 
                   if_entry->d_name);
1220
 
    }
1221
 
    return 0;
1222
 
  }
1223
 
  
1224
 
  /* Reject non-running interfaces */
1225
 
  if(not (ifr.ifr_flags & IFF_RUNNING)){
1226
 
    if(debug){
1227
 
      fprintf_plus(stderr, "Rejecting non-running interface \"%s\"\n",
1228
 
                   if_entry->d_name);
1229
 
    }
1230
 
    return 0;
1231
 
  }
1232
 
  
1233
 
  if(not good_flags(if_entry->d_name, &ifr)){
1234
 
    return 0;
1235
 
  }
1236
 
  return 1;
1237
 
}
1238
 
 
 
1291
 * This function determines if a network interface is up.
 
1292
 */
 
1293
__attribute__((nonnull, warn_unused_result))
 
1294
bool interface_is_up(const char *interface){
 
1295
  struct ifreq ifr;
 
1296
  if(not get_flags(interface, &ifr)){
 
1297
    if(debug){
 
1298
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1299
                   "\"%s\"\n", interface);
 
1300
    }
 
1301
    return false;
 
1302
  }
 
1303
  
 
1304
  return (bool)(ifr.ifr_flags & IFF_UP);
 
1305
}
 
1306
 
 
1307
/* 
 
1308
 * This function determines if a network interface is running
 
1309
 */
 
1310
__attribute__((nonnull, warn_unused_result))
 
1311
bool interface_is_running(const char *interface){
 
1312
  struct ifreq ifr;
 
1313
  if(not get_flags(interface, &ifr)){
 
1314
    if(debug){
 
1315
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1316
                   "\"%s\"\n", interface);
 
1317
    }
 
1318
    return false;
 
1319
  }
 
1320
  
 
1321
  return (bool)(ifr.ifr_flags & IFF_RUNNING);
 
1322
}
 
1323
 
 
1324
__attribute__((nonnull, pure, warn_unused_result))
1239
1325
int notdotentries(const struct dirent *direntry){
1240
1326
  /* Skip "." and ".." */
1241
1327
  if(direntry->d_name[0] == '.'
1248
1334
}
1249
1335
 
1250
1336
/* Is this directory entry a runnable program? */
 
1337
__attribute__((nonnull, warn_unused_result))
1251
1338
int runnable_hook(const struct dirent *direntry){
1252
1339
  int ret;
1253
1340
  size_t sret;
1261
1348
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1262
1349
                "abcdefghijklmnopqrstuvwxyz"
1263
1350
                "0123456789"
1264
 
                "_-");
 
1351
                "_.-");
1265
1352
  if((direntry->d_name)[sret] != '\0'){
1266
1353
    /* Contains non-allowed characters */
1267
1354
    if(debug){
1271
1358
    return 0;
1272
1359
  }
1273
1360
  
1274
 
  char *fullname = NULL;
1275
 
  ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1276
 
  if(ret < 0){
1277
 
    perror_plus("asprintf");
1278
 
    return 0;
1279
 
  }
1280
 
  
1281
 
  ret = stat(fullname, &st);
 
1361
  ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1282
1362
  if(ret == -1){
1283
1363
    if(debug){
1284
1364
      perror_plus("Could not stat hook");
1308
1388
  return 1;
1309
1389
}
1310
1390
 
1311
 
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval){
 
1391
__attribute__((nonnull, warn_unused_result))
 
1392
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
 
1393
                            mandos_context *mc){
1312
1394
  int ret;
1313
1395
  struct timespec now;
1314
1396
  struct timespec waited_time;
1315
1397
  intmax_t block_time;
1316
1398
  
1317
1399
  while(true){
1318
 
    if(mc.current_server == NULL){
1319
 
      if (debug){
 
1400
    if(mc->current_server == NULL){
 
1401
      if(debug){
1320
1402
        fprintf_plus(stderr, "Wait until first server is found."
1321
1403
                     " No timeout!\n");
1322
1404
      }
1323
1405
      ret = avahi_simple_poll_iterate(s, -1);
1324
1406
    } else {
1325
 
      if (debug){
 
1407
      if(debug){
1326
1408
        fprintf_plus(stderr, "Check current_server if we should run"
1327
1409
                     " it, or wait\n");
1328
1410
      }
1335
1417
      /* Calculating in ms how long time between now and server
1336
1418
         who we visted longest time ago. Now - last seen.  */
1337
1419
      waited_time.tv_sec = (now.tv_sec
1338
 
                            - mc.current_server->last_seen.tv_sec);
 
1420
                            - mc->current_server->last_seen.tv_sec);
1339
1421
      waited_time.tv_nsec = (now.tv_nsec
1340
 
                             - mc.current_server->last_seen.tv_nsec);
 
1422
                             - mc->current_server->last_seen.tv_nsec);
1341
1423
      /* total time is 10s/10,000ms.
1342
1424
         Converting to s from ms by dividing by 1,000,
1343
1425
         and ns to ms by dividing by 1,000,000. */
1345
1427
                     - ((intmax_t)waited_time.tv_sec * 1000))
1346
1428
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
1347
1429
      
1348
 
      if (debug){
 
1430
      if(debug){
1349
1431
        fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1350
1432
                     block_time);
1351
1433
      }
1352
1434
      
1353
1435
      if(block_time <= 0){
1354
 
        ret = start_mandos_communication(mc.current_server->ip,
1355
 
                                         mc.current_server->port,
1356
 
                                         mc.current_server->if_index,
1357
 
                                         mc.current_server->af);
 
1436
        ret = start_mandos_communication(mc->current_server->ip,
 
1437
                                         mc->current_server->port,
 
1438
                                         mc->current_server->if_index,
 
1439
                                         mc->current_server->af, mc);
1358
1440
        if(ret == 0){
1359
 
          avahi_simple_poll_quit(mc.simple_poll);
 
1441
          avahi_simple_poll_quit(s);
1360
1442
          return 0;
1361
1443
        }
1362
1444
        ret = clock_gettime(CLOCK_MONOTONIC,
1363
 
                            &mc.current_server->last_seen);
 
1445
                            &mc->current_server->last_seen);
1364
1446
        if(ret == -1){
1365
1447
          perror_plus("clock_gettime");
1366
1448
          return -1;
1367
1449
        }
1368
 
        mc.current_server = mc.current_server->next;
 
1450
        mc->current_server = mc->current_server->next;
1369
1451
        block_time = 0;         /* Call avahi to find new Mandos
1370
1452
                                   servers, but don't block */
1371
1453
      }
1373
1455
      ret = avahi_simple_poll_iterate(s, (int)block_time);
1374
1456
    }
1375
1457
    if(ret != 0){
1376
 
      if (ret > 0 or errno != EINTR){
 
1458
      if(ret > 0 or errno != EINTR){
1377
1459
        return (ret != 1) ? ret : 0;
1378
1460
      }
1379
1461
    }
1380
1462
  }
1381
1463
}
1382
1464
 
1383
 
bool run_network_hooks(const char *mode, const char *interface,
 
1465
/* Set effective uid to 0, return errno */
 
1466
__attribute__((warn_unused_result))
 
1467
error_t raise_privileges(void){
 
1468
  error_t old_errno = errno;
 
1469
  error_t ret_errno = 0;
 
1470
  if(seteuid(0) == -1){
 
1471
    ret_errno = errno;
 
1472
  }
 
1473
  errno = old_errno;
 
1474
  return ret_errno;
 
1475
}
 
1476
 
 
1477
/* Set effective and real user ID to 0.  Return errno. */
 
1478
__attribute__((warn_unused_result))
 
1479
error_t raise_privileges_permanently(void){
 
1480
  error_t old_errno = errno;
 
1481
  error_t ret_errno = raise_privileges();
 
1482
  if(ret_errno != 0){
 
1483
    errno = old_errno;
 
1484
    return ret_errno;
 
1485
  }
 
1486
  if(setuid(0) == -1){
 
1487
    ret_errno = errno;
 
1488
  }
 
1489
  errno = old_errno;
 
1490
  return ret_errno;
 
1491
}
 
1492
 
 
1493
/* Set effective user ID to unprivileged saved user ID */
 
1494
__attribute__((warn_unused_result))
 
1495
error_t lower_privileges(void){
 
1496
  error_t old_errno = errno;
 
1497
  error_t ret_errno = 0;
 
1498
  if(seteuid(uid) == -1){
 
1499
    ret_errno = errno;
 
1500
  }
 
1501
  errno = old_errno;
 
1502
  return ret_errno;
 
1503
}
 
1504
 
 
1505
/* Lower privileges permanently */
 
1506
__attribute__((warn_unused_result))
 
1507
error_t lower_privileges_permanently(void){
 
1508
  error_t old_errno = errno;
 
1509
  error_t ret_errno = 0;
 
1510
  if(setuid(uid) == -1){
 
1511
    ret_errno = errno;
 
1512
  }
 
1513
  errno = old_errno;
 
1514
  return ret_errno;
 
1515
}
 
1516
 
 
1517
__attribute__((nonnull))
 
1518
void run_network_hooks(const char *mode, const char *interface,
1384
1519
                       const float delay){
1385
 
  struct dirent **direntries;
1386
 
  struct dirent *direntry;
1387
 
  int ret;
1388
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1389
 
                         alphasort);
 
1520
  struct dirent **direntries = NULL;
 
1521
  if(hookdir_fd == -1){
 
1522
    hookdir_fd = open(hookdir, O_RDONLY);
 
1523
    if(hookdir_fd == -1){
 
1524
      if(errno == ENOENT){
 
1525
        if(debug){
 
1526
          fprintf_plus(stderr, "Network hook directory \"%s\" not"
 
1527
                       " found\n", hookdir);
 
1528
        }
 
1529
      } else {
 
1530
        perror_plus("open");
 
1531
      }
 
1532
      return;
 
1533
    }
 
1534
  }
 
1535
#ifdef __GLIBC__
 
1536
#if __GLIBC_PREREQ(2, 15)
 
1537
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
 
1538
                           runnable_hook, alphasort);
 
1539
#else  /* not __GLIBC_PREREQ(2, 15) */
 
1540
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1541
                         alphasort);
 
1542
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
1543
#else   /* not __GLIBC__ */
 
1544
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1545
                         alphasort);
 
1546
#endif  /* not __GLIBC__ */
1390
1547
  if(numhooks == -1){
1391
1548
    perror_plus("scandir");
1392
 
  } else {
1393
 
    int devnull = open("/dev/null", O_RDONLY);
1394
 
    for(int i = 0; i < numhooks; i++){
1395
 
      direntry = direntries[i];
1396
 
      char *fullname = NULL;
1397
 
      ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1398
 
      if(ret < 0){
 
1549
    return;
 
1550
  }
 
1551
  struct dirent *direntry;
 
1552
  int ret;
 
1553
  int devnull = open("/dev/null", O_RDONLY);
 
1554
  for(int i = 0; i < numhooks; i++){
 
1555
    direntry = direntries[i];
 
1556
    if(debug){
 
1557
      fprintf_plus(stderr, "Running network hook \"%s\"\n",
 
1558
                   direntry->d_name);
 
1559
    }
 
1560
    pid_t hook_pid = fork();
 
1561
    if(hook_pid == 0){
 
1562
      /* Child */
 
1563
      /* Raise privileges */
 
1564
      errno = raise_privileges_permanently();
 
1565
      if(errno != 0){
 
1566
        perror_plus("Failed to raise privileges");
 
1567
        _exit(EX_NOPERM);
 
1568
      }
 
1569
      /* Set group */
 
1570
      errno = 0;
 
1571
      ret = setgid(0);
 
1572
      if(ret == -1){
 
1573
        perror_plus("setgid");
 
1574
        _exit(EX_NOPERM);
 
1575
      }
 
1576
      /* Reset supplementary groups */
 
1577
      errno = 0;
 
1578
      ret = setgroups(0, NULL);
 
1579
      if(ret == -1){
 
1580
        perror_plus("setgroups");
 
1581
        _exit(EX_NOPERM);
 
1582
      }
 
1583
      ret = dup2(devnull, STDIN_FILENO);
 
1584
      if(ret == -1){
 
1585
        perror_plus("dup2(devnull, STDIN_FILENO)");
 
1586
        _exit(EX_OSERR);
 
1587
      }
 
1588
      ret = close(devnull);
 
1589
      if(ret == -1){
 
1590
        perror_plus("close");
 
1591
        _exit(EX_OSERR);
 
1592
      }
 
1593
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
 
1594
      if(ret == -1){
 
1595
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
 
1596
        _exit(EX_OSERR);
 
1597
      }
 
1598
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
 
1599
      if(ret == -1){
 
1600
        perror_plus("setenv");
 
1601
        _exit(EX_OSERR);
 
1602
      }
 
1603
      ret = setenv("DEVICE", interface, 1);
 
1604
      if(ret == -1){
 
1605
        perror_plus("setenv");
 
1606
        _exit(EX_OSERR);
 
1607
      }
 
1608
      ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
 
1609
      if(ret == -1){
 
1610
        perror_plus("setenv");
 
1611
        _exit(EX_OSERR);
 
1612
      }
 
1613
      ret = setenv("MODE", mode, 1);
 
1614
      if(ret == -1){
 
1615
        perror_plus("setenv");
 
1616
        _exit(EX_OSERR);
 
1617
      }
 
1618
      char *delaystring;
 
1619
      ret = asprintf(&delaystring, "%f", (double)delay);
 
1620
      if(ret == -1){
1399
1621
        perror_plus("asprintf");
1400
 
        continue;
1401
 
      }
1402
 
      if(debug){
1403
 
        fprintf_plus(stderr, "Running network hook \"%s\"\n",
1404
 
                     direntry->d_name);
1405
 
      }
1406
 
      pid_t hook_pid = fork();
1407
 
      if(hook_pid == 0){
1408
 
        /* Child */
1409
 
        dup2(devnull, STDIN_FILENO);
1410
 
        close(devnull);
1411
 
        dup2(STDERR_FILENO, STDOUT_FILENO);
1412
 
        ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1413
 
        if(ret == -1){
1414
 
          perror_plus("setenv");
1415
 
          return false;
1416
 
        }
1417
 
        ret = setenv("DEVICE", interface, 1);
1418
 
        if(ret == -1){
1419
 
          perror_plus("setenv");
1420
 
          return false;
1421
 
        }
1422
 
        ret = setenv("VERBOSE", debug ? "1" : "0", 1);
1423
 
        if(ret == -1){
1424
 
          perror_plus("setenv");
1425
 
          return false;
1426
 
        }
1427
 
        ret = setenv("MODE", mode, 1);
1428
 
        if(ret == -1){
1429
 
          perror_plus("setenv");
1430
 
          return false;
1431
 
        }
1432
 
        char *delaystring;
1433
 
        ret = asprintf(&delaystring, "%f", delay);
1434
 
        if(ret == -1){
1435
 
          perror_plus("asprintf");
1436
 
          return false;
1437
 
        }
1438
 
        ret = setenv("DELAY", delaystring, 1);
1439
 
        if(ret == -1){
1440
 
          free(delaystring);
1441
 
          perror_plus("setenv");
1442
 
          return false;
1443
 
        }
 
1622
        _exit(EX_OSERR);
 
1623
      }
 
1624
      ret = setenv("DELAY", delaystring, 1);
 
1625
      if(ret == -1){
1444
1626
        free(delaystring);
1445
 
        ret = execl(fullname, direntry->d_name, mode, NULL);
1446
 
        perror_plus("execl");
1447
 
      } else {
1448
 
        int status;
1449
 
        if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1450
 
          perror_plus("waitpid");
1451
 
          free(fullname);
1452
 
          continue;
1453
 
        }
1454
 
        if(WIFEXITED(status)){
1455
 
          if(WEXITSTATUS(status) != 0){
1456
 
            fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1457
 
                         " with status %d\n", direntry->d_name,
1458
 
                         WEXITSTATUS(status));
1459
 
            free(fullname);
1460
 
            continue;
1461
 
          }
1462
 
        } else if(WIFSIGNALED(status)){
1463
 
          fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1464
 
                       " signal %d\n", direntry->d_name,
1465
 
                       WTERMSIG(status));
1466
 
          free(fullname);
1467
 
          continue;
1468
 
        } else {
1469
 
          fprintf_plus(stderr, "Warning: network hook \"%s\""
1470
 
                       " crashed\n", direntry->d_name);
1471
 
          free(fullname);
1472
 
          continue;
1473
 
        }
1474
 
      }
1475
 
      free(fullname);
1476
 
      if(debug){
1477
 
        fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1478
 
                     direntry->d_name);
1479
 
      }
1480
 
    }
1481
 
    close(devnull);
1482
 
  }
1483
 
  return true;
 
1627
        perror_plus("setenv");
 
1628
        _exit(EX_OSERR);
 
1629
      }
 
1630
      free(delaystring);
 
1631
      if(connect_to != NULL){
 
1632
        ret = setenv("CONNECT", connect_to, 1);
 
1633
        if(ret == -1){
 
1634
          perror_plus("setenv");
 
1635
          _exit(EX_OSERR);
 
1636
        }
 
1637
      }
 
1638
      int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
 
1639
      if(hook_fd == -1){
 
1640
        perror_plus("openat");
 
1641
        _exit(EXIT_FAILURE);
 
1642
      }
 
1643
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
1644
        perror_plus("close");
 
1645
        _exit(EXIT_FAILURE);
 
1646
      }
 
1647
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
 
1648
                 environ) == -1){
 
1649
        perror_plus("fexecve");
 
1650
        _exit(EXIT_FAILURE);
 
1651
      }
 
1652
    } else {
 
1653
      int status;
 
1654
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
 
1655
        perror_plus("waitpid");
 
1656
        free(direntry);
 
1657
        continue;
 
1658
      }
 
1659
      if(WIFEXITED(status)){
 
1660
        if(WEXITSTATUS(status) != 0){
 
1661
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
 
1662
                       " with status %d\n", direntry->d_name,
 
1663
                       WEXITSTATUS(status));
 
1664
          free(direntry);
 
1665
          continue;
 
1666
        }
 
1667
      } else if(WIFSIGNALED(status)){
 
1668
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
 
1669
                     " signal %d\n", direntry->d_name,
 
1670
                     WTERMSIG(status));
 
1671
        free(direntry);
 
1672
        continue;
 
1673
      } else {
 
1674
        fprintf_plus(stderr, "Warning: network hook \"%s\""
 
1675
                     " crashed\n", direntry->d_name);
 
1676
        free(direntry);
 
1677
        continue;
 
1678
      }
 
1679
    }
 
1680
    if(debug){
 
1681
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
 
1682
                   direntry->d_name);
 
1683
    }
 
1684
    free(direntry);
 
1685
  }
 
1686
  free(direntries);
 
1687
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
1688
    perror_plus("close");
 
1689
  } else {
 
1690
    hookdir_fd = -1;
 
1691
  }
 
1692
  close(devnull);
 
1693
}
 
1694
 
 
1695
__attribute__((nonnull, warn_unused_result))
 
1696
error_t bring_up_interface(const char *const interface,
 
1697
                           const float delay){
 
1698
  error_t old_errno = errno;
 
1699
  int ret;
 
1700
  struct ifreq network;
 
1701
  unsigned int if_index = if_nametoindex(interface);
 
1702
  if(if_index == 0){
 
1703
    fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
 
1704
    errno = old_errno;
 
1705
    return ENXIO;
 
1706
  }
 
1707
  
 
1708
  if(quit_now){
 
1709
    errno = old_errno;
 
1710
    return EINTR;
 
1711
  }
 
1712
  
 
1713
  if(not interface_is_up(interface)){
 
1714
    error_t ret_errno = 0, ioctl_errno = 0;
 
1715
    if(not get_flags(interface, &network)){
 
1716
      ret_errno = errno;
 
1717
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1718
                   "\"%s\"\n", interface);
 
1719
      errno = old_errno;
 
1720
      return ret_errno;
 
1721
    }
 
1722
    network.ifr_flags |= IFF_UP; /* set flag */
 
1723
    
 
1724
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1725
    if(sd == -1){
 
1726
      ret_errno = errno;
 
1727
      perror_plus("socket");
 
1728
      errno = old_errno;
 
1729
      return ret_errno;
 
1730
    }
 
1731
    
 
1732
    if(quit_now){
 
1733
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1734
      if(ret == -1){
 
1735
        perror_plus("close");
 
1736
      }
 
1737
      errno = old_errno;
 
1738
      return EINTR;
 
1739
    }
 
1740
    
 
1741
    if(debug){
 
1742
      fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
 
1743
                   interface);
 
1744
    }
 
1745
    
 
1746
    /* Raise privileges */
 
1747
    ret_errno = raise_privileges();
 
1748
    if(ret_errno != 0){
 
1749
      errno = ret_errno;
 
1750
      perror_plus("Failed to raise privileges");
 
1751
    }
 
1752
    
 
1753
#ifdef __linux__
 
1754
    int ret_linux;
 
1755
    bool restore_loglevel = false;
 
1756
    if(ret_errno == 0){
 
1757
      /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
1758
         messages about the network interface to mess up the prompt */
 
1759
      ret_linux = klogctl(8, NULL, 5);
 
1760
      if(ret_linux == -1){
 
1761
        perror_plus("klogctl");
 
1762
      } else {
 
1763
        restore_loglevel = true;
 
1764
      }
 
1765
    }
 
1766
#endif  /* __linux__ */
 
1767
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1768
    ioctl_errno = errno;
 
1769
#ifdef __linux__
 
1770
    if(restore_loglevel){
 
1771
      ret_linux = klogctl(7, NULL, 0);
 
1772
      if(ret_linux == -1){
 
1773
        perror_plus("klogctl");
 
1774
      }
 
1775
    }
 
1776
#endif  /* __linux__ */
 
1777
    
 
1778
    /* If raise_privileges() succeeded above */
 
1779
    if(ret_errno == 0){
 
1780
      /* Lower privileges */
 
1781
      ret_errno = lower_privileges();
 
1782
      if(ret_errno != 0){
 
1783
        errno = ret_errno;
 
1784
        perror_plus("Failed to lower privileges");
 
1785
      }
 
1786
    }
 
1787
    
 
1788
    /* Close the socket */
 
1789
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1790
    if(ret == -1){
 
1791
      perror_plus("close");
 
1792
    }
 
1793
    
 
1794
    if(ret_setflags == -1){
 
1795
      errno = ioctl_errno;
 
1796
      perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
 
1797
      errno = old_errno;
 
1798
      return ioctl_errno;
 
1799
    }
 
1800
  } else if(debug){
 
1801
    fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
 
1802
                 interface);
 
1803
  }
 
1804
  
 
1805
  /* Sleep checking until interface is running.
 
1806
     Check every 0.25s, up to total time of delay */
 
1807
  for(int i=0; i < delay * 4; i++){
 
1808
    if(interface_is_running(interface)){
 
1809
      break;
 
1810
    }
 
1811
    struct timespec sleeptime = { .tv_nsec = 250000000 };
 
1812
    ret = nanosleep(&sleeptime, NULL);
 
1813
    if(ret == -1 and errno != EINTR){
 
1814
      perror_plus("nanosleep");
 
1815
    }
 
1816
  }
 
1817
  
 
1818
  errno = old_errno;
 
1819
  return 0;
 
1820
}
 
1821
 
 
1822
__attribute__((nonnull, warn_unused_result))
 
1823
error_t take_down_interface(const char *const interface){
 
1824
  error_t old_errno = errno;
 
1825
  struct ifreq network;
 
1826
  unsigned int if_index = if_nametoindex(interface);
 
1827
  if(if_index == 0){
 
1828
    fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
 
1829
    errno = old_errno;
 
1830
    return ENXIO;
 
1831
  }
 
1832
  if(interface_is_up(interface)){
 
1833
    error_t ret_errno = 0, ioctl_errno = 0;
 
1834
    if(not get_flags(interface, &network) and debug){
 
1835
      ret_errno = errno;
 
1836
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1837
                   "\"%s\"\n", interface);
 
1838
      errno = old_errno;
 
1839
      return ret_errno;
 
1840
    }
 
1841
    network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
 
1842
    
 
1843
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1844
    if(sd == -1){
 
1845
      ret_errno = errno;
 
1846
      perror_plus("socket");
 
1847
      errno = old_errno;
 
1848
      return ret_errno;
 
1849
    }
 
1850
    
 
1851
    if(debug){
 
1852
      fprintf_plus(stderr, "Taking down interface \"%s\"\n",
 
1853
                   interface);
 
1854
    }
 
1855
    
 
1856
    /* Raise privileges */
 
1857
    ret_errno = raise_privileges();
 
1858
    if(ret_errno != 0){
 
1859
      errno = ret_errno;
 
1860
      perror_plus("Failed to raise privileges");
 
1861
    }
 
1862
    
 
1863
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1864
    ioctl_errno = errno;
 
1865
    
 
1866
    /* If raise_privileges() succeeded above */
 
1867
    if(ret_errno == 0){
 
1868
      /* Lower privileges */
 
1869
      ret_errno = lower_privileges();
 
1870
      if(ret_errno != 0){
 
1871
        errno = ret_errno;
 
1872
        perror_plus("Failed to lower privileges");
 
1873
      }
 
1874
    }
 
1875
    
 
1876
    /* Close the socket */
 
1877
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1878
    if(ret == -1){
 
1879
      perror_plus("close");
 
1880
    }
 
1881
    
 
1882
    if(ret_setflags == -1){
 
1883
      errno = ioctl_errno;
 
1884
      perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
 
1885
      errno = old_errno;
 
1886
      return ioctl_errno;
 
1887
    }
 
1888
  } else if(debug){
 
1889
    fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
 
1890
                 interface);
 
1891
  }
 
1892
  
 
1893
  errno = old_errno;
 
1894
  return 0;
1484
1895
}
1485
1896
 
1486
1897
int main(int argc, char *argv[]){
 
1898
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
 
1899
                        .priority = "SECURE256:!CTYPE-X.509:"
 
1900
                        "+CTYPE-OPENPGP", .current_server = NULL,
 
1901
                        .interfaces = NULL, .interfaces_size = 0 };
1487
1902
  AvahiSServiceBrowser *sb = NULL;
1488
 
  int error;
 
1903
  error_t ret_errno;
1489
1904
  int ret;
1490
1905
  intmax_t tmpmax;
1491
1906
  char *tmp;
1492
1907
  int exitcode = EXIT_SUCCESS;
1493
 
  const char *interface = "";
1494
 
  struct ifreq network;
1495
 
  int sd = -1;
1496
 
  bool take_down_interface = false;
1497
 
  uid_t uid;
1498
 
  gid_t gid;
1499
 
  char tempdir[] = "/tmp/mandosXXXXXX";
1500
 
  bool tempdir_created = false;
 
1908
  char *interfaces_to_take_down = NULL;
 
1909
  size_t interfaces_to_take_down_size = 0;
 
1910
  char run_tempdir[] = "/run/tmp/mandosXXXXXX";
 
1911
  char old_tempdir[] = "/tmp/mandosXXXXXX";
 
1912
  char *tempdir = NULL;
1501
1913
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1502
1914
  const char *seckey = PATHDIR "/" SECKEY;
1503
1915
  const char *pubkey = PATHDIR "/" PUBKEY;
 
1916
  char *interfaces_hooks = NULL;
1504
1917
  
1505
1918
  bool gnutls_initialized = false;
1506
1919
  bool gpgme_initialized = false;
1568
1981
        .group = 2 },
1569
1982
      { .name = "retry", .key = 132,
1570
1983
        .arg = "SECONDS",
1571
 
        .doc = "Retry interval used when denied by the mandos server",
 
1984
        .doc = "Retry interval used when denied by the Mandos server",
1572
1985
        .group = 2 },
1573
1986
      { .name = "network-hook-dir", .key = 133,
1574
1987
        .arg = "DIR",
1597
2010
        connect_to = arg;
1598
2011
        break;
1599
2012
      case 'i':                 /* --interface */
1600
 
        interface = arg;
 
2013
        ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
 
2014
                                 arg, (int)',');
 
2015
        if(ret_errno != 0){
 
2016
          argp_error(state, "%s", strerror(ret_errno));
 
2017
        }
1601
2018
        break;
1602
2019
      case 's':                 /* --seckey */
1603
2020
        seckey = arg;
1646
2063
        argp_state_help(state, state->out_stream,
1647
2064
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
1648
2065
      case 'V':                 /* --version */
1649
 
        fprintf_plus(state->out_stream,
1650
 
                     "Mandos plugin mandos-client: ");
1651
2066
        fprintf_plus(state->out_stream, "%s\n", argp_program_version);
1652
2067
        exit(argp_err_exit_status);
1653
2068
        break;
1678
2093
    }
1679
2094
  }
1680
2095
    
1681
 
  if(getuid() == 0){
 
2096
  {
1682
2097
    /* Work around Debian bug #633582:
1683
2098
       <http://bugs.debian.org/633582> */
1684
 
    struct stat st;
1685
 
    
1686
 
    /* Re-raise priviliges */
1687
 
    errno = 0;
1688
 
    ret = seteuid(0);
1689
 
    if(ret == -1){
1690
 
      perror_plus("seteuid");
1691
 
    }
1692
 
    
1693
 
    if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
1694
 
      int seckey_fd = open(seckey, O_RDONLY);
1695
 
      if(seckey_fd == -1){
1696
 
        perror_plus("open");
1697
 
      } else {
1698
 
        ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
1699
 
        if(ret == -1){
1700
 
          perror_plus("fstat");
1701
 
        } else {
1702
 
          if(S_ISREG(st.st_mode)
1703
 
             and st.st_uid == 0 and st.st_gid == 0){
1704
 
            ret = fchown(seckey_fd, uid, gid);
1705
 
            if(ret == -1){
1706
 
              perror_plus("fchown");
1707
 
            }
1708
 
          }
1709
 
        }
1710
 
        TEMP_FAILURE_RETRY(close(seckey_fd));
1711
 
      }
1712
 
    }
1713
 
    
1714
 
    if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
1715
 
      int pubkey_fd = open(pubkey, O_RDONLY);
1716
 
      if(pubkey_fd == -1){
1717
 
        perror_plus("open");
1718
 
      } else {
1719
 
        ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
1720
 
        if(ret == -1){
1721
 
          perror_plus("fstat");
1722
 
        } else {
1723
 
          if(S_ISREG(st.st_mode)
1724
 
             and st.st_uid == 0 and st.st_gid == 0){
1725
 
            ret = fchown(pubkey_fd, uid, gid);
1726
 
            if(ret == -1){
1727
 
              perror_plus("fchown");
1728
 
            }
1729
 
          }
1730
 
        }
1731
 
        TEMP_FAILURE_RETRY(close(pubkey_fd));
1732
 
      }
1733
 
    }
1734
 
    
1735
 
    /* Lower privileges */
1736
 
    errno = 0;
1737
 
    ret = seteuid(uid);
1738
 
    if(ret == -1){
1739
 
      perror_plus("seteuid");
 
2099
    
 
2100
    /* Re-raise privileges */
 
2101
    ret_errno = raise_privileges();
 
2102
    if(ret_errno != 0){
 
2103
      errno = ret_errno;
 
2104
      perror_plus("Failed to raise privileges");
 
2105
    } else {
 
2106
      struct stat st;
 
2107
      
 
2108
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
 
2109
        int seckey_fd = open(seckey, O_RDONLY);
 
2110
        if(seckey_fd == -1){
 
2111
          perror_plus("open");
 
2112
        } else {
 
2113
          ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
 
2114
          if(ret == -1){
 
2115
            perror_plus("fstat");
 
2116
          } else {
 
2117
            if(S_ISREG(st.st_mode)
 
2118
               and st.st_uid == 0 and st.st_gid == 0){
 
2119
              ret = fchown(seckey_fd, uid, gid);
 
2120
              if(ret == -1){
 
2121
                perror_plus("fchown");
 
2122
              }
 
2123
            }
 
2124
          }
 
2125
          TEMP_FAILURE_RETRY(close(seckey_fd));
 
2126
        }
 
2127
      }
 
2128
    
 
2129
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
 
2130
        int pubkey_fd = open(pubkey, O_RDONLY);
 
2131
        if(pubkey_fd == -1){
 
2132
          perror_plus("open");
 
2133
        } else {
 
2134
          ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
 
2135
          if(ret == -1){
 
2136
            perror_plus("fstat");
 
2137
          } else {
 
2138
            if(S_ISREG(st.st_mode)
 
2139
               and st.st_uid == 0 and st.st_gid == 0){
 
2140
              ret = fchown(pubkey_fd, uid, gid);
 
2141
              if(ret == -1){
 
2142
                perror_plus("fchown");
 
2143
              }
 
2144
            }
 
2145
          }
 
2146
          TEMP_FAILURE_RETRY(close(pubkey_fd));
 
2147
        }
 
2148
      }
 
2149
    
 
2150
      /* Lower privileges */
 
2151
      ret_errno = lower_privileges();
 
2152
      if(ret_errno != 0){
 
2153
        errno = ret_errno;
 
2154
        perror_plus("Failed to lower privileges");
 
2155
      }
 
2156
    }
 
2157
  }
 
2158
  
 
2159
  /* Remove invalid interface names (except "none") */
 
2160
  {
 
2161
    char *interface = NULL;
 
2162
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
 
2163
                                 interface))){
 
2164
      if(strcmp(interface, "none") != 0
 
2165
         and if_nametoindex(interface) == 0){
 
2166
        if(interface[0] != '\0'){
 
2167
          fprintf_plus(stderr, "Not using nonexisting interface"
 
2168
                       " \"%s\"\n", interface);
 
2169
        }
 
2170
        argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
 
2171
        interface = NULL;
 
2172
      }
1740
2173
    }
1741
2174
  }
1742
2175
  
1743
2176
  /* Run network hooks */
1744
2177
  {
1745
 
    if(getuid() == 0){
1746
 
      /* Re-raise priviliges */
1747
 
      errno = 0;
1748
 
      ret = seteuid(0);
1749
 
      if(ret == -1){
1750
 
        perror_plus("seteuid");
1751
 
      }
1752
 
    }
1753
 
    if(not run_network_hooks("start", interface, delay)){
1754
 
      goto end;
1755
 
    }
1756
 
    if(getuid() == 0){
1757
 
      /* Lower privileges */
1758
 
      errno = 0;
1759
 
      ret = seteuid(uid);
1760
 
      if(ret == -1){
1761
 
        perror_plus("seteuid");
1762
 
      }
1763
 
    }
 
2178
    if(mc.interfaces != NULL){
 
2179
      interfaces_hooks = malloc(mc.interfaces_size);
 
2180
      if(interfaces_hooks == NULL){
 
2181
        perror_plus("malloc");
 
2182
        goto end;
 
2183
      }
 
2184
      memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
 
2185
      argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
 
2186
    }
 
2187
    run_network_hooks("start", interfaces_hooks != NULL ?
 
2188
                      interfaces_hooks : "", delay);
1764
2189
  }
1765
2190
  
1766
2191
  if(not debug){
1767
2192
    avahi_set_log_function(empty_log);
1768
2193
  }
1769
2194
  
1770
 
  if(interface[0] == '\0'){
1771
 
    struct dirent **direntries;
1772
 
    /* First look for interfaces that are up */
1773
 
    ret = scandir(sys_class_net, &direntries, up_interface,
1774
 
                  alphasort);
1775
 
    if(ret == 0){
1776
 
      /* No up interfaces, look for any good interfaces */
1777
 
      free(direntries);
1778
 
      ret = scandir(sys_class_net, &direntries, good_interface,
1779
 
                    alphasort);
1780
 
    }
1781
 
    if(ret >= 1){
1782
 
      /* Pick the first interface returned */
1783
 
      interface = strdup(direntries[0]->d_name);
1784
 
      if(debug){
1785
 
        fprintf_plus(stderr, "Using interface \"%s\"\n", interface);
1786
 
      }
1787
 
      if(interface == NULL){
1788
 
        perror_plus("malloc");
1789
 
        free(direntries);
1790
 
        exitcode = EXIT_FAILURE;
1791
 
        goto end;
1792
 
      }
1793
 
      free(direntries);
1794
 
    } else {
1795
 
      free(direntries);
1796
 
      fprintf_plus(stderr, "Could not find a network interface\n");
1797
 
      exitcode = EXIT_FAILURE;
1798
 
      goto end;
1799
 
    }
1800
 
  }
1801
 
  
1802
2195
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
1803
2196
     from the signal handler */
1804
2197
  /* Initialize the pseudo-RNG for Avahi */
1805
2198
  srand((unsigned int) time(NULL));
1806
 
  mc.simple_poll = avahi_simple_poll_new();
1807
 
  if(mc.simple_poll == NULL){
 
2199
  simple_poll = avahi_simple_poll_new();
 
2200
  if(simple_poll == NULL){
1808
2201
    fprintf_plus(stderr,
1809
2202
                 "Avahi: Failed to create simple poll object.\n");
1810
2203
    exitcode = EX_UNAVAILABLE;
1874
2267
    }
1875
2268
  }
1876
2269
  
1877
 
  /* If the interface is down, bring it up */
1878
 
  if(strcmp(interface, "none") != 0){
1879
 
    if_index = (AvahiIfIndex) if_nametoindex(interface);
1880
 
    if(if_index == 0){
1881
 
      fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1882
 
      exitcode = EX_UNAVAILABLE;
1883
 
      goto end;
1884
 
    }
1885
 
    
1886
 
    if(quit_now){
1887
 
      goto end;
1888
 
    }
1889
 
    
1890
 
    /* Re-raise priviliges */
1891
 
    errno = 0;
1892
 
    ret = seteuid(0);
1893
 
    if(ret == -1){
1894
 
      perror_plus("seteuid");
1895
 
    }
1896
 
    
1897
 
#ifdef __linux__
1898
 
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1899
 
       messages about the network interface to mess up the prompt */
1900
 
    ret = klogctl(8, NULL, 5);
1901
 
    bool restore_loglevel = true;
1902
 
    if(ret == -1){
1903
 
      restore_loglevel = false;
1904
 
      perror_plus("klogctl");
1905
 
    }
1906
 
#endif  /* __linux__ */
1907
 
    
1908
 
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1909
 
    if(sd < 0){
1910
 
      perror_plus("socket");
1911
 
      exitcode = EX_OSERR;
1912
 
#ifdef __linux__
1913
 
      if(restore_loglevel){
1914
 
        ret = klogctl(7, NULL, 0);
1915
 
        if(ret == -1){
1916
 
          perror_plus("klogctl");
1917
 
        }
1918
 
      }
1919
 
#endif  /* __linux__ */
1920
 
      /* Lower privileges */
1921
 
      errno = 0;
1922
 
      ret = seteuid(uid);
1923
 
      if(ret == -1){
1924
 
        perror_plus("seteuid");
1925
 
      }
1926
 
      goto end;
1927
 
    }
1928
 
    strcpy(network.ifr_name, interface);
1929
 
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
1930
 
    if(ret == -1){
1931
 
      perror_plus("ioctl SIOCGIFFLAGS");
1932
 
#ifdef __linux__
1933
 
      if(restore_loglevel){
1934
 
        ret = klogctl(7, NULL, 0);
1935
 
        if(ret == -1){
1936
 
          perror_plus("klogctl");
1937
 
        }
1938
 
      }
1939
 
#endif  /* __linux__ */
1940
 
      exitcode = EX_OSERR;
1941
 
      /* Lower privileges */
1942
 
      errno = 0;
1943
 
      ret = seteuid(uid);
1944
 
      if(ret == -1){
1945
 
        perror_plus("seteuid");
1946
 
      }
1947
 
      goto end;
1948
 
    }
1949
 
    if((network.ifr_flags & IFF_UP) == 0){
1950
 
      network.ifr_flags |= IFF_UP;
1951
 
      take_down_interface = true;
1952
 
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
1953
 
      if(ret == -1){
1954
 
        take_down_interface = false;
1955
 
        perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1956
 
        exitcode = EX_OSERR;
1957
 
#ifdef __linux__
1958
 
        if(restore_loglevel){
1959
 
          ret = klogctl(7, NULL, 0);
1960
 
          if(ret == -1){
1961
 
            perror_plus("klogctl");
 
2270
  /* If no interfaces were specified, make a list */
 
2271
  if(mc.interfaces == NULL){
 
2272
    struct dirent **direntries = NULL;
 
2273
    /* Look for any good interfaces */
 
2274
    ret = scandir(sys_class_net, &direntries, good_interface,
 
2275
                  alphasort);
 
2276
    if(ret >= 1){
 
2277
      /* Add all found interfaces to interfaces list */
 
2278
      for(int i = 0; i < ret; ++i){
 
2279
        ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
 
2280
                             direntries[i]->d_name);
 
2281
        if(ret_errno != 0){
 
2282
          errno = ret_errno;
 
2283
          perror_plus("argz_add");
 
2284
          free(direntries[i]);
 
2285
          continue;
 
2286
        }
 
2287
        if(debug){
 
2288
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
 
2289
                       direntries[i]->d_name);
 
2290
        }
 
2291
        free(direntries[i]);
 
2292
      }
 
2293
      free(direntries);
 
2294
    } else {
 
2295
      if(ret == 0){
 
2296
        free(direntries);
 
2297
      }
 
2298
      fprintf_plus(stderr, "Could not find a network interface\n");
 
2299
      exitcode = EXIT_FAILURE;
 
2300
      goto end;
 
2301
    }
 
2302
  }
 
2303
  
 
2304
  /* Bring up interfaces which are down, and remove any "none"s */
 
2305
  {
 
2306
    char *interface = NULL;
 
2307
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
 
2308
                                 interface))){
 
2309
      /* If interface name is "none", stop bringing up interfaces.
 
2310
         Also remove all instances of "none" from the list */
 
2311
      if(strcmp(interface, "none") == 0){
 
2312
        argz_delete(&mc.interfaces, &mc.interfaces_size,
 
2313
                    interface);
 
2314
        interface = NULL;
 
2315
        while((interface = argz_next(mc.interfaces,
 
2316
                                     mc.interfaces_size, interface))){
 
2317
          if(strcmp(interface, "none") == 0){
 
2318
            argz_delete(&mc.interfaces, &mc.interfaces_size,
 
2319
                        interface);
 
2320
            interface = NULL;
1962
2321
          }
1963
2322
        }
1964
 
#endif  /* __linux__ */
1965
 
        /* Lower privileges */
1966
 
        errno = 0;
1967
 
        ret = seteuid(uid);
1968
 
        if(ret == -1){
1969
 
          perror_plus("seteuid");
1970
 
        }
1971
 
        goto end;
1972
 
      }
1973
 
    }
1974
 
    /* Sleep checking until interface is running.
1975
 
       Check every 0.25s, up to total time of delay */
1976
 
    for(int i=0; i < delay * 4; i++){
1977
 
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
1978
 
      if(ret == -1){
1979
 
        perror_plus("ioctl SIOCGIFFLAGS");
1980
 
      } else if(network.ifr_flags & IFF_RUNNING){
1981
2323
        break;
1982
2324
      }
1983
 
      struct timespec sleeptime = { .tv_nsec = 250000000 };
1984
 
      ret = nanosleep(&sleeptime, NULL);
1985
 
      if(ret == -1 and errno != EINTR){
1986
 
        perror_plus("nanosleep");
1987
 
      }
1988
 
    }
1989
 
    if(not take_down_interface){
1990
 
      /* We won't need the socket anymore */
1991
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
1992
 
      if(ret == -1){
1993
 
        perror_plus("close");
1994
 
      }
1995
 
    }
1996
 
#ifdef __linux__
1997
 
    if(restore_loglevel){
1998
 
      /* Restores kernel loglevel to default */
1999
 
      ret = klogctl(7, NULL, 0);
2000
 
      if(ret == -1){
2001
 
        perror_plus("klogctl");
2002
 
      }
2003
 
    }
2004
 
#endif  /* __linux__ */
2005
 
    /* Lower privileges */
2006
 
    errno = 0;
2007
 
    /* Lower privileges */
2008
 
    ret = seteuid(uid);
2009
 
    if(ret == -1){
2010
 
      perror_plus("seteuid");
2011
 
    }
 
2325
      bool interface_was_up = interface_is_up(interface);
 
2326
      errno = bring_up_interface(interface, delay);
 
2327
      if(not interface_was_up){
 
2328
        if(errno != 0){
 
2329
          perror_plus("Failed to bring up interface");
 
2330
        } else {
 
2331
          errno = argz_add(&interfaces_to_take_down,
 
2332
                           &interfaces_to_take_down_size,
 
2333
                           interface);
 
2334
          if(errno != 0){
 
2335
            perror_plus("argz_add");
 
2336
          }
 
2337
        }
 
2338
      }
 
2339
    }
 
2340
    if(debug and (interfaces_to_take_down == NULL)){
 
2341
      fprintf_plus(stderr, "No interfaces were brought up\n");
 
2342
    }
 
2343
  }
 
2344
  
 
2345
  /* If we only got one interface, explicitly use only that one */
 
2346
  if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
 
2347
    if(debug){
 
2348
      fprintf_plus(stderr, "Using only interface \"%s\"\n",
 
2349
                   mc.interfaces);
 
2350
    }
 
2351
    if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2012
2352
  }
2013
2353
  
2014
2354
  if(quit_now){
2015
2355
    goto end;
2016
2356
  }
2017
2357
  
2018
 
  ret = init_gnutls_global(pubkey, seckey);
 
2358
  ret = init_gnutls_global(pubkey, seckey, &mc);
2019
2359
  if(ret == -1){
2020
2360
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2021
2361
    exitcode = EX_UNAVAILABLE;
2028
2368
    goto end;
2029
2369
  }
2030
2370
  
2031
 
  if(mkdtemp(tempdir) == NULL){
 
2371
  /* Try /run/tmp before /tmp */
 
2372
  tempdir = mkdtemp(run_tempdir);
 
2373
  if(tempdir == NULL and errno == ENOENT){
 
2374
      if(debug){
 
2375
        fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
 
2376
                     run_tempdir, old_tempdir);
 
2377
      }
 
2378
      tempdir = mkdtemp(old_tempdir);
 
2379
  }
 
2380
  if(tempdir == NULL){
2032
2381
    perror_plus("mkdtemp");
2033
2382
    goto end;
2034
2383
  }
2035
 
  tempdir_created = true;
2036
2384
  
2037
2385
  if(quit_now){
2038
2386
    goto end;
2039
2387
  }
2040
2388
  
2041
 
  if(not init_gpgme(pubkey, seckey, tempdir)){
 
2389
  if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2042
2390
    fprintf_plus(stderr, "init_gpgme failed\n");
2043
2391
    exitcode = EX_UNAVAILABLE;
2044
2392
    goto end;
2054
2402
    /* Connect directly, do not use Zeroconf */
2055
2403
    /* (Mainly meant for debugging) */
2056
2404
    char *address = strrchr(connect_to, ':');
 
2405
    
2057
2406
    if(address == NULL){
2058
2407
      fprintf_plus(stderr, "No colon in address\n");
2059
2408
      exitcode = EX_USAGE;
2064
2413
      goto end;
2065
2414
    }
2066
2415
    
2067
 
    uint16_t port;
 
2416
    in_port_t port;
2068
2417
    errno = 0;
2069
2418
    tmpmax = strtoimax(address+1, &tmp, 10);
2070
2419
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
2071
 
       or tmpmax != (uint16_t)tmpmax){
 
2420
       or tmpmax != (in_port_t)tmpmax){
2072
2421
      fprintf_plus(stderr, "Bad port number\n");
2073
2422
      exitcode = EX_USAGE;
2074
2423
      goto end;
2075
2424
    }
2076
 
  
 
2425
    
2077
2426
    if(quit_now){
2078
2427
      goto end;
2079
2428
    }
2080
2429
    
2081
 
    port = (uint16_t)tmpmax;
 
2430
    port = (in_port_t)tmpmax;
2082
2431
    *address = '\0';
2083
2432
    /* Colon in address indicates IPv6 */
2084
2433
    int af;
2100
2449
    }
2101
2450
    
2102
2451
    while(not quit_now){
2103
 
      ret = start_mandos_communication(address, port, if_index, af);
 
2452
      ret = start_mandos_communication(address, port, if_index, af,
 
2453
                                       &mc);
2104
2454
      if(quit_now or ret == 0){
2105
2455
        break;
2106
2456
      }
2108
2458
        fprintf_plus(stderr, "Retrying in %d seconds\n",
2109
2459
                     (int)retry_interval);
2110
2460
      }
2111
 
      sleep((int)retry_interval);
 
2461
      sleep((unsigned int)retry_interval);
2112
2462
    }
2113
2463
    
2114
 
    if (not quit_now){
 
2464
    if(not quit_now){
2115
2465
      exitcode = EXIT_SUCCESS;
2116
2466
    }
2117
2467
    
2132
2482
    config.publish_domain = 0;
2133
2483
    
2134
2484
    /* Allocate a new server */
2135
 
    mc.server = avahi_server_new(avahi_simple_poll_get
2136
 
                                 (mc.simple_poll), &config, NULL,
2137
 
                                 NULL, &error);
 
2485
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
 
2486
                                 &config, NULL, NULL, &ret_errno);
2138
2487
    
2139
2488
    /* Free the Avahi configuration data */
2140
2489
    avahi_server_config_free(&config);
2143
2492
  /* Check if creating the Avahi server object succeeded */
2144
2493
  if(mc.server == NULL){
2145
2494
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2146
 
                 avahi_strerror(error));
 
2495
                 avahi_strerror(ret_errno));
2147
2496
    exitcode = EX_UNAVAILABLE;
2148
2497
    goto end;
2149
2498
  }
2155
2504
  /* Create the Avahi service browser */
2156
2505
  sb = avahi_s_service_browser_new(mc.server, if_index,
2157
2506
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2158
 
                                   NULL, 0, browse_callback, NULL);
 
2507
                                   NULL, 0, browse_callback,
 
2508
                                   (void *)&mc);
2159
2509
  if(sb == NULL){
2160
2510
    fprintf_plus(stderr, "Failed to create service browser: %s\n",
2161
2511
                 avahi_strerror(avahi_server_errno(mc.server)));
2172
2522
  if(debug){
2173
2523
    fprintf_plus(stderr, "Starting Avahi loop search\n");
2174
2524
  }
2175
 
 
2176
 
  ret = avahi_loop_with_timeout(mc.simple_poll,
2177
 
                                (int)(retry_interval * 1000));
 
2525
  
 
2526
  ret = avahi_loop_with_timeout(simple_poll,
 
2527
                                (int)(retry_interval * 1000), &mc);
2178
2528
  if(debug){
2179
2529
    fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2180
2530
                 (ret == 0) ? "successfully" : "with error");
2187
2537
  }
2188
2538
  
2189
2539
  /* Cleanup things */
 
2540
  free(mc.interfaces);
 
2541
  
2190
2542
  if(sb != NULL)
2191
2543
    avahi_s_service_browser_free(sb);
2192
2544
  
2193
2545
  if(mc.server != NULL)
2194
2546
    avahi_server_free(mc.server);
2195
2547
  
2196
 
  if(mc.simple_poll != NULL)
2197
 
    avahi_simple_poll_free(mc.simple_poll);
 
2548
  if(simple_poll != NULL)
 
2549
    avahi_simple_poll_free(simple_poll);
2198
2550
  
2199
2551
  if(gnutls_initialized){
2200
2552
    gnutls_certificate_free_credentials(mc.cred);
2212
2564
    mc.current_server->prev->next = NULL;
2213
2565
    while(mc.current_server != NULL){
2214
2566
      server *next = mc.current_server->next;
 
2567
#ifdef __GNUC__
 
2568
#pragma GCC diagnostic push
 
2569
#pragma GCC diagnostic ignored "-Wcast-qual"
 
2570
#endif
 
2571
      free((char *)(mc.current_server->ip));
 
2572
#ifdef __GNUC__
 
2573
#pragma GCC diagnostic pop
 
2574
#endif
2215
2575
      free(mc.current_server);
2216
2576
      mc.current_server = next;
2217
2577
    }
2218
2578
  }
2219
2579
  
2220
 
  /* Re-raise priviliges */
 
2580
  /* Re-raise privileges */
2221
2581
  {
2222
 
    if(getuid() == 0){
2223
 
      errno = 0;
2224
 
      ret = seteuid(0);
2225
 
      if(ret == -1){
2226
 
        perror_plus("seteuid");
2227
 
      }
2228
 
    }
2229
 
    
2230
 
    /* Run network hooks */
2231
 
    run_network_hooks("stop", interface, delay);
2232
 
    
2233
 
    /* Take down the network interface */
2234
 
    if(take_down_interface and geteuid() == 0){
2235
 
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
2236
 
      if(ret == -1){
2237
 
        perror_plus("ioctl SIOCGIFFLAGS");
2238
 
      } else if(network.ifr_flags & IFF_UP){
2239
 
        network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2240
 
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
2241
 
        if(ret == -1){
2242
 
          perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2243
 
        }
2244
 
      }
2245
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
2246
 
      if(ret == -1){
2247
 
        perror_plus("close");
2248
 
      }
2249
 
    }
2250
 
  }
2251
 
  if(getuid() == 0){
2252
 
    /* Lower privileges permanently */
2253
 
    errno = 0;
2254
 
    ret = setuid(uid);
2255
 
    if(ret == -1){
2256
 
      perror_plus("setuid");
2257
 
    }
2258
 
  }
 
2582
    ret_errno = raise_privileges();
 
2583
    if(ret_errno != 0){
 
2584
      errno = ret_errno;
 
2585
      perror_plus("Failed to raise privileges");
 
2586
    } else {
 
2587
      
 
2588
      /* Run network hooks */
 
2589
      run_network_hooks("stop", interfaces_hooks != NULL ?
 
2590
                        interfaces_hooks : "", delay);
 
2591
      
 
2592
      /* Take down the network interfaces which were brought up */
 
2593
      {
 
2594
        char *interface = NULL;
 
2595
        while((interface=argz_next(interfaces_to_take_down,
 
2596
                                   interfaces_to_take_down_size,
 
2597
                                   interface))){
 
2598
          ret_errno = take_down_interface(interface);
 
2599
          if(ret_errno != 0){
 
2600
            errno = ret_errno;
 
2601
            perror_plus("Failed to take down interface");
 
2602
          }
 
2603
        }
 
2604
        if(debug and (interfaces_to_take_down == NULL)){
 
2605
          fprintf_plus(stderr, "No interfaces needed to be taken"
 
2606
                       " down\n");
 
2607
        }
 
2608
      }
 
2609
    }
 
2610
    
 
2611
    ret_errno = lower_privileges_permanently();
 
2612
    if(ret_errno != 0){
 
2613
      errno = ret_errno;
 
2614
      perror_plus("Failed to lower privileges permanently");
 
2615
    }
 
2616
  }
 
2617
  
 
2618
  free(interfaces_to_take_down);
 
2619
  free(interfaces_hooks);
2259
2620
  
2260
2621
  /* Removes the GPGME temp directory and all files inside */
2261
 
  if(tempdir_created){
 
2622
  if(tempdir != NULL){
2262
2623
    struct dirent **direntries = NULL;
2263
 
    struct dirent *direntry = NULL;
2264
 
    int numentries = scandir(tempdir, &direntries, notdotentries,
2265
 
                             alphasort);
2266
 
    if (numentries > 0){
2267
 
      for(int i = 0; i < numentries; i++){
2268
 
        direntry = direntries[i];
2269
 
        char *fullname = NULL;
2270
 
        ret = asprintf(&fullname, "%s/%s", tempdir,
2271
 
                       direntry->d_name);
2272
 
        if(ret < 0){
2273
 
          perror_plus("asprintf");
2274
 
          continue;
2275
 
        }
2276
 
        ret = remove(fullname);
2277
 
        if(ret == -1){
2278
 
          fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2279
 
                       strerror(errno));
2280
 
        }
2281
 
        free(fullname);
 
2624
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
 
2625
                                                  O_NOFOLLOW));
 
2626
    if(tempdir_fd == -1){
 
2627
      perror_plus("open");
 
2628
    } else {
 
2629
#ifdef __GLIBC__
 
2630
#if __GLIBC_PREREQ(2, 15)
 
2631
      int numentries = scandirat(tempdir_fd, ".", &direntries,
 
2632
                                 notdotentries, alphasort);
 
2633
#else  /* not __GLIBC_PREREQ(2, 15) */
 
2634
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2635
                               alphasort);
 
2636
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
2637
#else   /* not __GLIBC__ */
 
2638
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2639
                               alphasort);
 
2640
#endif  /* not __GLIBC__ */
 
2641
      if(numentries >= 0){
 
2642
        for(int i = 0; i < numentries; i++){
 
2643
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
 
2644
          if(ret == -1){
 
2645
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
 
2646
                         " \"%s\", 0): %s\n", tempdir,
 
2647
                         direntries[i]->d_name, strerror(errno));
 
2648
          }
 
2649
          free(direntries[i]);
 
2650
        }
 
2651
        
 
2652
        /* need to clean even if 0 because man page doesn't specify */
 
2653
        free(direntries);
 
2654
        if(numentries == -1){
 
2655
          perror_plus("scandir");
 
2656
        }
 
2657
        ret = rmdir(tempdir);
 
2658
        if(ret == -1 and errno != ENOENT){
 
2659
          perror_plus("rmdir");
 
2660
        }
2282
2661
      }
2283
 
    }
2284
 
 
2285
 
    /* need to clean even if 0 because man page doesn't specify */
2286
 
    free(direntries);
2287
 
    if (numentries == -1){
2288
 
      perror_plus("scandir");
2289
 
    }
2290
 
    ret = rmdir(tempdir);
2291
 
    if(ret == -1 and errno != ENOENT){
2292
 
      perror_plus("rmdir");
 
2662
      TEMP_FAILURE_RETRY(close(tempdir_fd));
2293
2663
    }
2294
2664
  }
2295
2665