/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-2012 Teddy Hogeborn
13
 
 * Copyright © 2008-2012 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() */
 
43
                                   stdout, ferror() */
44
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(),
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,
72
73
                                */
73
74
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
74
75
                                   getuid(), getgid(), seteuid(),
75
 
                                   setgid(), pause(), _exit() */
76
 
#include <arpa/inet.h>          /* inet_pton(), htons, inet_ntop() */
 
76
                                   setgid(), pause(), _exit(),
 
77
                                   unlinkat() */
 
78
#include <arpa/inet.h>          /* inet_pton(), htons() */
77
79
#include <iso646.h>             /* not, or, and */
78
80
#include <argp.h>               /* struct argp_option, error_t, struct
79
81
                                   argp_state, struct argp,
91
93
                                   argz_delete(), argz_append(),
92
94
                                   argz_stringify(), argz_add(),
93
95
                                   argz_count() */
 
96
#include <netdb.h>              /* getnameinfo(), NI_NUMERICHOST,
 
97
                                   EAI_SYSTEM, gai_strerror() */
94
98
 
95
99
#ifdef __linux__
96
100
#include <sys/klog.h>           /* klogctl() */
138
142
static const char sys_class_net[] = "/sys/class/net";
139
143
char *connect_to = NULL;
140
144
const char *hookdir = HOOKDIR;
 
145
int hookdir_fd = -1;
141
146
uid_t uid = 65534;
142
147
gid_t gid = 65534;
143
148
 
161
166
  const char *priority;
162
167
  gpgme_ctx_t ctx;
163
168
  server *current_server;
 
169
  char *interfaces;
 
170
  size_t interfaces_size;
164
171
} mandos_context;
165
172
 
166
173
/* global so signal handler can reach it*/
178
185
  perror(print_text);
179
186
}
180
187
 
181
 
__attribute__((format (gnu_printf, 2, 3)))
 
188
__attribute__((format (gnu_printf, 2, 3), nonnull))
182
189
int fprintf_plus(FILE *stream, const char *format, ...){
183
190
  va_list ap;
184
191
  va_start (ap, format);
185
192
  
186
193
  TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
187
194
                             program_invocation_short_name));
188
 
  return TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
 
195
  return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
189
196
}
190
197
 
191
198
/*
193
200
 * bytes. "buffer_capacity" is how much is currently allocated,
194
201
 * "buffer_length" is how much is already used.
195
202
 */
 
203
__attribute__((nonnull, warn_unused_result))
196
204
size_t incbuffer(char **buffer, size_t buffer_length,
197
205
                 size_t buffer_capacity){
198
206
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
199
 
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
200
 
    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;
201
213
      return 0;
202
214
    }
 
215
    *buffer = new_buf;
203
216
    buffer_capacity += BUFFER_SIZE;
204
217
  }
205
218
  return buffer_capacity;
206
219
}
207
220
 
208
221
/* Add server to set of servers to retry periodically */
 
222
__attribute__((nonnull, warn_unused_result))
209
223
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
210
224
                int af, server **current_server){
211
225
  int ret;
220
234
                          .af = af };
221
235
  if(new_server->ip == NULL){
222
236
    perror_plus("strdup");
 
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);
223
252
    return false;
224
253
  }
225
254
  /* Special case of first server */
227
256
    new_server->next = new_server;
228
257
    new_server->prev = new_server;
229
258
    *current_server = new_server;
230
 
  /* Place the new server last in the list */
231
259
  } else {
 
260
    /* Place the new server last in the list */
232
261
    new_server->next = *current_server;
233
262
    new_server->prev = (*current_server)->prev;
234
263
    new_server->prev->next = new_server;
235
264
    (*current_server)->prev = new_server;
236
265
  }
237
 
  ret = clock_gettime(CLOCK_MONOTONIC, &(*current_server)->last_seen);
238
 
  if(ret == -1){
239
 
    perror_plus("clock_gettime");
240
 
    return false;
241
 
  }
242
266
  return true;
243
267
}
244
268
 
245
269
/* 
246
270
 * Initialize GPGME.
247
271
 */
248
 
