/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: 2012-06-13 22:06:57 UTC
  • mto: This revision was merged to the branch mainline in revision 596.
  • Revision ID: teddy@recompile.se-20120613220657-qvq7c7nrndl3t413
* plugins.d/mandos-client.c (get_flags): Don't clobber errno.
  (up_interface): Removed; replaced with "interface_is_up".
  (interface_is_up, interface_is_running,
   lower_privileges_permanently, take_down_interface): New.
  (bring_up_interface): Return "error_t".  Use new functions
                        "interface_is_up", "get_flags", and
                        "interface_is_running".
  (main): Save all interfaces either autodetected or specified with
          --interface in argz vector "interfaces".  Save interfaces to
          take down on exit in argz vector "interfaces_to_take_down".
          Save interface names for DEVICE variable to network hooks as
          argz_vector "interfaces_hooks".  Bug fix: Be privileged
          while stopping network hooks.
* plugins.d/mandos-client.xml (SYNOPSIS): Changed --interface synopsis.
  (DESCRIPTION): Updated to document use of all interfaces.
  (OPTIONS): Updated description of "--interface".
* network-hooks.d/bridge: Parse comma-separated DEVICE environment
                          variable.
* network-hooks.d/openvpn: - '' -
* network-hooks.d/wireless: - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*  -*- coding: utf-8 -*- */
 
2
/*
 
3
 * Mandos-client - get and decrypt data from a Mandos server
 
4
 *
 
5
 * This program is partly derived from an example program for an Avahi
 
6
 * service browser, downloaded from
 
7
 * <http://avahi.org/browser/examples/core-browse-services.c>.  This
 
8
 * includes the following functions: "resolve_callback",
 
9
 * "browse_callback", and parts of "main".
 
10
 * 
 
11
 * Everything else is
 
12
 * Copyright © 2008-2012 Teddy Hogeborn
 
13
 * Copyright © 2008-2012 Björn Påhlsson
 
14
 * 
 
15
 * This program is free software: you can redistribute it and/or
 
16
 * modify it under the terms of the GNU General Public License as
 
17
 * published by the Free Software Foundation, either version 3 of the
 
18
 * License, or (at your option) any later version.
 
19
 * 
 
20
 * This program is distributed in the hope that it will be useful, but
 
21
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
23
 * General Public License for more details.
 
24
 * 
 
25
 * You should have received a copy of the GNU General Public License
 
26
 * along with this program.  If not, see
 
27
 * <http://www.gnu.org/licenses/>.
 
28
 * 
 
29
 * Contact the authors at <mandos@recompile.se>.
 
30
 */
 
31
 
 
32
/* Needed by GPGME, specifically gpgme_data_seek() */
 
33
#ifndef _LARGEFILE_SOURCE
 
34
#define _LARGEFILE_SOURCE
 
35
#endif
 
36
#ifndef _FILE_OFFSET_BITS
 
37
#define _FILE_OFFSET_BITS 64
 
38
#endif
 
39
 
 
40
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
 
41
 
 
42
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
 
43
                                   stdout, ferror(), remove() */
 
44
#include <stdint.h>             /* uint16_t, uint32_t, intptr_t */
 
45
#include <stddef.h>             /* NULL, size_t, ssize_t */
 
46
#include <stdlib.h>             /* free(), EXIT_SUCCESS, srand(),
 
47
                                   strtof(), abort() */
 
48
#include <stdbool.h>            /* bool, false, true */
 
49
#include <string.h>             /* memset(), strcmp(), strlen(),
 
50
                                   strerror(), asprintf(), strcpy() */
 
51
#include <sys/ioctl.h>          /* ioctl */
 
52
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
 
53
                                   sockaddr_in6, PF_INET6,
 
54
                                   SOCK_STREAM, uid_t, gid_t, open(),
 
55
                                   opendir(), DIR */
 
56
#include <sys/stat.h>           /* open(), S_ISREG */
 
57
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
 
58
                                   inet_pton(), connect() */
 
59
#include <fcntl.h>              /* open() */
 
60
#include <dirent.h>             /* opendir(), struct dirent, readdir()
 
61
                                 */
 
62
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
 
63
                                   strtoimax() */
 
64
#include <assert.h>             /* assert() */
 
65
#include <errno.h>              /* perror(), errno,
 
66
                                   program_invocation_short_name */
 
67
#include <time.h>               /* nanosleep(), time(), sleep() */
 
68
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
 
69
                                   SIOCSIFFLAGS, if_indextoname(),
 
70
                                   if_nametoindex(), IF_NAMESIZE */
 
71
#include <netinet/in.h>         /* IN6_IS_ADDR_LINKLOCAL,
 
72
                                   INET_ADDRSTRLEN, INET6_ADDRSTRLEN
 
73
                                */
 
74
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
 
75
                                   getuid(), getgid(), seteuid(),
 
76
                                   setgid(), pause(), _exit() */
 
77
#include <arpa/inet.h>          /* inet_pton(), htons, inet_ntop() */
 
78
#include <iso646.h>             /* not, or, and */
 
79
#include <argp.h>               /* struct argp_option, error_t, struct
 
80
                                   argp_state, struct argp,
 
81
                                   argp_parse(), ARGP_KEY_ARG,
 
82
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
 
83
#include <signal.h>             /* sigemptyset(), sigaddset(),
 
84
                                   sigaction(), SIGTERM, sig_atomic_t,
 
85
                                   raise() */
 
86
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
 
87
                                   EX_NOHOST, EX_IOERR, EX_PROTOCOL */
 
88
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
 
89
                                   WEXITSTATUS(), WTERMSIG() */
 
90
#include <grp.h>                /* setgroups() */
 
91
#include <argz.h>               /* argz_add_sep(), argz_next(),
 
92
                                   argz_delete(), argz_append(),
 
93
                                   argz_stringify(), argz_add(),
 
94
                                   argz_count() */
 
95
 
 
96
#ifdef __linux__
 
97
#include <sys/klog.h>           /* klogctl() */
 
98
#endif  /* __linux__ */
 
99
 
 
100
/* Avahi */
 
101
/* All Avahi types, constants and functions
 
102
 Avahi*, avahi_*,
 
103
 AVAHI_* */
 
104
#include <avahi-core/core.h>
 
105
#include <avahi-core/lookup.h>
 
106
#include <avahi-core/log.h>
 
107
#include <avahi-common/simple-watch.h>
 
108
#include <avahi-common/malloc.h>
 
109
#include <avahi-common/error.h>
 
110
 
 
111
/* GnuTLS */
 
112
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
 
113
                                   functions:
 
114
                                   gnutls_*
 
115
                                   init_gnutls_session(),
 
116
                                   GNUTLS_* */
 
117
#include <gnutls/openpgp.h>
 
118
                         /* gnutls_certificate_set_openpgp_key_file(),
 
119
                            GNUTLS_OPENPGP_FMT_BASE64 */
 
120
 
 
121
/* GPGME */
 
122
#include <gpgme.h>              /* All GPGME types, constants and
 
123
                                   functions:
 
124
                                   gpgme_*
 
125
                                   GPGME_PROTOCOL_OpenPGP,
 
126
                                   GPG_ERR_NO_* */
 
127
 
 
128
#define BUFFER_SIZE 256
 
129
 
 
130
#define PATHDIR "/conf/conf.d/mandos"
 
131
#define SECKEY "seckey.txt"
 
132
#define PUBKEY "pubkey.txt"
 
133
#define HOOKDIR "/lib/mandos/network-hooks.d"
 
134
 
 
135
bool debug = false;
 
136
static const char mandos_protocol_version[] = "1";
 
137
const char *argp_program_version = "mandos-client " VERSION;
 
138
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
139
static const char sys_class_net[] = "/sys/class/net";
 
140
char *connect_to = NULL;
 
141
const char *hookdir = HOOKDIR;
 
142
uid_t uid = 65534;
 
143
gid_t gid = 65534;
 
144
 
 
145
/* Doubly linked list that need to be circularly linked when used */
 
146
typedef struct server{
 
147
  const char *ip;
 
148
  uint16_t port;
 
149
  AvahiIfIndex if_index;
 
150
  int af;
 
151
  struct timespec last_seen;
 
152
  struct server *next;
 
153
  struct server *prev;
 
154
} server;
 
155
 
 
156
/* Used for passing in values through the Avahi callback functions */
 
157
typedef struct {
 
158
  AvahiSimplePoll *simple_poll;
 
159
  AvahiServer *server;
 
160
  gnutls_certificate_credentials_t cred;
 
161
  unsigned int dh_bits;
 
162
  gnutls_dh_params_t dh_params;
 
163
  const char *priority;
 
164
  gpgme_ctx_t ctx;
 
165
  server *current_server;
 
166
} mandos_context;
 
167
 
 
168
/* global context so signal handler can reach it*/
 
169
mandos_context mc = { .simple_poll = NULL, .server = NULL,
 
170
                      .dh_bits = 1024, .priority = "SECURE256"
 
171
                      ":!CTYPE-X.509:+CTYPE-OPENPGP",
 
172
                      .current_server = NULL };
 
173
 
 
174
sig_atomic_t quit_now = 0;
 
175
int signal_received = 0;
 
176
 
 
177
/* Function to use when printing errors */
 
178
void perror_plus(const char *print_text){
 
179
  int e = errno;
 
180
  fprintf(stderr, "Mandos plugin %s: ",
 
181
          program_invocation_short_name);
 
182
  errno = e;
 
183
  perror(print_text);
 
184
}
 
185
 
 
186
__attribute__((format (gnu_printf, 2, 3)))
 
187
int fprintf_plus(FILE *stream, const char *format, ...){
 
188
  va_list ap;
 
189
  va_start (ap, format);
 
190
  
 
191
  TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
 
192
                             program_invocation_short_name));
 
193
  return TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
 
194
}
 
195
 
 
196
/*
 
197
 * Make additional room in "buffer" for at least BUFFER_SIZE more
 
198
 * bytes. "buffer_capacity" is how much is currently allocated,
 
199
 * "buffer_length" is how much is already used.
 
200
 */
 
201
size_t incbuffer(char **buffer, size_t buffer_length,
 
202
                 size_t buffer_capacity){
 
203
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
 
204
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
205
    if(buffer == NULL){
 
206
      return 0;
 
207
    }
 
208
    buffer_capacity += BUFFER_SIZE;
 
209
  }
 
210
  return buffer_capacity;
 
211
}
 
212
 
 
213
/* Add server to set of servers to retry periodically */
 
214
bool add_server(const char *ip, uint16_t port, AvahiIfIndex if_index,
 
215
                int af){
 
216
  int ret;
 
217
  server *new_server = malloc(sizeof(server));
 
218
  if(new_server == NULL){
 
219
    perror_plus("malloc");
 
220
    return false;
 
221
  }
 
222
  *new_server = (server){ .ip = strdup(ip),
 
223
                          .port = port,
 
224
                          .if_index = if_index,
 
225
                          .af = af };
 
226
  if(new_server->ip == NULL){
 
227
    perror_plus("strdup");
 
228
    return false;
 
229
  }
 
230
  /* Special case of first server */
 
231
  if (mc.current_server == NULL){
 
232
    new_server->next = new_server;
 
233
    new_server->prev = new_server;
 
234
    mc.current_server = new_server;
 
235
  /* Place the new server last in the list */
 
236
  } else {
 
237
    new_server->next = mc.current_server;
 
238
    new_server->prev = mc.current_server->prev;
 
239
    new_server->prev->next = new_server;
 
240
    mc.current_server->prev = new_server;
 
241
  }
 
242
  ret = clock_gettime(CLOCK_MONOTONIC, &mc.current_server->last_seen);
 
243
  if(ret == -1){
 
244
    perror_plus("clock_gettime");
 
245
    return false;
 
246
  }
 
247
  return true;
 
248
}
 
