/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to plugins.d/mandos-client.c

  • Committer: Teddy Hogeborn
  • Date: 2012-06-14 21:42:44 UTC
  • mto: (301.1.1 release) (237.7.272 trunk)
  • mto: This revision was merged to the branch mainline in revision 302.
  • Revision ID: teddy@recompile.se-20120614214244-meh2hr7pg0p0fek2
* plugins.d/mandos-client.c: Only print message about nonexisting
                             network hook directory in debug mode.

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