static bool init_gpgme(const char *seckey, const char *pubkey,
249
 
                       const char *tempdir, mandos_context *mc){
 
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){
250
277
  gpgme_error_t rc;
251
278
  gpgme_engine_info_t engine_info;
252
279
  
253
280
  /*
254
281
   * Helper function to insert pub and seckey to the engine keyring.
255
282
   */
256
 
  bool import_key(const char *filename){
 
283
  bool import_key(const char * const filename){
257
284
    int ret;
258
285
    int fd;
259
286
    gpgme_data_t pgp_data;
340
367
 * Decrypt OpenPGP data.
341
368
 * Returns -1 on error
342
369
 */
 
370
__attribute__((nonnull, warn_unused_result))
343
371
static ssize_t pgp_packet_decrypt(const char *cryptotext,
344
372
                                  size_t crypto_size,
345
373
                                  char **plaintext,
465
493
  return plaintext_length;
466
494
}
467
495
 
468
 
static const char * safer_gnutls_strerror(int value){
 
496
__attribute__((warn_unused_result))
 
497
static const char *safer_gnutls_strerror(int value){
469
498
  const char *ret = gnutls_strerror(value);
470
499
  if(ret == NULL)
471
500
    ret = "(unknown)";
473
502
}
474
503
 
475
504
/* GnuTLS log function callback */
 
505
__attribute__((nonnull))
476
506
static void debuggnutls(__attribute__((unused)) int level,
477
507
                        const char* string){
478
508
  fprintf_plus(stderr, "GnuTLS: %s", string);
479
509
}
480
510
 
 
511
__attribute__((nonnull, warn_unused_result))
481
512
static int init_gnutls_global(const char *pubkeyfilename,
482
513
                              const char *seckeyfilename,
483
514
                              mandos_context *mc){
557
588
  return -1;
558
589
}
559
590
 
 
591
__attribute__((nonnull, warn_unused_result))
560
592
static int init_gnutls_session(gnutls_session_t *session,
561
593
                               mandos_context *mc){
562
594
  int ret;
619
651
                      __attribute__((unused)) const char *txt){}
620
652
 
621
653
/* Called when a Mandos server is found */
 
654
__attribute__((nonnull, warn_unused_result))
622
655
static int start_mandos_communication(const char *ip, in_port_t port,
623
656
                                      AvahiIfIndex if_index,
624
657
                                      int af, mandos_context *mc){
625
658
  int ret, tcp_sd = -1;
626
659
  ssize_t sret;
627
 
  union {
628
 
    struct sockaddr_in in;
629
 
    struct sockaddr_in6 in6;
630
 
  } to;
 
660
  struct sockaddr_storage to;
631
661
  char *buffer = NULL;
632
662
  char *decrypted_buffer = NULL;
633
663
  size_t buffer_length = 0;
657
687
    return -1;
658
688
  }
659
689
  
 
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
  
660
722
  ret = init_gnutls_session(&session, mc);
661
723
  if(ret != 0){
662
724
    return -1;
682
744
  
683
745
  memset(&to, 0, sizeof(to));
684
746
  if(af == AF_INET6){
685
 
    to.in6.sin6_family = (sa_family_t)af;
686
 
    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);
687
749
  } else {                      /* IPv4 */
688
 
    to.in.sin_family = (sa_family_t)af;
689
 
    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);
690
752
  }
691
753
  if(ret < 0 ){
692
754
    int e = errno;
701
763
    goto mandos_end;
702
764
  }
703
765
  if(af == AF_INET6){
704
 
    to.in6.sin6_port = htons(port);    
705
 
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
706
 
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
707
 
                                -Wunreachable-code*/
 
766
    ((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
 
767
    if(IN6_IS_ADDR_LINKLOCAL
 
768
       (&((struct sockaddr_in6 *)&to)->sin6_addr)){
708
769
      if(if_index == AVAHI_IF_UNSPEC){
709
770
        fprintf_plus(stderr, "An IPv6 link-local address is"
710
771
                     " incomplete without a network interface\n");
712
773
        goto mandos_end;
713
774
      }
714
775
      /* Set the network interface number as scope */
715
 
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
776
      ((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
716
777
    }
717
778
  } else {
718
 
    to.in.sin_port = htons(port); /* Spurious warnings from
719
 
                                     -Wconversion and
720
 
                                     -Wunreachable-code */
 
779
    ((struct sockaddr_in *)&to)->sin_port = htons(port);
721
780
  }
722
781
  
723
782
  if(quit_now){
740
799
    }
741
800
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
742
801
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
743
 
    const char *pcret;
744
802
    if(af == AF_INET6){
745
 
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
746
 
                        sizeof(addrstr));
 
803
      ret = getnameinfo((struct sockaddr *)&to,
 
804
                        sizeof(struct sockaddr_in6),
 
805
                        addrstr, sizeof(addrstr), NULL, 0,
 
806
                        NI_NUMERICHOST);
747
807
    } else {
748
 
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
749
 
                        sizeof(addrstr));
 
808
      ret = getnameinfo((struct sockaddr *)&to,
 
809
                        sizeof(struct sockaddr_in),
 
810
                        addrstr, sizeof(addrstr), NULL, 0,
 
811
                        NI_NUMERICHOST);
750
812
    }
751
 
    if(pcret == NULL){
752
 
      perror_plus("inet_ntop");
753
 
    } else {
754
 
      if(strcmp(addrstr, ip) != 0){
755
 
        fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
756
 
      }
 
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);
757
819
    }
758
820
  }
759
821
  
763
825
  }
764
826
  
765
827
  if(af == AF_INET6){
766
 
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
828
    ret = connect(tcp_sd, (struct sockaddr *)&to,
 
829
                  sizeof(struct sockaddr_in6));
767
830
  } else {
768
 
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
831
    ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
 
832
                  sizeof(struct sockaddr_in));
769
833
  }
770
834
  if(ret < 0){
771
 
    if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
 
835
    if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
772
836
      int e = errno;
773
837
      perror_plus("connect");
774
838
      errno = e;
989
1053
  return retval;
990
1054
}
991
1055
 
 
1056
__attribute__((nonnull))
992
1057
static void resolve_callback(AvahiSServiceResolver *r,
993
1058
                             AvahiIfIndex interface,
994
1059
                             AvahiProtocol proto,
1002
1067
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
1003
1068
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
1004
1069
                             flags,
1005
 
                             void* mc){
 
1070
                             void *mc){
1006
1071
  if(r == NULL){
1007
1072
    return;
1008
1073
  }
1011
1076
     timed out */
1012
1077
  
1013
1078
  if(quit_now){
 
1079
    avahi_s_service_resolver_free(r);
1014
1080
    return;
1015
1081
  }
1016
1082
  
1061
1127
                            const char *domain,
1062
1128
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
1063
1129
                            flags,
1064
 
                            void* mc){
 
1130
                            void *mc){
1065
1131
  if(b == NULL){
1066
1132
    return;
1067
1133
  }
1127
1193
  errno = old_errno;
1128
1194
}
1129
1195
 
 
1196
__attribute__((nonnull, warn_unused_result))
1130
1197
bool get_flags(const char *ifname, struct ifreq *ifr){
1131
1198
  int ret;
1132
1199
  error_t ret_errno;
1151
1218
  return true;
1152
1219
}
1153
1220
 
 
1221
__attribute__((nonnull, warn_unused_result))
1154
1222
bool good_flags(const char *ifname, const struct ifreq *ifr){
1155
1223
  
1156
1224
  /* Reject the loopback device */
1198
1266
 * corresponds to an acceptable network device.
1199
1267
 * (This function is passed to scandir(3) as a filter function.)
1200
1268
 */
 
1269
__attribute__((nonnull, warn_unused_result))
1201
1270
int good_interface(const struct dirent *if_entry){
1202
1271
  if(if_entry->d_name[0] == '.'){
1203
1272
    return 0;
1221
1290
/* 
1222
1291
 * This function determines if a network interface is up.
1223
1292
 */
 
1293
__attribute__((nonnull, warn_unused_result))
1224
1294
bool interface_is_up(const char *interface){
1225
1295
  struct ifreq ifr;
1226
1296
  if(not get_flags(interface, &ifr)){
1237
1307
/* 
1238
1308
 * This function determines if a network interface is running
1239
1309
 */
 
1310
__attribute__((nonnull, warn_unused_result))
1240
1311
bool interface_is_running(const char *interface){
1241
1312
  struct ifreq ifr;
1242
1313
  if(not get_flags(interface, &ifr)){
1250
1321
  return (bool)(ifr.ifr_flags & IFF_RUNNING);
1251
1322
}
1252
1323
 
 
1324
__attribute__((nonnull, pure, warn_unused_result))
1253
1325
int notdotentries(const struct dirent *direntry){
1254
1326
  /* Skip "." and ".." */
1255
1327
  if(direntry->d_name[0] == '.'
1262
1334
}
1263
1335
 
1264
1336
/* Is this directory entry a runnable program? */
 
1337
__attribute__((nonnull, warn_unused_result))
1265
1338
int runnable_hook(const struct dirent *direntry){
1266
1339
  int ret;
1267
1340
  size_t sret;
1275
1348
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1276
1349
                "abcdefghijklmnopqrstuvwxyz"
1277
1350
                "0123456789"
1278
 
                "_-");
 
1351
                "_.-");
1279
1352
  if((direntry->d_name)[sret] != '\0'){
1280
1353
    /* Contains non-allowed characters */
1281
1354
    if(debug){
1285
1358
    return 0;
1286
1359
  }
1287
1360
  
1288
 
  char *fullname = NULL;
1289
 
  ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1290
 
  if(ret < 0){
1291
 
    perror_plus("asprintf");
1292
 
    return 0;
1293
 
  }
1294
 
  
1295
 
  ret = stat(fullname, &st);
 
1361
  ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1296
1362
  if(ret == -1){
1297
1363
    if(debug){
1298
1364
      perror_plus("Could not stat hook");
1322
1388
  return 1;
1323
1389
}
1324
1390
 
 
1391
__attribute__((nonnull, warn_unused_result))
1325
1392
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1326
1393
                            mandos_context *mc){
1327
1394
  int ret;
1331
1398
  
1332
1399
  while(true){
1333
1400
    if(mc->current_server == NULL){
1334
 
      if (debug){
 
1401
      if(debug){
1335
1402
        fprintf_plus(stderr, "Wait until first server is found."
1336
1403
                     " No timeout!\n");
1337
1404
      }
1338
1405
      ret = avahi_simple_poll_iterate(s, -1);
1339
1406
    } else {
1340
 
      if (debug){
 
1407
      if(debug){
1341
1408
        fprintf_plus(stderr, "Check current_server if we should run"
1342
1409
                     " it, or wait\n");
1343
1410
      }
1360
1427
                     - ((intmax_t)waited_time.tv_sec * 1000))
1361
1428
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
1362
1429
      
1363
 
      if (debug){
 
1430
      if(debug){
1364
1431
        fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1365
1432
                     block_time);
1366
1433
      }
1388
1455
      ret = avahi_simple_poll_iterate(s, (int)block_time);
1389
1456
    }
1390
1457
    if(ret != 0){
1391
 
      if (ret > 0 or errno != EINTR){
 
1458
      if(ret > 0 or errno != EINTR){
1392
1459
        return (ret != 1) ? ret : 0;
1393
1460
      }
1394
1461
    }
1396
1463
}
1397
1464
 
1398
1465
/* Set effective uid to 0, return errno */
 
1466
__attribute__((warn_unused_result))
1399
1467
error_t raise_privileges(void){
1400
1468
  error_t old_errno = errno;
1401
1469
  error_t ret_errno = 0;
1402
1470
  if(seteuid(0) == -1){
1403
1471
    ret_errno = errno;
1404
 
    perror_plus("seteuid");
1405
1472
  }
1406
1473
  errno = old_errno;
1407
1474
  return ret_errno;
1408
1475
}
1409
1476
 
1410
1477
/* Set effective and real user ID to 0.  Return errno. */
 
1478
__attribute__((warn_unused_result))
1411
1479
error_t raise_privileges_permanently(void){
1412
1480
  error_t old_errno = errno;
1413
1481
  error_t ret_errno = raise_privileges();
1417
1485
  }
1418
1486
  if(setuid(0) == -1){
1419
1487
    ret_errno = errno;
1420
 
    perror_plus("seteuid");
1421
1488
  }
1422
1489
  errno = old_errno;
1423
1490
  return ret_errno;
1424
1491
}
1425
1492
 
1426
1493
/* Set effective user ID to unprivileged saved user ID */
 
1494
__attribute__((warn_unused_result))
1427
1495
error_t lower_privileges(void){
1428
1496
  error_t old_errno = errno;
1429
1497
  error_t ret_errno = 0;
1430
1498
  if(seteuid(uid) == -1){
1431
1499
    ret_errno = errno;
1432
 
    perror_plus("seteuid");
1433
1500
  }
1434
1501
  errno = old_errno;
1435
1502
  return ret_errno;
1436
1503
}
1437
1504
 
1438
1505
/* Lower privileges permanently */
 
1506
__attribute__((warn_unused_result))
1439
1507
error_t lower_privileges_permanently(void){
1440
1508
  error_t old_errno = errno;
1441
1509
  error_t ret_errno = 0;
1442
1510
  if(setuid(uid) == -1){
1443
1511
    ret_errno = errno;
1444
 
    perror_plus("setuid");
1445
1512
  }
1446
1513
  errno = old_errno;
1447
1514
  return ret_errno;
1448
1515
}
1449
1516
 
1450
 
bool run_network_hooks(const char *mode, const char *interface,
 
1517
__attribute__((nonnull))
 
1518
void run_network_hooks(const char *mode, const char *interface,
1451
1519
                       const float delay){
1452
 
  struct dirent **direntries;
 
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__ */
 
1547
  if(numhooks == -1){
 
1548
    perror_plus("scandir");
 
1549
    return;
 
1550
  }
1453
1551
  struct dirent *direntry;
1454
1552
  int ret;
1455
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1456
 
                         alphasort);
1457
 
  if(numhooks == -1){
1458
 
    if(errno == ENOENT){
1459
 
      if(debug){
1460
 
        fprintf_plus(stderr, "Network hook directory \"%s\" not"
1461
 
                     " found\n", hookdir);
1462
 
      }
1463
 
    } else {
1464
 
      perror_plus("scandir");
 
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);
1465
1559
    }
1466
 
  } else {
1467
 
    int devnull = open("/dev/null", O_RDONLY);
1468
 
    for(int i = 0; i < numhooks; i++){
1469
 
      direntry = direntries[i];
1470
 
      char *fullname = NULL;
1471
 
      ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1472
 
      if(ret < 0){
 
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){
1473
1621
        perror_plus("asprintf");
1474
 
        continue;
1475
 
      }
1476
 
      if(debug){
1477
 
        fprintf_plus(stderr, "Running network hook \"%s\"\n",
1478
 
                     direntry->d_name);
1479
 
      }
1480
 
      pid_t hook_pid = fork();
1481
 
      if(hook_pid == 0){
1482
 
        /* Child */
1483
 
        /* Raise privileges */
1484
 
        raise_privileges_permanently();
1485
 
        /* Set group */
1486
 
        errno = 0;
1487
 
        ret = setgid(0);
1488
 
        if(ret == -1){
1489
 
          perror_plus("setgid");
1490
 
        }
1491
 
        /* Reset supplementary groups */
1492
 
        errno = 0;
1493
 
        ret = setgroups(0, NULL);
1494
 
        if(ret == -1){
1495
 
          perror_plus("setgroups");
1496
 
        }
1497
 
        dup2(devnull, STDIN_FILENO);
1498
 
        close(devnull);
1499
 
        dup2(STDERR_FILENO, STDOUT_FILENO);
1500
 
        ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1501
 
        if(ret == -1){
1502
 
          perror_plus("setenv");
1503
 
          _exit(EX_OSERR);
1504
 
        }
1505
 
        ret = setenv("DEVICE", interface, 1);
1506
 
        if(ret == -1){
1507
 
          perror_plus("setenv");
1508
 
          _exit(EX_OSERR);
1509
 
        }
1510
 
        ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1511
 
        if(ret == -1){
1512
 
          perror_plus("setenv");
1513
 
          _exit(EX_OSERR);
1514
 
        }
1515
 
        ret = setenv("MODE", mode, 1);
1516
 
        if(ret == -1){
1517
 
          perror_plus("setenv");
1518
 
          _exit(EX_OSERR);
1519
 
        }
1520
 
        char *delaystring;
1521
 
        ret = asprintf(&delaystring, "%f", delay);
1522
 
        if(ret == -1){
1523
 
          perror_plus("asprintf");
1524
 
          _exit(EX_OSERR);
1525
 
        }
1526
 
        ret = setenv("DELAY", delaystring, 1);
1527
 
        if(ret == -1){
1528
 
          free(delaystring);
1529
 
          perror_plus("setenv");
1530
 
          _exit(EX_OSERR);
1531
 
        }
 
1622
        _exit(EX_OSERR);
 
1623
      }
 
1624
      ret = setenv("DELAY", delaystring, 1);
 
1625
      if(ret == -1){
1532
1626
        free(delaystring);
1533
 
        if(connect_to != NULL){
1534
 
          ret = setenv("CONNECT", connect_to, 1);
1535
 
          if(ret == -1){
1536
 
            perror_plus("setenv");
1537
 
            _exit(EX_OSERR);
1538
 
          }
1539
 
        }
1540
 
        if(execl(fullname, direntry->d_name, mode, NULL) == -1){
1541
 
          perror_plus("execl");
1542
 
          _exit(EXIT_FAILURE);
1543
 
        }
 
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;
1544
1673
      } else {
1545
 
        int status;
1546
 
        if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1547
 
          perror_plus("waitpid");
1548
 
          free(fullname);
1549
 
          continue;
1550
 
        }
1551
 
        if(WIFEXITED(status)){
1552
 
          if(WEXITSTATUS(status) != 0){
1553
 
            fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1554
 
                         " with status %d\n", direntry->d_name,
1555
 
                         WEXITSTATUS(status));
1556
 
            free(fullname);
1557
 
            continue;
1558
 
          }
1559
 
        } else if(WIFSIGNALED(status)){
1560
 
          fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1561
 
                       " signal %d\n", direntry->d_name,
1562
 
                       WTERMSIG(status));
1563
 
          free(fullname);
1564
 
          continue;
1565
 
        } else {
1566
 
          fprintf_plus(stderr, "Warning: network hook \"%s\""
1567
 
                       " crashed\n", direntry->d_name);
1568
 
          free(fullname);
1569
 
          continue;
1570
 
        }
1571
 
      }
1572
 
      free(fullname);
1573
 
      if(debug){
1574
 
        fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1575
 
                     direntry->d_name);
1576
 
      }
1577
 
    }
1578
 
    close(devnull);
1579
 
  }
1580
 
  return true;
 
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);
1581
1693
}
1582
1694
 
 
1695
__attribute__((nonnull, warn_unused_result))
1583
1696
error_t bring_up_interface(const char *const interface,
1584
1697
                           const float delay){
1585
 
  int sd = -1;
1586
1698
  error_t old_errno = errno;
1587
 
  error_t ret_errno = 0;
1588
 
  int ret, ret_setflags;
 
1699
  int ret;
1589
1700
  struct ifreq network;
1590
1701
  unsigned int if_index = if_nametoindex(interface);
1591
1702
  if(if_index == 0){
1600
1711
  }
1601
1712
  
1602
1713
  if(not interface_is_up(interface)){
1603
 
    if(not get_flags(interface, &network) and debug){
 
1714
    error_t ret_errno = 0, ioctl_errno = 0;
 
1715
    if(not get_flags(interface, &network)){
1604
1716
      ret_errno = errno;
1605
1717
      fprintf_plus(stderr, "Failed to get flags for interface "
1606
1718
                   "\"%s\"\n", interface);
 
1719
      errno = old_errno;
1607
1720
      return ret_errno;
1608
1721
    }
1609
 
    network.ifr_flags |= IFF_UP;
 
1722
    network.ifr_flags |= IFF_UP; /* set flag */
1610
1723
    
1611
 
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1612
 
    if(sd < 0){
 
1724
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1725
    if(sd == -1){
1613
1726
      ret_errno = errno;
1614
1727
      perror_plus("socket");
1615
1728
      errno = old_errno;
1616
1729
      return ret_errno;
1617
1730
    }
1618
 
  
 
1731
    
1619
1732
    if(quit_now){
1620
 
      close(sd);
 
1733
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1734
      if(ret == -1){
 
1735
        perror_plus("close");
 
1736
      }
1621
1737
      errno = old_errno;
1622
1738
      return EINTR;
1623
1739
    }
1627
1743
                   interface);
1628
1744
    }
1629
1745
    
1630
 
    /* Raise priviliges */
1631
 
    raise_privileges();
 
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
    }
1632
1752
    
1633
1753
#ifdef __linux__
1634
 
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1635
 
       messages about the network interface to mess up the prompt */
1636
 
    int ret_linux = klogctl(8, NULL, 5);
1637
 
    bool restore_loglevel = true;
1638
 
    if(ret_linux == -1){
1639
 
      restore_loglevel = false;
1640
 
      perror_plus("klogctl");
 
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
      }
1641
1765
    }
1642
1766
#endif  /* __linux__ */
1643
 
    ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1644
 
    ret_errno = errno;
 
1767
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1768
    ioctl_errno = errno;
1645
1769
#ifdef __linux__
1646
1770
    if(restore_loglevel){
1647
1771
      ret_linux = klogctl(7, NULL, 0);
1651
1775
    }
1652
1776
#endif  /* __linux__ */
1653
1777
    
1654
 
    /* Lower privileges */
1655
 
    lower_privileges();
 
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
    }
1656
1787
    
1657
1788
    /* Close the socket */
1658
1789
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
1661
1792
    }
1662
1793
    
1663
1794
    if(ret_setflags == -1){
1664
 
      errno = ret_errno;
 
1795
      errno = ioctl_errno;
1665
1796
      perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1666
1797
      errno = old_errno;
1667
 
      return ret_errno;
 
1798
      return ioctl_errno;
1668
1799
    }
1669
1800
  } else if(debug){
1670
1801
    fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
1688
1819
  return 0;
1689
1820
}
1690
1821
 
 
1822
__attribute__((nonnull, warn_unused_result))
1691
1823
error_t take_down_interface(const char *const interface){
1692
 
  int sd = -1;
1693
1824
  error_t old_errno = errno;
1694
 
  error_t ret_errno = 0;
1695
 
  int ret, ret_setflags;
1696
1825
  struct ifreq network;
1697
1826
  unsigned int if_index = if_nametoindex(interface);
1698
1827
  if(if_index == 0){
1701
1830
    return ENXIO;
1702
1831
  }
1703
1832
  if(interface_is_up(interface)){
 
1833
    error_t ret_errno = 0, ioctl_errno = 0;
1704
1834
    if(not get_flags(interface, &network) and debug){
1705
1835
      ret_errno = errno;
1706
1836
      fprintf_plus(stderr, "Failed to get flags for interface "
1707
1837
                   "\"%s\"\n", interface);
 
1838
      errno = old_errno;
1708
1839
      return ret_errno;
1709
1840
    }
1710
1841
    network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
1711
1842
    
1712
 
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1713
 
    if(sd < 0){
 
1843
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1844
    if(sd == -1){
1714
1845
      ret_errno = errno;
1715
1846
      perror_plus("socket");
1716
1847
      errno = old_errno;
1722
1853
                   interface);
1723
1854
    }
1724
1855
    
1725
 
    /* Raise priviliges */
1726
 
    raise_privileges();
1727
 
    
1728
 
    ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1729
 
    ret_errno = errno;
1730
 
    
1731
 
    /* Lower privileges */
1732
 
    lower_privileges();
 
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
    }
1733
1875
    
1734
1876
    /* Close the socket */
1735
 
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1877
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
1736
1878
    if(ret == -1){
1737
1879
      perror_plus("close");
1738
1880
    }
1739
1881
    
1740
1882
    if(ret_setflags == -1){
1741
 
      errno = ret_errno;
 
1883
      errno = ioctl_errno;
1742
1884
      perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
1743
1885
      errno = old_errno;
1744
 
      return ret_errno;
 
1886
      return ioctl_errno;
1745
1887
    }
1746
1888
  } else if(debug){
1747
1889
    fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
1755
1897
int main(int argc, char *argv[]){
1756
1898
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
1757
1899
                        .priority = "SECURE256:!CTYPE-X.509:"
1758
 
                        "+CTYPE-OPENPGP", .current_server = NULL };
 
1900
                        "+CTYPE-OPENPGP", .current_server = NULL,
 
1901
                        .interfaces = NULL, .interfaces_size = 0 };
1759
1902
  AvahiSServiceBrowser *sb = NULL;
1760
1903
  error_t ret_errno;
1761
1904
  int ret;
1762
1905
  intmax_t tmpmax;
1763
1906
  char *tmp;
1764
1907
  int exitcode = EXIT_SUCCESS;
1765
 
  char *interfaces = NULL;
1766
 
  size_t interfaces_size = 0;
1767
1908
  char *interfaces_to_take_down = NULL;
1768
1909
  size_t interfaces_to_take_down_size = 0;
1769
 
  char tempdir[] = "/tmp/mandosXXXXXX";
1770
 
  bool tempdir_created = false;
 
1910
  char run_tempdir[] = "/run/tmp/mandosXXXXXX";
 
1911
  char old_tempdir[] = "/tmp/mandosXXXXXX";
 
1912
  char *tempdir = NULL;
1771
1913
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1772
1914
  const char *seckey = PATHDIR "/" SECKEY;
1773
1915
  const char *pubkey = PATHDIR "/" PUBKEY;
1774
1916
  char *interfaces_hooks = NULL;
1775
 
  size_t interfaces_hooks_size = 0;
1776
1917
  
1777
1918
  bool gnutls_initialized = false;
1778
1919
  bool gpgme_initialized = false;
1869
2010
        connect_to = arg;
1870
2011
        break;
1871
2012
      case 'i':                 /* --interface */
1872
 
        ret_errno = argz_add_sep(&interfaces, &interfaces_size, arg,
1873
 
                                 (int)',');
 
2013
        ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
 
2014
                                 arg, (int)',');