249
 
 
250
/* 
 
251
 * Initialize GPGME.
 
252
 */
 
253
static bool init_gpgme(const char *seckey, const char *pubkey,
 
254
                       const char *tempdir){
 
255
  gpgme_error_t rc;
 
256
  gpgme_engine_info_t engine_info;
 
257
  
 
258
  
 
259
  /*
 
260
   * Helper function to insert pub and seckey to the engine keyring.
 
261
   */
 
262
  bool import_key(const char *filename){
 
263
    int ret;
 
264
    int fd;
 
265
    gpgme_data_t pgp_data;
 
266
    
 
267
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
 
268
    if(fd == -1){
 
269
      perror_plus("open");
 
270
      return false;
 
271
    }
 
272
    
 
273
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
 
274
    if(rc != GPG_ERR_NO_ERROR){
 
275
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
 
276
                   gpgme_strsource(rc), gpgme_strerror(rc));
 
277
      return false;
 
278
    }
 
279
    
 
280
    rc = gpgme_op_import(mc.ctx, pgp_data);
 
281
    if(rc != GPG_ERR_NO_ERROR){
 
282
      fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
 
283
                   gpgme_strsource(rc), gpgme_strerror(rc));
 
284
      return false;
 
285
    }
 
286
    
 
287
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
288
    if(ret == -1){
 
289
      perror_plus("close");
 
290
    }
 
291
    gpgme_data_release(pgp_data);
 
292
    return true;
 
293
  }
 
294
  
 
295
  if(debug){
 
296
    fprintf_plus(stderr, "Initializing GPGME\n");
 
297
  }
 
298
  
 
299
  /* Init GPGME */
 
300
  gpgme_check_version(NULL);
 
301
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
302
  if(rc != GPG_ERR_NO_ERROR){
 
303
    fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
 
304
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
305
    return false;
 
306
  }
 
307
  
 
308
  /* Set GPGME home directory for the OpenPGP engine only */
 
309
  rc = gpgme_get_engine_info(&engine_info);
 
310
  if(rc != GPG_ERR_NO_ERROR){
 
311
    fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
 
312
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
313
    return false;
 
314
  }
 
315
  while(engine_info != NULL){
 
316
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
 
317
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
 
318
                            engine_info->file_name, tempdir);
 
319
      break;
 
320
    }
 
321
    engine_info = engine_info->next;
 
322
  }
 
323
  if(engine_info == NULL){
 
324
    fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
 
325
                 tempdir);
 
326
    return false;
 
327
  }
 
328
  
 
329
  /* Create new GPGME "context" */
 
330
  rc = gpgme_new(&(mc.ctx));
 
331
  if(rc != GPG_ERR_NO_ERROR){
 
332
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
 
333
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
 
334
                 gpgme_strerror(rc));
 
335
    return false;
 
336
  }
 
337
  
 
338
  if(not import_key(pubkey) or not import_key(seckey)){
 
339
    return false;
 
340
  }
 
341
  
 
342
  return true;
 
343
}
 
344
 
 
345
/* 
 
346
 * Decrypt OpenPGP data.
 
347
 * Returns -1 on error
 
348
 */
 
349
static ssize_t pgp_packet_decrypt(const char *cryptotext,
 
350
                                  size_t crypto_size,
 
351
                                  char **plaintext){
 
352
  gpgme_data_t dh_crypto, dh_plain;
 
353
  gpgme_error_t rc;
 
354
  ssize_t ret;
 
355
  size_t plaintext_capacity = 0;
 
356
  ssize_t plaintext_length = 0;
 
357
  
 
358
  if(debug){
 
359
    fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
 
360
  }
 
361
  
 
362
  /* Create new GPGME data buffer from memory cryptotext */
 
363
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
 
364
                               0);
 
365
  if(rc != GPG_ERR_NO_ERROR){
 
366
    fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
 
367
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
368
    return -1;
 
369
  }
 
370
  
 
371
  /* Create new empty GPGME data buffer for the plaintext */
 
372
  rc = gpgme_data_new(&dh_plain);
 
373
  if(rc != GPG_ERR_NO_ERROR){
 
374
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
 
375
                 "bad gpgme_data_new: %s: %s\n",
 
376
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
377
    gpgme_data_release(dh_crypto);
 
378
    return -1;
 
379
  }
 
380
  
 
381
  /* Decrypt data from the cryptotext data buffer to the plaintext
 
382
     data buffer */
 
383
  rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
 
384
  if(rc != GPG_ERR_NO_ERROR){
 
385
    fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
 
386
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
387
    plaintext_length = -1;
 
388
    if(debug){
 
389
      gpgme_decrypt_result_t result;
 
390
      result = gpgme_op_decrypt_result(mc.ctx);
 
391
      if(result == NULL){
 
392
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
 
393
      } else {
 
394
        fprintf_plus(stderr, "Unsupported algorithm: %s\n",
 
395
                     result->unsupported_algorithm);
 
396
        fprintf_plus(stderr, "Wrong key usage: %u\n",
 
397
                     result->wrong_key_usage);
 
398
        if(result->file_name != NULL){
 
399
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
 
400
        }
 
401
        gpgme_recipient_t recipient;
 
402
        recipient = result->recipients;
 
403
        while(recipient != NULL){
 
404
          fprintf_plus(stderr, "Public key algorithm: %s\n",
 
405
                       gpgme_pubkey_algo_name
 
406
                       (recipient->pubkey_algo));
 
407
          fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
 
408
          fprintf_plus(stderr, "Secret key available: %s\n",
 
409
                       recipient->status == GPG_ERR_NO_SECKEY
 
410
                       ? "No" : "Yes");
 
411
          recipient = recipient->next;
 
412
        }
 
413
      }
 
414
    }
 
415
    goto decrypt_end;
 
416
  }
 
417
  
 
418
  if(debug){
 
419
    fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
 
420
  }
 
421
  
 
422
  /* Seek back to the beginning of the GPGME plaintext data buffer */
 
423
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
 
424
    perror_plus("gpgme_data_seek");
 
425
    plaintext_length = -1;
 
426
    goto decrypt_end;
 
427
  }
 
428
  
 
429
  *plaintext = NULL;
 
430
  while(true){
 
431
    plaintext_capacity = incbuffer(plaintext,
 
432
                                   (size_t)plaintext_length,
 
433
                                   plaintext_capacity);
 
434
    if(plaintext_capacity == 0){
 
435
      perror_plus("incbuffer");
 
436
      plaintext_length = -1;
 
437
      goto decrypt_end;
 
438
    }
 
439
    
 
440
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
 
441
                          BUFFER_SIZE);
 
442
    /* Print the data, if any */
 
443
    if(ret == 0){
 
444
      /* EOF */
 
445
      break;
 
446
    }
 
447
    if(ret < 0){
 
448
      perror_plus("gpgme_data_read");
 
449
      plaintext_length = -1;
 
450
      goto decrypt_end;
 
451
    }
 
452
    plaintext_length += ret;
 
453
  }
 
454
  
 
455
  if(debug){
 
456
    fprintf_plus(stderr, "Decrypted password is: ");
 
457
    for(ssize_t i = 0; i < plaintext_length; i++){
 
458
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
 
459
    }
 
460
    fprintf(stderr, "\n");
 
461
  }
 
462
  
 
463
 decrypt_end:
 
464
  
 
465
  /* Delete the GPGME cryptotext data buffer */
 
466
  gpgme_data_release(dh_crypto);
 
467
  
 
468
  /* Delete the GPGME plaintext data buffer */
 
469
  gpgme_data_release(dh_plain);
 
470
  return plaintext_length;
 
471
}
 
472
 
 
473
static const char * safer_gnutls_strerror(int value){
 
474
  const char *ret = gnutls_strerror(value); /* Spurious warning from
 
475
                                               -Wunreachable-code */
 
476
  if(ret == NULL)
 
477
    ret = "(unknown)";
 
478
  return ret;
 
479
}
 
480
 
 
481
/* GnuTLS log function callback */
 
482
static void debuggnutls(__attribute__((unused)) int level,
 
483
                        const char* string){
 
484
  fprintf_plus(stderr, "GnuTLS: %s", string);
 
485
}
 
486
 
 
487
static int init_gnutls_global(const char *pubkeyfilename,
 
488
                              const char *seckeyfilename){
 
489
  int ret;
 
490
  
 
491
  if(debug){
 
492
    fprintf_plus(stderr, "Initializing GnuTLS\n");
 
493
  }
 
494
  
 
495
  ret = gnutls_global_init();
 
496
  if(ret != GNUTLS_E_SUCCESS){
 
497
    fprintf_plus(stderr, "GnuTLS global_init: %s\n",
 
498
                 safer_gnutls_strerror(ret));
 
499
    return -1;
 
500
  }
 
501
  
 
502
  if(debug){
 
503
    /* "Use a log level over 10 to enable all debugging options."
 
504
     * - GnuTLS manual
 
505
     */
 
506
    gnutls_global_set_log_level(11);
 
507
    gnutls_global_set_log_function(debuggnutls);
 
508
  }
 
509
  
 
510
  /* OpenPGP credentials */
 
511
  ret = gnutls_certificate_allocate_credentials(&mc.cred);
 
512
  if(ret != GNUTLS_E_SUCCESS){
 
513
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
 
514
                 safer_gnutls_strerror(ret));
 
515
    gnutls_global_deinit();
 
516
    return -1;
 
517
  }
 
518
  
 
519
  if(debug){
 
520
    fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
 
521
                 " secret key %s as GnuTLS credentials\n",
 
522
                 pubkeyfilename,
 
523
                 seckeyfilename);
 
524
  }
 
525
  
 
526
  ret = gnutls_certificate_set_openpgp_key_file
 
527
    (mc.cred, pubkeyfilename, seckeyfilename,
 
528
     GNUTLS_OPENPGP_FMT_BASE64);
 
529
  if(ret != GNUTLS_E_SUCCESS){
 
530
    fprintf_plus(stderr,
 
531
                 "Error[%d] while reading the OpenPGP key pair ('%s',"
 
532
                 " '%s')\n", ret, pubkeyfilename, seckeyfilename);
 
533
    fprintf_plus(stderr, "The GnuTLS error is: %s\n",
 
534
                 safer_gnutls_strerror(ret));
 
535
    goto globalfail;
 
536
  }
 
537
  
 
538
  /* GnuTLS server initialization */
 
539
  ret = gnutls_dh_params_init(&mc.dh_params);
 
540
  if(ret != GNUTLS_E_SUCCESS){
 
541
    fprintf_plus(stderr, "Error in GnuTLS DH parameter"
 
542
                 " initialization: %s\n",
 
543
                 safer_gnutls_strerror(ret));
 
544
    goto globalfail;
 
545
  }
 
546
  ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
 
547
  if(ret != GNUTLS_E_SUCCESS){
 
548
    fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
 
549
                 safer_gnutls_strerror(ret));
 
550
    goto globalfail;
 
551
  }
 
552
  
 
553
  gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
 
554
  
 
555
  return 0;
 
556
  
 
557
 globalfail:
 
558
  
 
559
  gnutls_certificate_free_credentials(mc.cred);
 
560
  gnutls_global_deinit();
 
561
  gnutls_dh_params_deinit(mc.dh_params);
 
562
  return -1;
 
563
}
 
