/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 23:25:46 UTC
  • Revision ID: teddy@recompile.se-20120616232546-dcrkmnhd4yhq6mvd
* plugins.d/mandos-client (mandos_context): Moved to inside "main()".
                                            All users changed.
  (add_server): Take new "current_server" argument; all callers
                changed.
  (init_gpgme, pgp_packet_decrypt, init_gnutls_global,
  init_gnutls_session, start_mandos_communication,
  avahi_loop_with_timeout): Take new "mc" argument"; all callers
                            changed.
  (resolve_callback, browse_callback): Handle void pointer userdata as
                                       "mc", a pointer to
                                       mandos_context.  All callers
                                       changed.
  (main): New "mc" variable.

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