/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-16 22:38:54 UTC
  • Revision ID: teddy@recompile.se-20120616223854-mfxkg6fgqr56sma5
* plugins.d/mandos-client (mandos_context): Removed "simple_poll"
                                            member.
  (simple_poll): New global variable.  All users changed.

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