564
 
 
565
static int init_gnutls_session(gnutls_session_t *session){
 
566
  int ret;
 
567
  /* GnuTLS session creation */
 
568
  do {
 
569
    ret = gnutls_init(session, GNUTLS_SERVER);
 
570
    if(quit_now){
 
571
      return -1;
 
572
    }
 
573
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
574
  if(ret != GNUTLS_E_SUCCESS){
 
575
    fprintf_plus(stderr,
 
576
                 "Error in GnuTLS session initialization: %s\n",
 
577
                 safer_gnutls_strerror(ret));
 
578
  }
 
579
  
 
580
  {
 
581
    const char *err;
 
582
    do {
 
583
      ret = gnutls_priority_set_direct(*session, mc.priority, &err);
 
584
      if(quit_now){
 
585
        gnutls_deinit(*session);
 
586
        return -1;
 
587
      }
 
588
    } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
589
    if(ret != GNUTLS_E_SUCCESS){
 
590
      fprintf_plus(stderr, "Syntax error at: %s\n", err);
 
591
      fprintf_plus(stderr, "GnuTLS error: %s\n",
 
592
                   safer_gnutls_strerror(ret));
 
593
      gnutls_deinit(*session);
 
594
      return -1;
 
595
    }
 
596
  }
 
597
  
 
598
  do {
 
599
    ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
600
                                 mc.cred);
 
601
    if(quit_now){
 
602
      gnutls_deinit(*session);
 
603
      return -1;
 
604
    }
 
605
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
606
  if(ret != GNUTLS_E_SUCCESS){
 
607
    fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
 
608
                 safer_gnutls_strerror(ret));
 
609
    gnutls_deinit(*session);
 
610
    return -1;
 
611
  }
 
612
  
 
613
  /* ignore client certificate if any. */
 
614
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
 
615
  
 
616
  gnutls_dh_set_prime_bits(*session, mc.dh_bits);
 
617
  
 
618
  return 0;
 
619
}
 
620
 
 
621
/* Avahi log function callback */
 
622
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
623
                      __attribute__((unused)) const char *txt){}
 
624
 
 
625
/* Called when a Mandos server is found */
 
626
static int start_mandos_communication(const char *ip, uint16_t port,
 
627
                                      AvahiIfIndex if_index,
 
628
                                      int af){
 
629
  int ret, tcp_sd = -1;
 
630
  ssize_t sret;
 
631
  union {
 
632
    struct sockaddr_in in;
 
633
    struct sockaddr_in6 in6;
 
634
  } to;
 
635
  char *buffer = NULL;
 
636
  char *decrypted_buffer = NULL;
 
637
  size_t buffer_length = 0;
 
638
  size_t buffer_capacity = 0;
 
639
  size_t written;
 
640
  int retval = -1;
 
641
  gnutls_session_t session;
 
642
  int pf;                       /* Protocol family */
 
643
  
 
644
  errno = 0;
 
645
  
 
646
  if(quit_now){
 
647
    errno = EINTR;
 
648
    return -1;
 
649
  }
 
650
  
 
651
  switch(af){
 
652
  case AF_INET6:
 
653
    pf = PF_INET6;
 
654
    break;
 
655
  case AF_INET:
 
656
    pf = PF_INET;
 
657
    break;
 
658
  default:
 
659
    fprintf_plus(stderr, "Bad address family: %d\n", af);
 
660
    errno = EINVAL;
 
661
    return -1;
 
662
  }
 
663
  
 
664
  ret = init_gnutls_session(&session);
 
665
  if(ret != 0){
 
666
    return -1;
 
667
  }
 
668
  
 
669
  if(debug){
 
670
    fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
 
671
                 PRIu16 "\n", ip, port);
 
672
  }
 
673
  
 
674
  tcp_sd = socket(pf, SOCK_STREAM, 0);
 
675
  if(tcp_sd < 0){
 
676
    int e = errno;
 
677
    perror_plus("socket");
 
678
    errno = e;
 
679
    goto mandos_end;
 
680
  }
 
681
  
 
682
  if(quit_now){
 
683
    errno = EINTR;
 
684
    goto mandos_end;
 
685
  }
 
686
  
 
687
  memset(&to, 0, sizeof(to));
 
688
  if(af == AF_INET6){
 
689
    to.in6.sin6_family = (sa_family_t)af;
 
690
    ret = inet_pton(af, ip, &to.in6.sin6_addr);
 
691
  } else {                      /* IPv4 */
 
692
    to.in.sin_family = (sa_family_t)af;
 
693
    ret = inet_pton(af, ip, &to.in.sin_addr);
 
694
  }
 
695
  if(ret < 0 ){
 
696
    int e = errno;
 
697
    perror_plus("inet_pton");
 
698
    errno = e;
 
699
    goto mandos_end;
 
700
  }
 
701
  if(ret == 0){
 
702
    int e = errno;
 
703
    fprintf_plus(stderr, "Bad address: %s\n", ip);
 
704
    errno = e;
 
705
    goto mandos_end;
 
706
  }
 
707
  if(af == AF_INET6){
 
708
    to.in6.sin6_port = htons(port); /* Spurious warnings from
 
709
                                       -Wconversion and
 
710
                                       -Wunreachable-code */
 
711
    
 
712
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
 
713
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
 
714
                                -Wunreachable-code*/
 
715
      if(if_index == AVAHI_IF_UNSPEC){
 
716
        fprintf_plus(stderr, "An IPv6 link-local address is"
 
717
                     " incomplete without a network interface\n");
 
718
        errno = EINVAL;
 
719
        goto mandos_end;
 
720
      }
 
721
      /* Set the network interface number as scope */
 
722
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
723
    }
 
724
  } else {
 
725
    to.in.sin_port = htons(port); /* Spurious warnings from
 
726
                                     -Wconversion and
 
727
                                     -Wunreachable-code */
 
728
  }
 
729
  
 
730
  if(quit_now){
 
731
    errno = EINTR;
 
732
    goto mandos_end;
 
733
  }
 
734
  
 
735
  if(debug){
 
736
    if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
 
737
      char interface[IF_NAMESIZE];
 
738
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
739
        perror_plus("if_indextoname");
 
740
      } else {
 
741
        fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIu16
 
742
                     "\n", ip, interface, port);
 
743
      }
 
744
    } else {
 
745
      fprintf_plus(stderr, "Connection to: %s, port %" PRIu16 "\n",
 
746
                   ip, port);
 
747
    }
 
748
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
 
749
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
 
750
    const char *pcret;
 
751
    if(af == AF_INET6){
 
752
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
 
753
                        sizeof(addrstr));
 
754
    } else {
 
755
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
 
756
                        sizeof(addrstr));
 
757
    }
 
758
    if(pcret == NULL){
 
759
      perror_plus("inet_ntop");
 
760
    } else {
 
761
      if(strcmp(addrstr, ip) != 0){
 
762
        fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
 
763
      }
 
764
    }
 
765
  }
 
766
  
 
767
  if(quit_now){
 
768
    errno = EINTR;
 
769
    goto mandos_end;
 
770
  }
 
771
  
 
772
  if(af == AF_INET6){
 
773
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
774
  } else {
 
775
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
776
  }
 
777
  if(ret < 0){
 
778
    if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
 
779
      int e = errno;
 
780
      perror_plus("connect");
 
781
      errno = e;
 
782
    }
 
783
    goto mandos_end;
 
784
  }
 
785
  
 
786
  if(quit_now){
 
787
    errno = EINTR;
 
788
    goto mandos_end;
 
789
  }
 
790
  
 
791
  const char *out = mandos_protocol_version;
 
792
  written = 0;
 
793
  while(true){
 
794
    size_t out_size = strlen(out);
 
795
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
796
                                        out_size - written));
 
797
    if(ret == -1){
 
798
      int e = errno;
 
799
      perror_plus("write");
 
800
      errno = e;
 
801
      goto mandos_end;
 
802
    }
 
803
    written += (size_t)ret;
 
804
    if(written < out_size){
 
805
      continue;
 
806
    } else {
 
807
      if(out == mandos_protocol_version){
 
808
        written = 0;
 
809
        out = "\r\n";
 
810
      } else {
 
811
        break;
 
812
      }
 
813
    }
 
814
  
 
815
    if(quit_now){
 
816
      errno = EINTR;
 
817
      goto mandos_end;
 
818
    }
 
819
  }
 
820
  
 
821
  if(debug){
 
822
    fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
 
823
  }
 
824
  
 
825
  if(quit_now){
 
826
    errno = EINTR;
 
827
    goto mandos_end;
 
828
  }
 
829
  
 
830
  /* This casting via intptr_t is to eliminate warning about casting
 
831
     an int to a pointer type.  This is exactly how the GnuTLS Guile
 
832
     function "set-session-transport-fd!" does it. */
 
833
  gnutls_transport_set_ptr(session,
 
834
                           (gnutls_transport_ptr_t)(intptr_t)tcp_sd);
 
835
  
 
836
  if(quit_now){
 
837
    errno = EINTR;
 
838
    goto mandos_end;
 
839
  }
 
840
  
 
841
  do {
 
842
    ret = gnutls_handshake(session);
 
843
    if(quit_now){
 
844
      errno = EINTR;
 
845
      goto mandos_end;
 
846
    }
 
847
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
848
  
 
849
  if(ret != GNUTLS_E_SUCCESS){
 
850
    if(debug){
 
851
      fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
 
852
      gnutls_perror(ret);
 
853
    }
 
854
    errno = EPROTO;
 
855
    goto mandos_end;
 
856
  }
 
857
  
 
858
  /* Read OpenPGP packet that contains the wanted password */
 
859
  
 
860
  if(debug){
 
861
    fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
 
862
                 " %s\n", ip);
 
863
  }
 
864
  
 
865
  while(true){
 
866
    
 
867
    if(quit_now){
 
868
      errno = EINTR;
 
869
      goto mandos_end;
 
870
    }
 
871
    
 
872
    buffer_capacity = incbuffer(&buffer, buffer_length,
 
873
                                buffer_capacity);
 
874
    if(buffer_capacity == 0){
 
875
      int e = errno;
 
876
      perror_plus("incbuffer");
 
877
      errno = e;
 
878
      goto mandos_end;
 
879
    }
 
880
    
 
881
    if(quit_now){
 
882
      errno = EINTR;
 
883
      goto mandos_end;
 
884
    }
 
885
    
 
886
    sret = gnutls_record_recv(session, buffer+buffer_length,
 
887
                              BUFFER_SIZE);
 
888
    if(sret == 0){
 
889
      break;
 
890
    }
 
891
    if(sret < 0){
 
892
      switch(sret){
 
893
      case GNUTLS_E_INTERRUPTED:
 
894
      case GNUTLS_E_AGAIN:
 
895
        break;
 
896
      case GNUTLS_E_REHANDSHAKE:
 
897
        do {
 
898
          ret = gnutls_handshake(session);
 
899
          
 
900
          if(quit_now){
 
901
            errno = EINTR;
 
902
            goto mandos_end;
 
903
          }
 
904
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
905
        if(ret < 0){
 
906
          fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
 
907
                       "***\n");
 
908
          gnutls_perror(ret);
 
909
          errno = EPROTO;
 
910
          goto mandos_end;
 
911
        }
 
912
        break;
 
913
      default:
 
914
        fprintf_plus(stderr, "Unknown error while reading data from"
 
915
                     " encrypted session with Mandos server\n");
 
916
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
917
        errno = EIO;
 
918
        goto mandos_end;
 
919
      }
 
920
    } else {
 
921
      buffer_length += (size_t) sret;
 
922
    }
 
923
  }
 