1874
2015
        if(ret_errno != 0){
1875
2016
          argp_error(state, "%s", strerror(ret_errno));
1876
2017
        }
1956
2097
    /* Work around Debian bug #633582:
1957
2098
       <http://bugs.debian.org/633582> */
1958
2099
    
1959
 
    /* Re-raise priviliges */
1960
 
    if(raise_privileges() == 0){
 
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 {
1961
2106
      struct stat st;
1962
2107
      
1963
2108
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2003
2148
      }
2004
2149
    
2005
2150
      /* Lower privileges */
2006
 
      errno = 0;
2007
 
      ret = seteuid(uid);
2008
 
      if(ret == -1){
2009
 
        perror_plus("seteuid");
 
2151
      ret_errno = lower_privileges();
 
2152
      if(ret_errno != 0){
 
2153
        errno = ret_errno;
 
2154
        perror_plus("Failed to lower privileges");
2010
2155
      }
2011
2156
    }
2012
2157
  }
2013
2158
  
2014
 
  /* Remove empty interface names */
 
2159
  /* Remove invalid interface names (except "none") */
2015
2160
  {
2016
2161
    char *interface = NULL;
2017
 
    while((interface = argz_next(interfaces, interfaces_size,
 
2162
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2018
2163
                                 interface))){
2019
 
      if(if_nametoindex(interface) == 0){
2020
 
        if(interface[0] != '\0' and strcmp(interface, "none") != 0){
 
2164
      if(strcmp(interface, "none") != 0
 
2165
         and if_nametoindex(interface) == 0){
 
2166
        if(interface[0] != '\0'){
2021
2167
          fprintf_plus(stderr, "Not using nonexisting interface"
2022
2168
                       " \"%s\"\n", interface);
2023
2169
        }
2024
 
        argz_delete(&interfaces, &interfaces_size, interface);
 
2170
        argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2025
2171
        interface = NULL;
2026
2172
      }
2027
2173
    }
2029
2175
  
2030
2176
  /* Run network hooks */
2031
2177
  {
2032
 
    
2033
 
    if(interfaces != NULL){
2034
 
      interfaces_hooks = malloc(interfaces_size);
 
2178
    if(mc.interfaces != NULL){
 
2179
      interfaces_hooks = malloc(mc.interfaces_size);
2035
2180
      if(interfaces_hooks == NULL){
2036
2181
        perror_plus("malloc");
2037
2182
        goto end;
2038
2183
      }
2039
 
      memcpy(interfaces_hooks, interfaces, interfaces_size);
2040
 
      interfaces_hooks_size = interfaces_size;
2041
 
      argz_stringify(interfaces_hooks, interfaces_hooks_size,
2042
 
                     (int)',');
2043
 
    }
2044
 
    if(not run_network_hooks("start", interfaces_hooks != NULL ?
2045
 
                             interfaces_hooks : "", delay)){
2046
 
      goto end;
2047
 
    }
 
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);
2048
2189
  }
2049
2190
  
2050
2191
  if(not debug){
2127
2268
  }
2128
2269
  
2129
2270
  /* If no interfaces were specified, make a list */
2130
 
  if(interfaces == NULL){
2131
 
    struct dirent **direntries;
 
2271
  if(mc.interfaces == NULL){
 
2272
    struct dirent **direntries = NULL;
2132
2273
    /* Look for any good interfaces */
2133
2274
    ret = scandir(sys_class_net, &direntries, good_interface,
2134
2275
                  alphasort);
2135
2276
    if(ret >= 1){
2136
2277
      /* Add all found interfaces to interfaces list */
2137
2278
      for(int i = 0; i < ret; ++i){
2138
 
        ret_errno = argz_add(&interfaces, &interfaces_size,
 
2279
        ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2139
2280
                             direntries[i]->d_name);
2140
2281
        if(ret_errno != 0){
 
2282
          errno = ret_errno;
2141
2283
          perror_plus("argz_add");
 
2284
          free(direntries[i]);
2142
2285
          continue;
2143
2286
        }
2144
2287
        if(debug){
2145
2288
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
2146
2289
                       direntries[i]->d_name);
2147
2290
        }
 
2291
        free(direntries[i]);
2148
2292
      }
2149
2293
      free(direntries);
2150
2294
    } else {
2151
 
      free(direntries);
 
2295
      if(ret == 0){
 
2296
        free(direntries);
 
2297
      }
2152
2298
      fprintf_plus(stderr, "Could not find a network interface\n");
2153
2299
      exitcode = EXIT_FAILURE;
2154
2300
      goto end;
2155
2301
    }
2156
2302
  }
2157
2303
  
2158
 
  /* If we only got one interface, explicitly use only that one */
2159
 
  if(argz_count(interfaces, interfaces_size) == 1){
2160
 
    if(debug){
2161
 
      fprintf_plus(stderr, "Using only interface \"%s\"\n",
2162
 
                   interfaces);
2163
 
    }
2164
 
    if_index = (AvahiIfIndex)if_nametoindex(interfaces);
2165
 
  }
2166
 
  
2167
 
  /* Bring up interfaces which are down */
2168
 
  if(not (argz_count(interfaces, interfaces_size) == 1
2169
 
          and strcmp(interfaces, "none") == 0)){
 
2304
  /* Bring up interfaces which are down, and remove any "none"s */
 
2305
  {
2170
2306
    char *interface = NULL;
2171
 
    while((interface = argz_next(interfaces, interfaces_size,
 
2307
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2172
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;
 
2321
          }
 
2322
        }
 
2323
        break;
 
2324
      }
2173
2325
      bool interface_was_up = interface_is_up(interface);
2174
 
      ret = bring_up_interface(interface, delay);
 
2326
      errno = bring_up_interface(interface, delay);
2175
2327
      if(not interface_was_up){
2176
 
        if(ret != 0){
2177
 
          errno = ret;
 
2328
        if(errno != 0){
2178
2329
          perror_plus("Failed to bring up interface");
2179
2330
        } else {
2180
 
          ret_errno = argz_add(&interfaces_to_take_down,
2181
 
                               &interfaces_to_take_down_size,
2182
 
                               interface);
 
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
          }
2183
2337
        }
2184
2338
      }
2185
2339
    }
2186
 
    free(interfaces);
2187
 
    interfaces = NULL;
2188
 
    interfaces_size = 0;
2189
2340
    if(debug and (interfaces_to_take_down == NULL)){
2190
2341
      fprintf_plus(stderr, "No interfaces were brought up\n");
2191
2342
    }
2192
2343
  }
2193
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);
 
2352
  }
 
2353
  
2194
2354
  if(quit_now){
2195
2355
    goto end;
2196
2356
  }
2208
2368
    goto end;
2209
2369
  }