924
  
 
925
  if(debug){
 
926
    fprintf_plus(stderr, "Closing TLS session\n");
 
927
  }
 
928
  
 
929
  if(quit_now){
 
930
    errno = EINTR;
 
931
    goto mandos_end;
 
932
  }
 
933
  
 
934
  do {
 
935
    ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
936
    if(quit_now){
 
937
      errno = EINTR;
 
938
      goto mandos_end;
 
939
    }
 
940
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
941
  
 
942
  if(buffer_length > 0){
 
943
    ssize_t decrypted_buffer_size;
 
944
    decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
 
945
                                               &decrypted_buffer);
 
946
    if(decrypted_buffer_size >= 0){
 
947
      
 
948
      written = 0;
 
949
      while(written < (size_t) decrypted_buffer_size){
 
950
        if(quit_now){
 
951
          errno = EINTR;
 
952
          goto mandos_end;
 
953
        }
 
954
        
 
955
        ret = (int)fwrite(decrypted_buffer + written, 1,
 
956
                          (size_t)decrypted_buffer_size - written,
 
957
                          stdout);
 
958
        if(ret == 0 and ferror(stdout)){
 
959
          int e = errno;
 
960
          if(debug){
 
961
            fprintf_plus(stderr, "Error writing encrypted data: %s\n",
 
962
                         strerror(errno));
 
963
          }
 
964
          errno = e;
 
965
          goto mandos_end;
 
966
        }
 
967
        written += (size_t)ret;
 
968
      }
 
969
      retval = 0;
 
970
    }
 
971
  }
 
972
  
 
973
  /* Shutdown procedure */
 
974
  
 
975
 mandos_end:
 
976
  {
 
977
    int e = errno;
 
978
    free(decrypted_buffer);
 
979
    free(buffer);
 
980
    if(tcp_sd >= 0){
 
981
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
982
    }
 
983
    if(ret == -1){
 
984
      if(e == 0){
 
985
        e = errno;
 
986
      }
 
987
      perror_plus("close");
 
988
    }
 
989
    gnutls_deinit(session);
 
990
    errno = e;
 
991
    if(quit_now){
 
992
      errno = EINTR;
 
993
      retval = -1;
 
994
    }
 
995
  }
 
996
  return retval;
 
997
}
 
998
 
 
999
static void resolve_callback(AvahiSServiceResolver *r,
 
1000
                             AvahiIfIndex interface,
 
1001
                             AvahiProtocol proto,
 
1002
                             AvahiResolverEvent event,
 
1003
                             const char *name,
 
1004
                             const char *type,
 
1005
                             const char *domain,
 
1006
                             const char *host_name,
 
1007
                             const AvahiAddress *address,
 
1008
                             uint16_t port,
 
1009
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
 
1010
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
1011
                             flags,
 
1012
                             AVAHI_GCC_UNUSED void* userdata){
 
1013
  assert(r);
 
1014
  
 
1015
  /* Called whenever a service has been resolved successfully or
 
1016
     timed out */
 
1017
  
 
1018
  if(quit_now){
 
1019
    return;
 
1020
  }
 
1021
  
 
1022
  switch(event){
 
1023
  default:
 
1024
  case AVAHI_RESOLVER_FAILURE:
 
1025
    fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
 
1026
                 "'%s' of type '%s' in domain '%s': %s\n", name, type,
 
1027
                 domain,
 
1028
                 avahi_strerror(avahi_server_errno(mc.server)));
 
1029
    break;
 
1030
    
 
1031
  case AVAHI_RESOLVER_FOUND:
 
1032
    {
 
1033
      char ip[AVAHI_ADDRESS_STR_MAX];
 
1034
      avahi_address_snprint(ip, sizeof(ip), address);
 
1035
      if(debug){
 
1036
        fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
1037
                     PRIdMAX ") on port %" PRIu16 "\n", name,
 
1038
                     host_name, ip, (intmax_t)interface, port);
 
1039
      }
 
1040
      int ret = start_mandos_communication(ip, port, interface,
 
1041
                                           avahi_proto_to_af(proto));
 
1042
      if(ret == 0){
 
1043
        avahi_simple_poll_quit(mc.simple_poll);
 
1044
      } else {
 
1045
        if(not add_server(ip, port, interface,
 
1046
                          avahi_proto_to_af(proto))){
 
1047
          fprintf_plus(stderr, "Failed to add server \"%s\" to server"
 
1048
                       " list\n", name);
 
1049
        }
 
1050
      }
 
1051
    }
 
1052
  }
 
1053
  avahi_s_service_resolver_free(r);
 
1054
}
 
1055
 
 
1056
static void browse_callback(AvahiSServiceBrowser *b,
 
1057
                            AvahiIfIndex interface,
 
1058
                            AvahiProtocol protocol,
 
1059
                            AvahiBrowserEvent event,
 
1060
                            const char *name,
 
1061
                            const char *type,
 
1062
                            const char *domain,
 
1063
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
1064
                            flags,
 
1065
                            AVAHI_GCC_UNUSED void* userdata){
 
1066
  assert(b);
 
1067
  
 
1068
  /* Called whenever a new services becomes available on the LAN or
 
1069
     is removed from the LAN */
 
1070
  
 
1071
  if(quit_now){
 
1072
    return;
 
1073
  }
 
1074
  
 
1075
  switch(event){
 
1076
  default:
 
1077
  case AVAHI_BROWSER_FAILURE:
 
1078
    
 
1079
    fprintf_plus(stderr, "(Avahi browser) %s\n",
 
1080
                 avahi_strerror(avahi_server_errno(mc.server)));
 
1081
    avahi_simple_poll_quit(mc.simple_poll);
 
1082
    return;
 
1083
    
 
1084
  case AVAHI_BROWSER_NEW:
 
1085
    /* We ignore the returned Avahi resolver object. In the callback
 
1086
       function we free it. If the Avahi server is terminated before
 
1087
       the callback function is called the Avahi server will free the
 
1088
       resolver for us. */
 
1089
    
 
1090
    if(avahi_s_service_resolver_new(mc.server, interface, protocol,
 
1091
                                    name, type, domain, protocol, 0,
 
1092
                                    resolve_callback, NULL) == NULL)
 
1093
      fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
 
1094
                   " %s\n", name,
 
1095
                   avahi_strerror(avahi_server_errno(mc.server)));
 
1096
    break;
 
1097
    
 
1098
  case AVAHI_BROWSER_REMOVE:
 
1099
    break;
 
1100
    
 
1101
  case AVAHI_BROWSER_ALL_FOR_NOW:
 
1102
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
1103
    if(debug){
 
1104
      fprintf_plus(stderr, "No Mandos server found, still"
 
1105
                   " searching...\n");
 
1106
    }
 
1107
    break;
 
1108
  }
 
1109
}
 
1110
 
 
1111
/* Signal handler that stops main loop after SIGTERM */
 
1112
static void handle_sigterm(int sig){
 
1113
  if(quit_now){
 
1114
    return;
 
1115
  }
 
1116
  quit_now = 1;
 
1117
  signal_received = sig;
 
1118
  int old_errno = errno;
 
1119
  /* set main loop to exit */
 
1120
  if(mc.simple_poll != NULL){
 
1121
    avahi_simple_poll_quit(mc.simple_poll);
 
1122
  }
 
1123
  errno = old_errno;
 
1124
}
 
1125
 
 
1126
bool get_flags(const char *ifname, struct ifreq *ifr){
 
1127
  int ret;
 
1128
  error_t ret_errno;
 
1129
  
 
1130
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1131
  if(s < 0){
 
1132
    ret_errno = errno;
 
1133
    perror_plus("socket");
 
1134
    errno = ret_errno;
 
1135
    return false;
 
1136
  }
 
1137
  strcpy(ifr->ifr_name, ifname);
 
1138
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
 
1139
  if(ret == -1){
 
1140
    if(debug){
 
1141
      ret_errno = errno;
 
1142
      perror_plus("ioctl SIOCGIFFLAGS");
 
1143
      errno = ret_errno;
 
1144
    }
 
1145
    return false;
 
1146
  }
 
1147
  return true;
 
1148
}
 
1149
 
 
1150
bool good_flags(const char *ifname, const struct ifreq *ifr){
 
1151
  
 
1152
  /* Reject the loopback device */
 
1153
  if(ifr->ifr_flags & IFF_LOOPBACK){
 
1154
    if(debug){
 
1155
      fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
 
1156
                   ifname);
 
1157
    }
 
1158
    return false;
 
1159
  }
 
1160
  /* Accept point-to-point devices only if connect_to is specified */
 
1161
  if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
 
1162
    if(debug){
 
1163
      fprintf_plus(stderr, "Accepting point-to-point interface"
 
1164
                   " \"%s\"\n", ifname);
 
1165
    }
 
1166
    return true;
 
1167
  }
 
1168
  /* Otherwise, reject non-broadcast-capable devices */
 
1169
  if(not (ifr->ifr_flags & IFF_BROADCAST)){
 
1170
    if(debug){
 
1171
      fprintf_plus(stderr, "Rejecting non-broadcast interface"
 
1172
                   " \"%s\"\n", ifname);
 
1173
    }
 
1174
    return false;
 
1175
  }
 
1176
  /* Reject non-ARP interfaces (including dummy interfaces) */
 
1177
  if(ifr->ifr_flags & IFF_NOARP){
 
1178
    if(debug){
 
1179
      fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
 
1180
                   ifname);
 
1181
    }
 
1182
    return false;
 
1183
  }
 
1184
  
 
1185
  /* Accept this device */
 
1186
  if(debug){
 
1187
    fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
 
1188
  }
 
1189
  return true;
 
1190
}
 
1191
 
 
1192
/* 
 
1193
 * This function determines if a directory entry in /sys/class/net
 
1194
 * corresponds to an acceptable network device.
 
1195
 * (This function is passed to scandir(3) as a filter function.)
 
1196
 */
 
1197
int good_interface(const struct dirent *if_entry){
 
1198
  if(if_entry->d_name[0] == '.'){
 
1199
    return 0;
 
1200
  }
 
1201
  
 
1202
  struct ifreq ifr;
 
1203
  if(not get_flags(if_entry->d_name, &ifr)){
 
1204
    if(debug){
 
1205
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1206
                   "\"%s\"\n", if_entry->d_name);
 
1207
    }
 
1208
    return 0;
 
1209
  }
 
1210
  
 
1211
  if(not good_flags(if_entry->d_name, &ifr)){
 
1212
    return 0;
 
1213
  }
 
1214
  return 1;
 
1215
}
 
1216
 
 
1217
/* 
 
1218
 * This function determines if a network interface is up.
 
1219
 */
 
1220
bool interface_is_up(const char *interface){
 
1221
  struct ifreq ifr;
 
1222
  if(not get_flags(interface, &ifr)){
 
1223
    if(debug){
 
1224
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1225
                   "\"%s\"\n", interface);
 
1226
    }
 
1227
    return false;
 
1228
  }
 
1229
  
 
1230
  return (bool)(ifr.ifr_flags & IFF_UP);
 
1231
}
 
1232
 
 
1233
/* 
 
1234
 * This function determines if a network interface is running
 
1235
 */
 
1236
bool interface_is_running(const char *interface){
 
1237
  struct ifreq ifr;
 
1238
  if(not get_flags(interface, &ifr)){
 
1239
    if(debug){
 
1240
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1241
                   "\"%s\"\n", interface);
 
1242
    }
 
1243
    return false;
 
1244
  }
 
1245
  
 
1246
  return (bool)(ifr.ifr_flags & IFF_RUNNING);
 
1247
}
 
1248
 
 
1249
int notdotentries(const struct dirent *direntry){
 
1250
  /* Skip "." and ".." */
 
1251
  if(direntry->d_name[0] == '.'
 
1252
     and (direntry->d_name[1] == '\0'
 
1253
          or (direntry->d_name[1] == '.'
 
1254
              and direntry->d_name[2] == '\0'))){
 
1255
    return 0;
 
1256
  }
 
1257
  return 1;
 
1258
}
 
1259
 
 
1260
/* Is this directory entry a runnable program? */
 
1261
int runnable_hook(const struct dirent *direntry){
 
1262
  int ret;
 
1263
  size_t sret;
 
1264
  struct stat st;
 
1265
  
 
1266
  if((direntry->d_name)[0] == '\0'){
 
1267
    /* Empty name? */
 
1268
    return 0;
 
1269
  }
 
1270
  
 
1271
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 
1272
                "abcdefghijklmnopqrstuvwxyz"
 
1273
                "0123456789"
 
1274
                "_-");
 
1275
  if((direntry->d_name)[sret] != '\0'){
 
1276
    /* Contains non-allowed characters */
 
1277
    if(debug){
 
1278
      fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
 
1279
                   direntry->d_name);
 
1280
    }
 
1281
    return 0;
 
1282
  }
 
1283
  
 
1284
  char *fullname = NULL;
 
1285
  ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
 
1286
  if(ret < 0){
 
1287
    perror_plus("asprintf");
 
1288
    return 0;
 
1289
  }
 
1290
  
 
1291
  ret = stat(fullname, &st);
 
1292
  if(ret == -1){
 
1293
    if(debug){
 
1294
      perror_plus("Could not stat hook");
 
1295
    }
 
1296
    return 0;
 
1297
  }
 
1298
  if(not (S_ISREG(st.st_mode))){
 
1299
    /* Not a regular file */
 
1300
    if(debug){
 
1301
      fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
 
1302
                   direntry->d_name);
 
1303
    }
 
1304
    return 0;
 
1305
  }
 
1306
  if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
 
1307
    /* Not executable */
 
1308
    if(debug){
 
1309
      fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
 
1310
                   direntry->d_name);
 
1311
    }
 
1312
    return 0;
 
1313
  }
 
1314
  if(debug){
 
1315
    fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
 
1316
                 direntry->d_name);
 
1317
  }
 
1318
  return 1;
 
1319
}
 
1320
 
 
1321
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval){
 
1322
  int ret;
 
1323
  struct timespec now;
 
1324
  struct timespec waited_time;
 
1325
  intmax_t block_time;
 
1326
  
 
1327
  while(true){
 
1328
    if(mc.current_server == NULL){
 
1329
      if (debug){
 
1330
        fprintf_plus(stderr, "Wait until first server is found."
 
1331
                     " No timeout!\n");
 
1332
      }
 
1333
      ret = avahi_simple_poll_iterate(s, -1);
 
1334
    } else {
 
1335
      if (debug){
 
1336
        fprintf_plus(stderr, "Check current_server if we should run"
 
1337
                     " it, or wait\n");
 
1338
      }
 
1339
      /* the current time */
 
1340
      ret = clock_gettime(CLOCK_MONOTONIC, &now);
 
1341
      if(ret == -1){
 
1342
        perror_plus("clock_gettime");
 
1343
        return -1;
 
1344
      }
 
1345
      /* Calculating in ms how long time between now and server
 
1346
         who we visted longest time ago. Now - last seen.  */
 
1347
      waited_time.tv_sec = (now.tv_sec
 
1348
                            - mc.current_server->last_seen.tv_sec);
 
1349
      waited_time.tv_nsec = (now.tv_nsec
 
1350
                             - mc.current_server->last_seen.tv_nsec);
 
1351
      /* total time is 10s/10,000ms.
 
1352
         Converting to s from ms by dividing by 1,000,
 
1353
         and ns to ms by dividing by 1,000,000. */
 
1354
      block_time = ((retry_interval
 
1355
                     - ((intmax_t)waited_time.tv_sec * 1000))
 
1356
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
 
1357
      
 
1358
      if (debug){
 
1359
        fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
 
1360
                     block_time);
 
1361
      }
 
1362
      
 
1363
      if(block_time <= 0){
 
1364
        ret = start_mandos_communication(mc.current_server->ip,
 
1365
                                         mc.current_server->port,
 
1366
                                         mc.current_server->if_index,
 
1367
                                         mc.current_server->af);
 
1368
        if(ret == 0){
 
1369
          avahi_simple_poll_quit(mc.simple_poll);
 
1370
          return 0;
 
1371
        }
 
1372
        ret = clock_gettime(CLOCK_MONOTONIC,
 
1373
                            &mc.current_server->last_seen);
 
1374
        if(ret == -1){
 
1375
          perror_plus("clock_gettime");
 
1376
          return -1;
 
1377
        }
 
1378
        mc.current_server = mc.current_server->next;
 
1379
        block_time = 0;         /* Call avahi to find new Mandos
 
1380
                                   servers, but don't block */
 
1381
      }
 
1382
      
 
1383
      ret = avahi_simple_poll_iterate(s, (int)block_time);
 
1384
    }
 
1385
    if(ret != 0){
 
1386
      if (ret > 0 or errno != EINTR){
 
1387
        return (ret != 1) ? ret : 0;
 
1388
      }
 
1389
    }
 
1390
  }
 
1391
}
 
1392
 
 
1393
/* Set effective uid to 0, return errno */
 
1394
error_t raise_privileges(void){
 
1395
  error_t old_errno = errno;
 
1396
  error_t ret_errno = 0;
 
1397
  if(seteuid(0) == -1){
 
1398
    ret_errno = errno;
 
1399
    perror_plus("seteuid");
 
1400
  }
 
1401
  errno = old_errno;
 
1402
  return ret_errno;
 
1403
}
 
1404
 
 
1405
/* Set effective and real user ID to 0.  Return errno. */
 
1406
error_t raise_privileges_permanently(void){
 
1407
  error_t old_errno = errno;
 
1408
  error_t ret_errno = raise_privileges();
 
1409
  if(ret_errno != 0){
 
1410
    errno = old_errno;
 
1411
    return ret_errno;
 
1412
  }
 
1413
  if(setuid(0) == -1){
 
1414
    ret_errno = errno;
 
1415
    perror_plus("seteuid");
 
1416
  }
 
1417
  errno = old_errno;
 
1418
  return ret_errno;
 
1419
}
 
1420
 
 
1421
/* Set effective user ID to unprivileged saved user ID */
 
1422
error_t lower_privileges(void){
 
1423
  error_t old_errno = errno;
 
1424
  error_t ret_errno = 0;
 
1425
  if(seteuid(uid) == -1){
 
1426
    ret_errno = errno;
 
1427
    perror_plus("seteuid");
 
1428
  }
 
1429
  errno = old_errno;
 
1430
  return ret_errno;
 
1431
}
 
1432
 
 
1433
/* Lower privileges permanently */
 
1434
error_t lower_privileges_permanently(void){
 
1435
  error_t old_errno = errno;
 
1436
  error_t ret_errno = 0;
 
1437
  if(setuid(uid) == -1){
 
1438
    ret_errno = errno;
 
1439
    perror_plus("setuid");
 
1440
  }
 
1441
  errno = old_errno;
 
1442
  return ret_errno;
 
1443
}
 
1444
 
 
1445
bool run_network_hooks(const char *mode, const char *interface,
 
1446
                       const float delay){
 
1447
  struct dirent **direntries;
 
1448
  struct dirent *direntry;
 
1449
  int ret;
 
1450
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1451
                         alphasort);
 
1452
  if(numhooks == -1){
 
1453
    perror_plus("scandir");
 
1454
  } else {
 
1455
    int devnull = open("/dev/null", O_RDONLY);
 
1456
    for(int i = 0; i < numhooks; i++){
 
1457
      direntry = direntries[i];
 
1458
      char *fullname = NULL;
 
1459
      ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
 
1460
      if(ret < 0){
 
1461
        perror_plus("asprintf");
 
1462
        continue;
 
1463
      }
 
1464
      if(debug){
 
1465
        fprintf_plus(stderr, "Running network hook \"%s\"\n",
 
1466
                     direntry->d_name);
 
1467
      }
 
1468
      pid_t hook_pid = fork();
 
1469
      if(hook_pid == 0){
 
1470
        /* Child */
 
1471
        /* Raise privileges */
 
1472
        raise_privileges_permanently();
 
1473
        /* Set group */
 
1474
        errno = 0;
 
1475
        ret = setgid(0);
 
1476
        if(ret == -1){
 
1477
          perror_plus("setgid");
 
1478
        }
 
1479
        /* Reset supplementary groups */
 
1480
        errno = 0;
 
1481
        ret = setgroups(0, NULL);
 
1482
        if(ret == -1){
 
1483
          perror_plus("setgroups");
 
1484
        }
 
1485
        dup2(devnull, STDIN_FILENO);
 
1486
        close(devnull);
 
1487
        dup2(STDERR_FILENO, STDOUT_FILENO);
 
1488
        ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
 
1489
        if(ret == -1){
 
1490
          perror_plus("setenv");
 
1491
          _exit(EX_OSERR);
 
1492
        }
 
1493
        ret = setenv("DEVICE", interface, 1);
 
1494
        if(ret == -1){
 
1495
          perror_plus("setenv");
 
1496
          _exit(EX_OSERR);
 
1497
        }
 
1498
        ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
 
1499
        if(ret == -1){
 
1500
          perror_plus("setenv");
 
1501
          _exit(EX_OSERR);
 
1502
        }
 
1503
        ret = setenv("MODE", mode, 1);
 
1504
        if(ret == -1){
 
1505
          perror_plus("setenv");
 
1506
          _exit(EX_OSERR);
 
1507
        }
 
1508
        char *delaystring;
 
1509
        ret = asprintf(&delaystring, "%f", delay);
 
1510
        if(ret == -1){
 
1511
          perror_plus("asprintf");
 
1512
          _exit(EX_OSERR);
 
1513
        }
 
1514
        ret = setenv("DELAY", delaystring, 1);
 
1515
        if(ret == -1){
 
1516
          free(delaystring);
 
1517
          perror_plus("setenv");
 
1518
          _exit(EX_OSERR);
 
1519
        }
 
1520
        free(delaystring);
 
1521
        if(connect_to != NULL){
 
1522
          ret = setenv("CONNECT", connect_to, 1);
 
1523
          if(ret == -1){
 
1524
            perror_plus("setenv");
 
1525
            _exit(EX_OSERR);
 
1526
          }
 
1527
        }
 
1528
        if(execl(fullname, direntry->d_name, mode, NULL) == -1){
 
1529
          perror_plus("execl");
 
1530
          _exit(EXIT_FAILURE);
 
1531
        }
 
1532
      } else {
 
1533
        int status;
 
1534
        if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
 
1535
          perror_plus("waitpid");
 
1536
          free(fullname);
 
1537
          continue;
 
1538
        }
 
1539
        if(WIFEXITED(status)){
 
1540
          if(WEXITSTATUS(status) != 0){
 
1541
            fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
 
1542
                         " with status %d\n", direntry->d_name,
 
1543
                         WEXITSTATUS(status));
 
1544
            free(fullname);
 
1545
            continue;
 
1546
          }
 
1547
        } else if(WIFSIGNALED(status)){
 
1548
          fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
 
1549
                       " signal %d\n", direntry->d_name,
 
1550
                       WTERMSIG(status));
 
1551
          free(fullname);
 
1552
          continue;
 
1553
        } else {
 
1554
          fprintf_plus(stderr, "Warning: network hook \"%s\""
 
1555
                       " crashed\n", direntry->d_name);
 
1556
          free(fullname);
 
1557
          continue;
 
1558
        }
 