2210
2370
  
2211
 
  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){
2212
2381
    perror_plus("mkdtemp");
2213
2382
    goto end;
2214
2383
  }
2215
 
  tempdir_created = true;
2216
2384
  
2217
2385
  if(quit_now){
2218
2386
    goto end;
2254
2422
      exitcode = EX_USAGE;
2255
2423
      goto end;
2256
2424
    }
2257
 
  
 
2425
    
2258
2426
    if(quit_now){
2259
2427
      goto end;
2260
2428
    }
2290
2458
        fprintf_plus(stderr, "Retrying in %d seconds\n",
2291
2459
                     (int)retry_interval);
2292
2460
      }
2293
 
      sleep((int)retry_interval);
 
2461
      sleep((unsigned int)retry_interval);
2294
2462
    }
2295
2463
    
2296
 
    if (not quit_now){
 
2464
    if(not quit_now){
2297
2465
      exitcode = EXIT_SUCCESS;
2298
2466
    }
2299
2467
    
2354
2522
  if(debug){
2355
2523
    fprintf_plus(stderr, "Starting Avahi loop search\n");
2356
2524
  }
2357
 
 
 
2525
  
2358
2526
  ret = avahi_loop_with_timeout(simple_poll,
2359
2527
                                (int)(retry_interval * 1000), &mc);
2360
2528
  if(debug){
2369
2537
  }
2370
2538
  
2371
2539
  /* Cleanup things */
 
2540
  free(mc.interfaces);
 
2541
  
2372
2542
  if(sb != NULL)
2373
2543
    avahi_s_service_browser_free(sb);
2374
2544
  
2394
2564
    mc.current_server->prev->next = NULL;
2395
2565
    while(mc.current_server != NULL){
2396
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
2397
2575
      free(mc.current_server);
2398
2576
      mc.current_server = next;
2399
2577
    }
2400
2578
  }