1559
      }
 
1560
      free(fullname);
 
1561
      if(debug){
 
1562
        fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
 
1563
                     direntry->d_name);
 
1564
      }
 
1565
    }
 
1566
    close(devnull);
 
1567
  }
 
1568
  return true;
 
1569
}
 
1570
 
 
1571
error_t bring_up_interface(const char *const interface,
 
1572
                           const float delay){
 
1573
  int sd = -1;
 
1574
  error_t old_errno = errno;
 
1575
  error_t ret_errno = 0;
 
1576
  int ret, ret_setflags;
 
1577
  struct ifreq network;
 
1578
  unsigned int if_index = if_nametoindex(interface);
 
1579
  if(if_index == 0){
 
1580
    fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
 
1581
    errno = old_errno;
 
1582
    return ENXIO;
 
1583
  }
 
1584
  
 
1585
  if(quit_now){
 
1586
    errno = old_errno;
 
1587
    return EINTR;
 
1588
  }
 
1589
  
 
1590
  if(not interface_is_up(interface)){
 
1591
    if(not get_flags(interface, &network) and debug){
 
1592
      ret_errno = errno;
 
1593
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1594
                   "\"%s\"\n", interface);
 
1595
      return ret_errno;
 
1596
    }
 
1597
    network.ifr_flags |= IFF_UP;
 
1598
    
 
1599
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1600
    if(sd < 0){
 
1601
      ret_errno = errno;
 
1602
      perror_plus("socket");
 
1603
      errno = old_errno;
 
1604
      return ret_errno;
 
1605
    }
 
1606
  
 
1607
    if(quit_now){
 
1608
      close(sd);
 
1609
      errno = old_errno;
 
1610
      return EINTR;
 
1611
    }
 
1612
    
 
1613
    if(debug){
 
1614
      fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
 
1615
                   interface);
 
1616
    }
 
1617
    
 
1618
    /* Raise priviliges */
 
1619
    raise_privileges();
 
1620
    
 
1621
#ifdef __linux__
 
1622
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
1623
       messages about the network interface to mess up the prompt */
 
1624
    int ret_linux = klogctl(8, NULL, 5);
 
1625
    bool restore_loglevel = true;
 
1626
    if(ret_linux == -1){
 
1627
      restore_loglevel = false;
 
1628
      perror_plus("klogctl");
 
1629
    }
 
1630
#endif  /* __linux__ */
 
1631
    ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1632
    ret_errno = errno;
 
1633
#ifdef __linux__
 
1634
    if(restore_loglevel){
 
1635
      ret_linux = klogctl(7, NULL, 0);
 
1636
      if(ret_linux == -1){
 
1637
        perror_plus("klogctl");
 
1638
      }
 
1639
    }
 
1640
#endif  /* __linux__ */
 
1641
    
 
1642
    /* Lower privileges */
 
1643
    lower_privileges();
 
1644
    
 
1645
    /* Close the socket */
 
1646
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1647
    if(ret == -1){
 
1648
      perror_plus("close");
 
1649
    }
 
1650
    
 
1651
    if(ret_setflags == -1){
 
1652
      errno = ret_errno;
 
1653
      perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
 
1654
      errno = old_errno;
 
1655
      return ret_errno;
 
1656
    }
 
1657
  } else if(debug){
 
1658
    fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
 
1659
                 interface);
 
1660
  }
 
1661
  
 
1662
  /* Sleep checking until interface is running.
 
1663
     Check every 0.25s, up to total time of delay */
 
1664
  for(int i=0; i < delay * 4; i++){
 
1665
    if(interface_is_running(interface)){
 
1666
      break;
 
1667
    }
 
1668
    struct timespec sleeptime = { .tv_nsec = 250000000 };
 
1669
    ret = nanosleep(&sleeptime, NULL);
 
1670
    if(ret == -1 and errno != EINTR){
 
1671
      perror_plus("nanosleep");
 
1672
    }
 
1673
  }
 
1674
  
 
1675
  errno = old_errno;
 
1676
  return 0;
 
1677
}
 
1678
 
 
1679
error_t take_down_interface(const char *const interface){
 
1680
  int sd = -1;
 
1681
  error_t old_errno = errno;
 
1682
  error_t ret_errno = 0;
 
1683
  int ret, ret_setflags;
 
1684
  struct ifreq network;
 
1685
  unsigned int if_index = if_nametoindex(interface);
 
1686
  if(if_index == 0){
 
1687
    fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
 
1688
    errno = old_errno;
 
1689
    return ENXIO;
 
1690
  }
 
1691
  if(interface_is_up(interface)){
 
1692
    if(not get_flags(interface, &network) and debug){
 
1693
      ret_errno = errno;
 
1694
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1695
                   "\"%s\"\n", interface);
 
1696
      return ret_errno;
 
1697
    }
 
1698
    network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
 
1699
    
 
1700
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1701
    if(sd < 0){
 
1702
      ret_errno = errno;
 
1703
      perror_plus("socket");
 
1704
      errno = old_errno;
 
1705
      return ret_errno;
 
1706
    }
 
1707
    
 
1708
    if(debug){
 
1709
      fprintf_plus(stderr, "Taking down interface \"%s\"\n",
 
1710
                   interface);
 
1711
    }
 
1712
    
 
1713
    /* Raise priviliges */
 
1714
    raise_privileges();
 
1715
    
 
1716
    ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1717
    ret_errno = errno;
 
1718
    
 
1719
    /* Lower privileges */
 
1720
    lower_privileges();
 
1721
    
 
1722
    /* Close the socket */
 
1723
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1724
    if(ret == -1){
 
1725
      perror_plus("close");
 
1726
    }
 
1727
    
 
1728
    if(ret_setflags == -1){
 
1729
      errno = ret_errno;
 
1730
      perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
 
1731
      errno = old_errno;
 
1732
      return ret_errno;
 
1733
    }
 
1734
  } else if(debug){
 
1735
    fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
 
1736
                 interface);
 
1737
  }
 
1738
  
 
1739
  errno = old_errno;
 
1740
  return 0;
 
1741
}
 
1742
 
 
1743
int main(int argc, char *argv[]){
 
1744
  AvahiSServiceBrowser *sb = NULL;
 
1745
  error_t ret_errno;
 
1746
  int ret;
 
1747
  intmax_t tmpmax;
 
1748
  char *tmp;
 
1749
  int exitcode = EXIT_SUCCESS;
 
1750
  char *interfaces = NULL;
 
1751
  size_t interfaces_size = 0;
 
1752
  char *interfaces_to_take_down = NULL;
 
1753
  size_t interfaces_to_take_down_size = 0;
 
1754
  char tempdir[] = "/tmp/mandosXXXXXX";
 
1755
  bool tempdir_created = false;
 
1756
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
1757
  const char *seckey = PATHDIR "/" SECKEY;
 
1758
  const char *pubkey = PATHDIR "/" PUBKEY;
 
1759
  char *interfaces_hooks = NULL;
 
1760
  size_t interfaces_hooks_size = 0;
 
1761
  
 
1762
  bool gnutls_initialized = false;
 
1763
  bool gpgme_initialized = false;
 
1764
  float delay = 2.5f;
 
1765
  double retry_interval = 10; /* 10s between trying a server and
 
1766
                                 retrying the same server again */
 
1767
  
 
1768
  struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
 
1769
  struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
 
1770
  
 
1771
  uid = getuid();
 
1772
  gid = getgid();
 
1773
  
 
1774
  /* Lower any group privileges we might have, just to be safe */
 
1775
  errno = 0;
 
1776
  ret = setgid(gid);
 
1777
  if(ret == -1){
 
1778
    perror_plus("setgid");
 
1779
  }
 
1780
  
 
1781
  /* Lower user privileges (temporarily) */
 
1782
  errno = 0;
 
1783
  ret = seteuid(uid);
 
1784
  if(ret == -1){
 
1785
    perror_plus("seteuid");
 
1786
  }
 
1787
  
 
1788
  if(quit_now){
 
1789
    goto end;
 
1790
  }
 
1791
  
 
1792
  {
 
1793
    struct argp_option options[] = {
 
1794
      { .name = "debug", .key = 128,
 
1795
        .doc = "Debug mode", .group = 3 },
 
1796
      { .name = "connect", .key = 'c',
 
1797
        .arg = "ADDRESS:PORT",
 
1798
        .doc = "Connect directly to a specific Mandos server",
 
1799
        .group = 1 },
 
1800
      { .name = "interface", .key = 'i',
 
1801
        .arg = "NAME",
 
1802
        .doc = "Network interface that will be used to search for"
 
1803
        " Mandos servers",
 
1804
        .group = 1 },
 
1805
      { .name = "seckey", .key = 's',
 
1806
        .arg = "FILE",
 
1807
        .doc = "OpenPGP secret key file base name",
 
1808
        .group = 1 },
 
1809
      { .name = "pubkey", .key = 'p',
 
1810
        .arg = "FILE",
 
1811
        .doc = "OpenPGP public key file base name",
 
1812
        .group = 2 },
 
1813
      { .name = "dh-bits", .key = 129,
 
1814
        .arg = "BITS",
 
1815
        .doc = "Bit length of the prime number used in the"
 
1816
        " Diffie-Hellman key exchange",
 
1817
        .group = 2 },
 
1818
      { .name = "priority", .key = 130,
 
1819
        .arg = "STRING",
 
1820
        .doc = "GnuTLS priority string for the TLS handshake",
 
1821
        .group = 1 },
 
1822
      { .name = "delay", .key = 131,
 
1823
        .arg = "SECONDS",
 
1824
        .doc = "Maximum delay to wait for interface startup",
 
1825
        .group = 2 },
 
1826
      { .name = "retry", .key = 132,
 
1827
        .arg = "SECONDS",
 
1828
        .doc = "Retry interval used when denied by the Mandos server",
 
1829
        .group = 2 },
 
1830
      { .name = "network-hook-dir", .key = 133,
 
1831
        .arg = "DIR",
 
1832
        .doc = "Directory where network hooks are located",
 
1833
        .group = 2 },
 
1834
      /*
 
1835
       * These reproduce what we would get without ARGP_NO_HELP
 
1836
       */
 
1837
      { .name = "help", .key = '?',
 
1838
        .doc = "Give this help list", .group = -1 },
 
1839
      { .name = "usage", .key = -3,
 
1840
        .doc = "Give a short usage message", .group = -1 },
 
1841
      { .name = "version", .key = 'V',
 
1842
        .doc = "Print program version", .group = -1 },
 
1843
      { .name = NULL }
 
1844
    };
 
1845
    
 
1846
    error_t parse_opt(int key, char *arg,
 
1847
                      struct argp_state *state){
 
1848
      errno = 0;
 
1849
      switch(key){
 
1850
      case 128:                 /* --debug */
 
1851
        debug = true;
 
1852
        break;
 
1853
      case 'c':                 /* --connect */
 
1854
        connect_to = arg;
 
1855
        break;
 
1856
      case 'i':                 /* --interface */
 
1857
        ret_errno = argz_add_sep(&interfaces, &interfaces_size, arg,
 
1858
                                 (int)',');
 
1859
        if(ret_errno != 0){
 
1860
          argp_error(state, "%s", strerror(ret_errno));
 
1861
        }
 
1862
        break;
 
1863
      case 's':                 /* --seckey */
 
1864
        seckey = arg;
 
1865
        break;
 
1866
      case 'p':                 /* --pubkey */
 
1867
        pubkey = arg;
 
1868
        break;
 
1869
      case 129:                 /* --dh-bits */
 
1870
        errno = 0;
 
1871
        tmpmax = strtoimax(arg, &tmp, 10);
 
1872
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
1873
           or tmpmax != (typeof(mc.dh_bits))tmpmax){
 
1874
          argp_error(state, "Bad number of DH bits");
 
1875
        }
 
1876
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
 
1877
        break;
 
1878
      case 130:                 /* --priority */
 
1879
        mc.priority = arg;
 
1880
        break;
 
1881
      case 131:                 /* --delay */
 
1882
        errno = 0;
 
1883
        delay = strtof(arg, &tmp);
 
1884
        if(errno != 0 or tmp == arg or *tmp != '\0'){
 
1885
          argp_error(state, "Bad delay");
 
1886
        }
 
1887
      case 132:                 /* --retry */
 
1888
        errno = 0;
 
1889
        retry_interval = strtod(arg, &tmp);
 
1890
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
1891
           or (retry_interval * 1000) > INT_MAX
 
1892
           or retry_interval < 0){
 
1893
          argp_error(state, "Bad retry interval");
 
1894
        }
 
1895
        break;
 
1896
      case 133:                 /* --network-hook-dir */
 
1897
        hookdir = arg;
 
1898
        break;
 
1899
        /*
 
1900
         * These reproduce what we would get without ARGP_NO_HELP
 
1901
         */
 
1902
      case '?':                 /* --help */
 
1903
        argp_state_help(state, state->out_stream,
 
1904
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
 
1905
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
1906
      case -3:                  /* --usage */
 
1907
        argp_state_help(state, state->out_stream,
 
1908
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
1909
      case 'V':                 /* --version */
 
1910
        fprintf_plus(state->out_stream, "%s\n", argp_program_version);
 
1911
        exit(argp_err_exit_status);
 
1912
        break;
 
1913
      default:
 
1914
        return ARGP_ERR_UNKNOWN;
 
1915
      }
 
1916
      return errno;
 
1917
    }
 
1918
    
 
1919
    struct argp argp = { .options = options, .parser = parse_opt,
 
1920
                         .args_doc = "",
 
1921
                         .doc = "Mandos client -- Get and decrypt"
 
1922
                         " passwords from a Mandos server" };
 
1923
    ret = argp_parse(&argp, argc, argv,
 
1924
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
1925
    switch(ret){
 
1926
    case 0:
 
1927
      break;
 
1928
    case ENOMEM:
 
1929
    default:
 
1930
      errno = ret;
 
1931
      perror_plus("argp_parse");
 
1932
      exitcode = EX_OSERR;
 
1933
      goto end;
 
1934
    case EINVAL:
 
1935
      exitcode = EX_USAGE;
 
1936
      goto end;
 
1937
    }
 
1938
  }
 
1939
    
 
1940
  {
 
1941
    /* Work around Debian bug #633582:
 
1942
       <http://bugs.debian.org/633582> */
 
1943
    
 
1944
    /* Re-raise priviliges */
 
1945
    if(raise_privileges() == 0){
 
1946
      struct stat st;
 
1947
      
 
1948
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
 
1949
        int seckey_fd = open(seckey, O_RDONLY);
 
1950
        if(seckey_fd == -1){
 
1951
          perror_plus("open");
 
1952
        } else {
 
1953
          ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
 
1954
          if(ret == -1){
 
1955
            perror_plus("fstat");
 
1956
          } else {
 
1957
            if(S_ISREG(st.st_mode)
 
1958
               and st.st_uid == 0 and st.st_gid == 0){
 
1959
              ret = fchown(seckey_fd, uid, gid);
 
1960
              if(ret == -1){
 
1961
                perror_plus("fchown");
 
1962
              }
 
1963
            }
 
1964
          }
 
1965
          TEMP_FAILURE_RETRY(close(seckey_fd));
 
1966
        }
 
1967
      }
 
1968
    
 
1969
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
 
1970
        int pubkey_fd = open(pubkey, O_RDONLY);
 
1971
        if(pubkey_fd == -1){
 
1972
          perror_plus("open");
 
1973
        } else {
 
1974
          ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
 
1975
          if(ret == -1){
 
1976
            perror_plus("fstat");
 
1977
          } else {
 
1978
            if(S_ISREG(st.st_mode)
 
1979
               and st.st_uid == 0 and st.st_gid == 0){
 
1980
              ret = fchown(pubkey_fd, uid, gid);
 
1981
              if(ret == -1){
 
1982
                perror_plus("fchown");
 
1983
              }
 
1984
            }
 
1985
          }
 
1986
          TEMP_FAILURE_RETRY(close(pubkey_fd));
 
1987
        }
 
1988
      }
 
1989
    
 
1990
      /* Lower privileges */
 
1991
      errno = 0;
 
1992
      ret = seteuid(uid);
 
1993
      if(ret == -1){
 
1994
        perror_plus("seteuid");
 
1995
      }
 
1996
    }
 
1997
  }
 
1998
  
 
1999
  /* Remove empty interface names */
 
2000
  {
 
2001
    char *interface = NULL;
 
2002
    while((interface = argz_next(interfaces, interfaces_size,
 
2003
                                 interface))){
 
2004
      if(if_nametoindex(interface) == 0){
 
2005
        if(interface[0] != '\0' and strcmp(interface, "none") != 0){
 
2006
          fprintf_plus(stderr, "Not using nonexisting interface"
 
2007
                       " \"%s\"\n", interface);
 
2008
        }
 
2009
        argz_delete(&interfaces, &interfaces_size, interface);
 
2010
        interface = NULL;
 
2011
      }
 
2012
    }
 
2013
  }
 
2014
  
 
2015
  /* Run network hooks */
 
2016
  {
 
2017
    ret_errno = argz_append(&interfaces_hooks, &interfaces_hooks_size,
 
2018
                            interfaces, interfaces_size);
 
2019
    if(ret_errno != 0){
 
2020
      errno = ret_errno;
 
2021
      perror_plus("argz_append");
 
2022
      goto end;
 
2023
    }
 
2024
    argz_stringify(interfaces_hooks, interfaces_hooks_size, (int)',');
 
2025
    if(not run_network_hooks("start", interfaces_hooks, delay)){
 
2026
      goto end;
 
2027
    }
 
2028
  }
 
2029
  
 
2030
  if(not debug){
 
2031
    avahi_set_log_function(empty_log);
 
2032
  }
 
2033
  
 
2034
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
 
2035
     from the signal handler */
 
2036
  /* Initialize the pseudo-RNG for Avahi */
 
2037
  srand((unsigned int) time(NULL));
 
2038
  mc.simple_poll = avahi_simple_poll_new();
 
2039
  if(mc.simple_poll == NULL){
 
2040
    fprintf_plus(stderr,
 
2041
                 "Avahi: Failed to create simple poll object.\n");
 
2042
    exitcode = EX_UNAVAILABLE;
 
2043
    goto end;
 
2044
  }
 
2045
  
 
2046
  sigemptyset(&sigterm_action.sa_mask);
 
2047
  ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
 
2048
  if(ret == -1){
 
2049
    perror_plus("sigaddset");
 
2050
    exitcode = EX_OSERR;
 
2051
    goto end;
 
2052
  }
 
2053
  ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
 
2054
  if(ret == -1){
 
2055
    perror_plus("sigaddset");
 
2056
    exitcode = EX_OSERR;
 
2057
    goto end;
 
2058
  }
 
2059
  ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
 
2060
  if(ret == -1){
 
2061
    perror_plus("sigaddset");
 
2062
    exitcode = EX_OSERR;
 
2063
    goto end;
 
2064
  }
 
2065
  /* Need to check if the handler is SIG_IGN before handling:
 
2066
     | [[info:libc:Initial Signal Actions]] |
 
2067
     | [[info:libc:Basic Signal Handling]]  |
 
2068
  */
 
2069
  ret = sigaction(SIGINT, NULL, &old_sigterm_action);
 
2070
  if(ret == -1){
 
2071
    perror_plus("sigaction");
 
2072
    return EX_OSERR;
 
2073
  }
 
2074
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
2075
    ret = sigaction(SIGINT, &sigterm_action, NULL);
 
2076
    if(ret == -1){
 
2077
      perror_plus("sigaction");
 
2078
      exitcode = EX_OSERR;
 
2079
      goto end;
 
2080
    }
 
2081
  }
 
2082
  ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
 
2083
  if(ret == -1){
 
2084
    perror_plus("sigaction");
 
2085
    return EX_OSERR;
 
2086
  }
 
2087
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
2088
    ret = sigaction(SIGHUP, &sigterm_action, NULL);
 
2089
    if(ret == -1){
 
2090
      perror_plus("sigaction");
 
2091
      exitcode = EX_OSERR;
 
2092
      goto end;
 
2093
    }
 
2094
  }
 
2095
  ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
 
2096
  if(ret == -1){
 
2097
    perror_plus("sigaction");
 
2098
    return EX_OSERR;
 
2099
  }
 
2100
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
2101
    ret = sigaction(SIGTERM, &sigterm_action, NULL);
 
2102
    if(ret == -1){
 
2103
      perror_plus("sigaction");
 
2104
      exitcode = EX_OSERR;
 
2105
      goto end;
 
2106
    }
 
2107
  }
 
2108
  
 
2109
  /* If no interfaces were specified, make a list */
 
2110
  if(interfaces == NULL){
 
2111
    struct dirent **direntries;
 
2112
    /* Look for any good interfaces */
 
2113
    ret = scandir(sys_class_net, &direntries, good_interface,
 
2114
                  alphasort);
 
2115
    if(ret >= 1){
 
2116
      /* Add all found interfaces to interfaces list */
 
2117
      for(int i = 0; i < ret; ++i){
 
2118
        ret_errno = argz_add(&interfaces, &interfaces_size,
 
2119
                             direntries[i]->d_name);
 
2120
        if(ret_errno != 0){
 
2121
          perror_plus("argz_add");
 
2122
          continue;
 
2123
        }
 
2124
        if(debug){
 
2125
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
 
2126
                       direntries[i]->d_name);
 
2127
        }
 
2128
      }
 
2129
      free(direntries);
 
2130
    } else {
 
2131
      free(direntries);
 
2132
      fprintf_plus(stderr, "Could not find a network interface\n");
 
2133
      exitcode = EXIT_FAILURE;
 
2134
      goto end;
 
2135
    }
 
2136
  }
 
2137
  
 
2138
  /* If we only got one interface, explicitly use only that one */
 
2139
  if(argz_count(interfaces, interfaces_size) == 1){
 
2140
    if(debug){
 
2141
      fprintf_plus(stderr, "Using only interface \"%s\"\n",
 
2142
                   interfaces);
 
2143
    }
 
2144
    if_index = (AvahiIfIndex)if_nametoindex(interfaces);
 
2145
  }
 
2146
  
 
2147
  /* Bring up interfaces which are down */
 