2401
2579
  
2402
 
  /* Re-raise priviliges */
 
2580
  /* Re-raise privileges */
2403
2581
  {
2404
 
    raise_privileges();
2405
 
    
2406
 
    /* Run network hooks */
2407
 
    run_network_hooks("stop", interfaces_hooks != NULL ?
2408
 
                      interfaces_hooks : "", delay);
2409
 
    
2410
 
    /* Take down the network interfaces which were brought up */
2411
 
    {
2412
 
      char *interface = NULL;
2413
 
      while((interface=argz_next(interfaces_to_take_down,
2414
 
                                 interfaces_to_take_down_size,
2415
 
                                 interface))){
2416
 
        ret_errno = take_down_interface(interface);
2417
 
        if(ret_errno != 0){
2418
 
          errno = ret_errno;
2419
 
          perror_plus("Failed to take down interface");
2420
 
        }
2421
 
      }
2422
 
      if(debug and (interfaces_to_take_down == NULL)){
2423
 
        fprintf_plus(stderr, "No interfaces needed to be taken"
2424
 
                     " down\n");
2425
 
      }
2426
 
    }
2427
 
    
2428
 
    lower_privileges_permanently();
 
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
    }
2429
2616
  }
2430
2617
  
2431
2618
  free(interfaces_to_take_down);