2148
  if(not (argz_count(interfaces, interfaces_size) == 1
 
2149
          and strcmp(interfaces, "none") == 0)){
 
2150
    char *interface = NULL;
 
2151
    while((interface = argz_next(interfaces, interfaces_size,
 
2152
                                 interface))){
 
2153
      bool interface_was_up = interface_is_up(interface);
 
2154
      ret = bring_up_interface(interface, delay);
 
2155
      if(not interface_was_up){
 
2156
        if(ret != 0){
 
2157
          errno = ret;
 
2158
          perror_plus("Failed to bring up interface");
 
2159
        } else {
 
2160
          ret_errno = argz_add(&interfaces_to_take_down,
 
2161
                               &interfaces_to_take_down_size,
 
2162
                               interface);
 
2163
        }
 
2164
      }
 
2165
    }
 
2166
    free(interfaces);
 
2167
    interfaces = NULL;
 
2168
    interfaces_size = 0;
 
2169
    if(debug and (interfaces_to_take_down == NULL)){
 
2170
      fprintf_plus(stderr, "No interfaces were brought up\n");
 
2171
    }
 
2172
  }
 
2173
  
 
2174
  if(quit_now){
 
2175
    goto end;
 
2176
  }
 
2177
  
 
2178
  ret = init_gnutls_global(pubkey, seckey);
 
2179
  if(ret == -1){
 
2180
    fprintf_plus(stderr, "init_gnutls_global failed\n");
 
2181
    exitcode = EX_UNAVAILABLE;
 
2182
    goto end;
 
2183
  } else {
 
2184
    gnutls_initialized = true;
 
2185
  }
 
2186
  
 
2187
  if(quit_now){
 
2188
    goto end;
 
2189
  }
 
2190
  
 
2191
  if(mkdtemp(tempdir) == NULL){
 
2192
    perror_plus("mkdtemp");
 
2193
    goto end;
 
2194
  }
 
2195
  tempdir_created = true;
 
2196
  
 
2197
  if(quit_now){
 
2198
    goto end;
 
2199
  }
 
2200
  
 
2201
  if(not init_gpgme(pubkey, seckey, tempdir)){
 
2202
    fprintf_plus(stderr, "init_gpgme failed\n");
 
2203
    exitcode = EX_UNAVAILABLE;
 
2204
    goto end;
 
2205
  } else {
 
2206
    gpgme_initialized = true;
 
2207
  }
 
2208
  
 
2209
  if(quit_now){
 
2210
    goto end;
 
2211
  }
 
2212
  
 
2213
  if(connect_to != NULL){
 
2214
    /* Connect directly, do not use Zeroconf */
 
2215
    /* (Mainly meant for debugging) */
 
2216
    char *address = strrchr(connect_to, ':');
 
2217
    
 
2218
    if(address == NULL){
 
2219
      fprintf_plus(stderr, "No colon in address\n");
 
2220
      exitcode = EX_USAGE;
 
2221
      goto end;
 
2222
    }
 
2223
    
 
2224
    if(quit_now){
 
2225
      goto end;
 
2226
    }
 
2227
    
 
2228
    uint16_t port;
 
2229
    errno = 0;
 
2230
    tmpmax = strtoimax(address+1, &tmp, 10);
 
2231
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
 
2232
       or tmpmax != (uint16_t)tmpmax){
 
2233
      fprintf_plus(stderr, "Bad port number\n");
 
2234
      exitcode = EX_USAGE;
 
2235
      goto end;
 
2236
    }
 
2237
  
 
2238
    if(quit_now){
 
2239
      goto end;
 
2240
    }
 
2241
    
 
2242
    port = (uint16_t)tmpmax;
 
2243
    *address = '\0';
 
2244
    /* Colon in address indicates IPv6 */
 
2245
    int af;
 
2246
    if(strchr(connect_to, ':') != NULL){
 
2247
      af = AF_INET6;
 
2248
      /* Accept [] around IPv6 address - see RFC 5952 */
 
2249
      if(connect_to[0] == '[' and address[-1] == ']')
 
2250
        {
 
2251
          connect_to++;
 
2252
          address[-1] = '\0';
 
2253
        }
 
2254
    } else {
 
2255
      af = AF_INET;
 
2256
    }
 
2257
    address = connect_to;
 
2258
    
 
2259
    if(quit_now){
 
2260
      goto end;
 
2261
    }
 
2262
    
 
2263
    while(not quit_now){
 
2264
      ret = start_mandos_communication(address, port, if_index, af);
 
2265
      if(quit_now or ret == 0){
 
2266
        break;
 
2267
      }
 
2268
      if(debug){
 
2269
        fprintf_plus(stderr, "Retrying in %d seconds\n",
 
2270
                     (int)retry_interval);
 
2271
      }
 
2272
      sleep((int)retry_interval);
 
2273
    }
 
2274
    
 
2275
    if (not quit_now){
 
2276
      exitcode = EXIT_SUCCESS;
 
2277
    }
 
2278
    
 
2279
    goto end;
 
2280
  }
 
2281
  
 
2282
  if(quit_now){
 
2283
    goto end;
 
2284
  }
 
2285
  
 
2286
  {
 
2287
    AvahiServerConfig config;
 
2288
    /* Do not publish any local Zeroconf records */
 
2289
    avahi_server_config_init(&config);
 
2290
    config.publish_hinfo = 0;
 
2291
    config.publish_addresses = 0;
 
2292
    config.publish_workstation = 0;
 
2293
    config.publish_domain = 0;
 
2294
    
 
2295
    /* Allocate a new server */
 
2296
    mc.server = avahi_server_new(avahi_simple_poll_get
 
2297
                                 (mc.simple_poll), &config, NULL,
 
2298
                                 NULL, &ret_errno);
 
2299
    
 
2300
    /* Free the Avahi configuration data */
 
2301
    avahi_server_config_free(&config);
 
2302
  }
 
2303
  
 
2304
  /* Check if creating the Avahi server object succeeded */
 
2305
  if(mc.server == NULL){
 
2306
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
 
2307
                 avahi_strerror(ret_errno));
 
2308
    exitcode = EX_UNAVAILABLE;
 
2309
    goto end;
 
2310
  }
 
2311
  
 
2312
  if(quit_now){
 
2313
    goto end;
 
2314
  }
 
2315
  
 
2316
  /* Create the Avahi service browser */
 
2317
  sb = avahi_s_service_browser_new(mc.server, if_index,
 
2318
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
 
2319
                                   NULL, 0, browse_callback, NULL);
 
2320
  if(sb == NULL){
 
2321
    fprintf_plus(stderr, "Failed to create service browser: %s\n",
 
2322
                 avahi_strerror(avahi_server_errno(mc.server)));
 
2323
    exitcode = EX_UNAVAILABLE;
 
2324
    goto end;
 
2325
  }
 
2326
  
 
2327
  if(quit_now){
 
2328
    goto end;
 
2329
  }
 
2330
  
 
2331
  /* Run the main loop */
 
2332
  
 
2333
  if(debug){
 
2334
    fprintf_plus(stderr, "Starting Avahi loop search\n");
 
2335
  }
 
2336
 
 
2337
  ret = avahi_loop_with_timeout(mc.simple_poll,
 
2338
                                (int)(retry_interval * 1000));
 
2339
  if(debug){
 
2340
    fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
 
2341
                 (ret == 0) ? "successfully" : "with error");
 
2342
  }
 
2343
  
 
2344
 end:
 
2345
  
 
2346
  if(debug){
 
2347
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
2348
  }
 
2349
  
 
2350
  /* Cleanup things */
 
2351
  if(sb != NULL)
 
2352
    avahi_s_service_browser_free(sb);
 
2353
  
 
2354
  if(mc.server != NULL)
 
2355
    avahi_server_free(mc.server);
 
2356
  
 
2357
  if(mc.simple_poll != NULL)
 
2358
    avahi_simple_poll_free(mc.simple_poll);
 
2359
  
 
2360
  if(gnutls_initialized){
 
2361
    gnutls_certificate_free_credentials(mc.cred);
 
2362
    gnutls_global_deinit();
 
2363
    gnutls_dh_params_deinit(mc.dh_params);
 
2364
  }
 
2365
  
 
2366
  if(gpgme_initialized){
 
2367
    gpgme_release(mc.ctx);
 
2368
  }
 
2369
  
 
2370
  /* Cleans up the circular linked list of Mandos servers the client
 
2371
     has seen */
 
2372
  if(mc.current_server != NULL){
 
2373
    mc.current_server->prev->next = NULL;
 
2374
    while(mc.current_server != NULL){
 
2375
      server *next = mc.current_server->next;
 
2376
      free(mc.current_server);
 
2377
      mc.current_server = next;
 
2378
    }
 
2379
  }
 
2380
  
 
2381
  /* Re-raise priviliges */
 
2382
  {
 
2383
    raise_privileges();
 
2384
    
 
2385
    /* Run network hooks */
 
2386
    run_network_hooks("stop", interfaces_hooks, delay);
 
2387
    
 
2388
    /* Take down the network interfaces which were brought up */
 
2389
    {
 
2390
      char *interface = NULL;
 
2391
      while((interface=argz_next(interfaces_to_take_down,
 
2392
                                 interfaces_to_take_down_size,
 
2393
                                 interface))){
 
2394
        ret_errno = take_down_interface(interface);
 
2395
        if(ret_errno != 0){
 
2396
          errno = ret_errno;
 
2397
          perror_plus("Failed to take down interface");
 
2398
        }
 
2399
      }
 
2400
      if(debug and (interfaces_to_take_down == NULL)){
 
2401
        fprintf_plus(stderr, "No interfaces needed to be taken"
 
2402
                     " down\n");
 
2403
      }
 
2404
    }
 
2405
    
 
2406
    lower_privileges_permanently();
 
2407
  }
 
2408
  
 
2409
  free(interfaces_to_take_down);
 
2410
  free(interfaces_hooks);
 
2411
  
 
2412
  /* Removes the GPGME temp directory and all files inside */
 
2413
  if(tempdir_created){
 
2414
    struct dirent **direntries = NULL;
 
2415
    struct dirent *direntry = NULL;
 
2416
    int numentries = scandir(tempdir, &direntries, notdotentries,
 
2417
                             alphasort);
 
2418
    if (numentries > 0){
 
2419
      for(int i = 0; i < numentries; i++){
 
2420
        direntry = direntries[i];
 
2421
        char *fullname = NULL;
 
2422
        ret = asprintf(&fullname, "%s/%s", tempdir,
 
2423
                       direntry->d_name);
 
2424
        if(ret < 0){
 
2425
          perror_plus("asprintf");
 
2426
          continue;
 
2427
        }
 
2428
        ret = remove(fullname);
 
2429
        if(ret == -1){
 
2430
          fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
 
2431
                       strerror(errno));
 
2432
        }
 
2433
        free(fullname);
 
2434
      }
 
2435
    }
 
2436
 
 
2437
    /* need to clean even if 0 because man page doesn't specify */
 
2438
    free(direntries);
 
2439
    if (numentries == -1){
 
2440
      perror_plus("scandir");
 
2441
    }
 
2442
    ret = rmdir(tempdir);
 
2443
    if(ret == -1 and errno != ENOENT){
 
2444
      perror_plus("rmdir");
 
2445
    }
 
2446
  }
 
2447
  
 
2448
  if(quit_now){
 
2449
    sigemptyset(&old_sigterm_action.sa_mask);
 
2450
    old_sigterm_action.sa_handler = SIG_DFL;
 
2451
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
 
2452
                                            &old_sigterm_action,
 
2453
                                            NULL));
 
2454
    if(ret == -1){
 
2455
      perror_plus("sigaction");
 
2456
    }
 
2457
    do {
 
2458
      ret = raise(signal_received);
 
2459
    } while(ret != 0 and errno == EINTR);
 
2460
    if(ret != 0){
 
2461
      perror_plus("raise");
 
2462
      abort();
 
2463
    }
 
2464
    TEMP_FAILURE_RETRY(pause());
 
2465
  }
 
2466
  
 
2467
  return exitcode;
 
2468
}