2432
2619
  free(interfaces_hooks);
2433
2620
  
2434
2621
  /* Removes the GPGME temp directory and all files inside */
2435
 
  if(tempdir_created){
 
2622
  if(tempdir != NULL){
2436
2623
    struct dirent **direntries = NULL;
2437
 
    struct dirent *direntry = NULL;
2438
 
    int numentries = scandir(tempdir, &direntries, notdotentries,
2439
 
                             alphasort);
2440
 
    if (numentries > 0){
2441
 
      for(int i = 0; i < numentries; i++){
2442
 
        direntry = direntries[i];
2443
 
        char *fullname = NULL;
2444
 
        ret = asprintf(&fullname, "%s/%s", tempdir,
2445
 
                       direntry->d_name);
2446
 
        if(ret < 0){
2447
 
          perror_plus("asprintf");
2448
 
          continue;
2449
 
        }
2450
 
        ret = remove(fullname);
2451
 
        if(ret == -1){
2452
 
          fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2453
 
                       strerror(errno));
2454
 
        }
2455
 
        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
        }
2456
2661
      }
2457
 
    }
2458
 
 
2459
 
    /* need to clean even if 0 because man page doesn't specify */
2460
 
    free(direntries);
2461
 
    if (numentries == -1){
2462
 
      perror_plus("scandir");
2463
 
    }
2464
 
    ret = rmdir(tempdir);
2465
 
    if(ret == -1 and errno != ENOENT){
2466
 
      perror_plus("rmdir");
 
2662
      TEMP_FAILURE_RETRY(close(tempdir_fd));
2467
2663
    }
2468
2664
  }
2469
2665