/mandos/release

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2016-02-28 20:34:59 UTC
  • mto: (237.7.594 trunk)
  • mto: This revision was merged to the branch mainline in revision 333.
  • Revision ID: teddy@recompile.se-20160228203459-81vtqvr96yukyrok
Client: Remove calls to gnutls_global_init and gnutls_global_deinit

* plugins.d/mandos-client.c (init_gnutls_global, main): Don't call
  gnutls_global_init or gnutls_global_deinit anymore; these are
  unnecessary in GnuTLS 3.3.

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-2016 Teddy Hogeborn
 
13
 * Copyright © 2008-2016 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  /* not _LARGEFILE_SOURCE */
 
36
#ifndef _FILE_OFFSET_BITS
 
37
#define _FILE_OFFSET_BITS 64
 
38
#endif  /* not _FILE_OFFSET_BITS */
 
39
 
 
40
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
 
41
 
 
42
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
 
43
                                   stdout, ferror() */
 
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>             /* strcmp(), strlen(), strerror(),
 
50
                                   asprintf(), strncpy() */
 
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
                                   getnameinfo() */
 
60
#include <fcntl.h>              /* open(), unlinkat() */
 
61
#include <dirent.h>             /* opendir(), struct dirent, readdir()
 
62
                                 */
 
63
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
 
64
                                   strtoimax() */
 
65
#include <errno.h>              /* perror(), errno,
 
66
                                   program_invocation_short_name */
 
67
#include <time.h>               /* nanosleep(), time(), sleep() */
 
68
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
 
69
                                   SIOCSIFFLAGS, if_indextoname(),
 
70
                                   if_nametoindex(), IF_NAMESIZE */
 
71
#include <netinet/in.h>         /* IN6_IS_ADDR_LINKLOCAL,
 
72
                                   INET_ADDRSTRLEN, INET6_ADDRSTRLEN
 
73
                                */
 
74
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
 
75
                                   getuid(), getgid(), seteuid(),
 
76
                                   setgid(), pause(), _exit(),
 
77
                                   unlinkat() */
 
78
#include <arpa/inet.h>          /* inet_pton(), htons() */
 
79
#include <iso646.h>             /* not, or, and */
 
80
#include <argp.h>               /* struct argp_option, error_t, struct
 
81
                                   argp_state, struct argp,
 
82
                                   argp_parse(), ARGP_KEY_ARG,
 
83
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
 
84
#include <signal.h>             /* sigemptyset(), sigaddset(),
 
85
                                   sigaction(), SIGTERM, sig_atomic_t,
 
86
                                   raise() */
 
87
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
 
88
                                   EX_NOHOST, EX_IOERR, EX_PROTOCOL */
 
89
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
 
90
                                   WEXITSTATUS(), WTERMSIG() */
 
91
#include <grp.h>                /* setgroups() */
 
92
#include <argz.h>               /* argz_add_sep(), argz_next(),
 
93
                                   argz_delete(), argz_append(),
 
94
                                   argz_stringify(), argz_add(),
 
95
                                   argz_count() */
 
96
#include <netdb.h>              /* getnameinfo(), NI_NUMERICHOST,
 
97
                                   EAI_SYSTEM, gai_strerror() */
 
98
 
 
99
#ifdef __linux__
 
100
#include <sys/klog.h>           /* klogctl() */
 
101
#endif  /* __linux__ */
 
102
 
 
103
/* Avahi */
 
104
/* All Avahi types, constants and functions
 
105
 Avahi*, avahi_*,
 
106
 AVAHI_* */
 
107
#include <avahi-core/core.h>
 
108
#include <avahi-core/lookup.h>
 
109
#include <avahi-core/log.h>
 
110
#include <avahi-common/simple-watch.h>
 
111
#include <avahi-common/malloc.h>
 
112
#include <avahi-common/error.h>
 
113
 
 
114
/* GnuTLS */
 
115
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
 
116
                                   functions:
 
117
                                   gnutls_*
 
118
                                   init_gnutls_session(),
 
119
                                   GNUTLS_* */
 
120
#include <gnutls/openpgp.h>
 
121
                         /* gnutls_certificate_set_openpgp_key_file(),
 
122
                            GNUTLS_OPENPGP_FMT_BASE64 */
 
123
 
 
124
/* GPGME */
 
125
#include <gpgme.h>              /* All GPGME types, constants and
 
126
                                   functions:
 
127
                                   gpgme_*
 
128
                                   GPGME_PROTOCOL_OpenPGP,
 
129
                                   GPG_ERR_NO_* */
 
130
 
 
131
#define BUFFER_SIZE 256
 
132
 
 
133
#define PATHDIR "/conf/conf.d/mandos"
 
134
#define SECKEY "seckey.txt"
 
135
#define PUBKEY "pubkey.txt"
 
136
#define HOOKDIR "/lib/mandos/network-hooks.d"
 
137
 
 
138
bool debug = false;
 
139
static const char mandos_protocol_version[] = "1";
 
140
const char *argp_program_version = "mandos-client " VERSION;
 
141
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
142
static const char sys_class_net[] = "/sys/class/net";
 
143
char *connect_to = NULL;
 
144
const char *hookdir = HOOKDIR;
 
145
int hookdir_fd = -1;
 
146
uid_t uid = 65534;
 
147
gid_t gid = 65534;
 
148
 
 
149
/* Doubly linked list that need to be circularly linked when used */
 
150
typedef struct server{
 
151
  const char *ip;
 
152
  in_port_t port;
 
153
  AvahiIfIndex if_index;
 
154
  int af;
 
155
  struct timespec last_seen;
 
156
  struct server *next;
 
157
  struct server *prev;
 
158
} server;
 
159
 
 
160
/* Used for passing in values through the Avahi callback functions */
 
161
typedef struct {
 
162
  AvahiServer *server;
 
163
  gnutls_certificate_credentials_t cred;
 
164
  unsigned int dh_bits;
 
165
  gnutls_dh_params_t dh_params;
 
166
  const char *priority;
 
167
  gpgme_ctx_t ctx;
 
168
  server *current_server;
 
169
  char *interfaces;
 
170
  size_t interfaces_size;
 
171
} mandos_context;
 
172
 
 
173
/* global so signal handler can reach it*/
 
174
AvahiSimplePoll *simple_poll;
 
175
 
 
176
sig_atomic_t quit_now = 0;
 
177
int signal_received = 0;
 
178
 
 
179
/* Function to use when printing errors */
 
180
void perror_plus(const char *print_text){
 
181
  int e = errno;
 
182
  fprintf(stderr, "Mandos plugin %s: ",
 
183
          program_invocation_short_name);
 
184
  errno = e;
 
185
  perror(print_text);
 
186
}
 
187
 
 
188
__attribute__((format (gnu_printf, 2, 3), nonnull))
 
189
int fprintf_plus(FILE *stream, const char *format, ...){
 
190
  va_list ap;
 
191
  va_start (ap, format);
 
192
  
 
193
  TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
 
194
                             program_invocation_short_name));
 
195
  return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
 
196
}
 
197
 
 
198
/*
 
199
 * Make additional room in "buffer" for at least BUFFER_SIZE more
 
200
 * bytes. "buffer_capacity" is how much is currently allocated,
 
201
 * "buffer_length" is how much is already used.
 
202
 */
 
203
__attribute__((nonnull, warn_unused_result))
 
204
size_t incbuffer(char **buffer, size_t buffer_length,
 
205
                 size_t buffer_capacity){
 
206
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
 
207
    char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
208
    if(new_buf == NULL){
 
209
      int old_errno = errno;
 
210
      free(*buffer);
 
211
      errno = old_errno;
 
212
      *buffer = NULL;
 
213
      return 0;
 
214
    }
 
215
    *buffer = new_buf;
 
216
    buffer_capacity += BUFFER_SIZE;
 
217
  }
 
218
  return buffer_capacity;
 
219
}
 
220
 
 
221
/* Add server to set of servers to retry periodically */
 
222
__attribute__((nonnull, warn_unused_result))
 
223
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
 
224
                int af, server **current_server){
 
225
  int ret;
 
226
  server *new_server = malloc(sizeof(server));
 
227
  if(new_server == NULL){
 
228
    perror_plus("malloc");
 
229
    return false;
 
230
  }
 
231
  *new_server = (server){ .ip = strdup(ip),
 
232
                          .port = port,
 
233
                          .if_index = if_index,
 
234
                          .af = af };
 
235
  if(new_server->ip == NULL){
 
236
    perror_plus("strdup");
 
237
    free(new_server);
 
238
    return false;
 
239
  }
 
240
  ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
 
241
  if(ret == -1){
 
242
    perror_plus("clock_gettime");
 
243
#ifdef __GNUC__
 
244
#pragma GCC diagnostic push
 
245
#pragma GCC diagnostic ignored "-Wcast-qual"
 
246
#endif
 
247
    free((char *)(new_server->ip));
 
248
#ifdef __GNUC__
 
249
#pragma GCC diagnostic pop
 
250
#endif
 
251
    free(new_server);
 
252
    return false;
 
253
  }
 
254
  /* Special case of first server */
 
255
  if(*current_server == NULL){
 
256
    new_server->next = new_server;
 
257
    new_server->prev = new_server;
 
258
    *current_server = new_server;
 
259
  } else {
 
260
    /* Place the new server last in the list */
 
261
    new_server->next = *current_server;
 
262
    new_server->prev = (*current_server)->prev;
 
263
    new_server->prev->next = new_server;
 
264
    (*current_server)->prev = new_server;
 
265
  }
 
266
  return true;
 
267
}
 
268
 
 
269
/* 
 
270
 * Initialize GPGME.
 
271
 */
 
272
__attribute__((nonnull, warn_unused_result))
 
273
static bool init_gpgme(const char * const seckey,
 
274
                       const char * const pubkey,
 
275
                       const char * const tempdir,
 
276
                       mandos_context *mc){
 
277
  gpgme_error_t rc;
 
278
  gpgme_engine_info_t engine_info;
 
279
  
 
280
  /*
 
281
   * Helper function to insert pub and seckey to the engine keyring.
 
282
   */
 
283
  bool import_key(const char * const filename){
 
284
    int ret;
 
285
    int fd;
 
286
    gpgme_data_t pgp_data;
 
287
    
 
288
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
 
289
    if(fd == -1){
 
290
      perror_plus("open");
 
291
      return false;
 
292
    }
 
293
    
 
294
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
 
295
    if(rc != GPG_ERR_NO_ERROR){
 
296
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
 
297
                   gpgme_strsource(rc), gpgme_strerror(rc));
 
298
      return false;
 
299
    }
 
300
    
 
301
    rc = gpgme_op_import(mc->ctx, pgp_data);
 
302
    if(rc != GPG_ERR_NO_ERROR){
 
303
      fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
 
304
                   gpgme_strsource(rc), gpgme_strerror(rc));
 
305
      return false;
 
306
    }
 
307
    
 
308
    ret = close(fd);
 
309
    if(ret == -1){
 
310
      perror_plus("close");
 
311
    }
 
312
    gpgme_data_release(pgp_data);
 
313
    return true;
 
314
  }
 
315
  
 
316
  if(debug){
 
317
    fprintf_plus(stderr, "Initializing GPGME\n");
 
318
  }
 
319
  
 
320
  /* Init GPGME */
 
321
  gpgme_check_version(NULL);
 
322
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
323
  if(rc != GPG_ERR_NO_ERROR){
 
324
    fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
 
325
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
326
    return false;
 
327
  }
 
328
  
 
329
  /* Set GPGME home directory for the OpenPGP engine only */
 
330
  rc = gpgme_get_engine_info(&engine_info);
 
331
  if(rc != GPG_ERR_NO_ERROR){
 
332
    fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
 
333
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
334
    return false;
 
335
  }
 
336
  while(engine_info != NULL){
 
337
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
 
338
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
 
339
                            engine_info->file_name, tempdir);
 
340
      break;
 
341
    }
 
342
    engine_info = engine_info->next;
 
343
  }
 
344
  if(engine_info == NULL){
 
345
    fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
 
346
                 tempdir);
 
347
    return false;
 
348
  }
 
349
  
 
350
  /* Create new GPGME "context" */
 
351
  rc = gpgme_new(&(mc->ctx));
 
352
  if(rc != GPG_ERR_NO_ERROR){
 
353
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
 
354
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
 
355
                 gpgme_strerror(rc));
 
356
    return false;
 
357
  }
 
358
  
 
359
  if(not import_key(pubkey) or not import_key(seckey)){
 
360
    return false;
 
361
  }
 
362
  
 
363
  return true;
 
364
}
 
365
 
 
366
/* 
 
367
 * Decrypt OpenPGP data.
 
368
 * Returns -1 on error
 
369
 */
 
370
__attribute__((nonnull, warn_unused_result))
 
371
static ssize_t pgp_packet_decrypt(const char *cryptotext,
 
372
                                  size_t crypto_size,
 
373
                                  char **plaintext,
 
374
                                  mandos_context *mc){
 
375
  gpgme_data_t dh_crypto, dh_plain;
 
376
  gpgme_error_t rc;
 
377
  ssize_t ret;
 
378
  size_t plaintext_capacity = 0;
 
379
  ssize_t plaintext_length = 0;
 
380
  
 
381
  if(debug){
 
382
    fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
 
383
  }
 
384
  
 
385
  /* Create new GPGME data buffer from memory cryptotext */
 
386
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
 
387
                               0);
 
388
  if(rc != GPG_ERR_NO_ERROR){
 
389
    fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
 
390
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
391
    return -1;
 
392
  }
 
393
  
 
394
  /* Create new empty GPGME data buffer for the plaintext */
 
395
  rc = gpgme_data_new(&dh_plain);
 
396
  if(rc != GPG_ERR_NO_ERROR){
 
397
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
 
398
                 "bad gpgme_data_new: %s: %s\n",
 
399
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
400
    gpgme_data_release(dh_crypto);
 
401
    return -1;
 
402
  }
 
403
  
 
404
  /* Decrypt data from the cryptotext data buffer to the plaintext
 
405
     data buffer */
 
406
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
 
407
  if(rc != GPG_ERR_NO_ERROR){
 
408
    fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
 
409
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
410
    plaintext_length = -1;
 
411
    if(debug){
 
412
      gpgme_decrypt_result_t result;
 
413
      result = gpgme_op_decrypt_result(mc->ctx);
 
414
      if(result == NULL){
 
415
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
 
416
      } else {
 
417
        fprintf_plus(stderr, "Unsupported algorithm: %s\n",
 
418
                     result->unsupported_algorithm);
 
419
        fprintf_plus(stderr, "Wrong key usage: %u\n",
 
420
                     result->wrong_key_usage);
 
421
        if(result->file_name != NULL){
 
422
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
 
423
        }
 
424
        gpgme_recipient_t recipient;
 
425
        recipient = result->recipients;
 
426
        while(recipient != NULL){
 
427
          fprintf_plus(stderr, "Public key algorithm: %s\n",
 
428
                       gpgme_pubkey_algo_name
 
429
                       (recipient->pubkey_algo));
 
430
          fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
 
431
          fprintf_plus(stderr, "Secret key available: %s\n",
 
432
                       recipient->status == GPG_ERR_NO_SECKEY
 
433
                       ? "No" : "Yes");
 
434
          recipient = recipient->next;
 
435
        }
 
436
      }
 
437
    }
 
438
    goto decrypt_end;
 
439
  }
 
440
  
 
441
  if(debug){
 
442
    fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
 
443
  }
 
444
  
 
445
  /* Seek back to the beginning of the GPGME plaintext data buffer */
 
446
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
 
447
    perror_plus("gpgme_data_seek");
 
448
    plaintext_length = -1;
 
449
    goto decrypt_end;
 
450
  }
 
451
  
 
452
  *plaintext = NULL;
 
453
  while(true){
 
454
    plaintext_capacity = incbuffer(plaintext,
 
455
                                   (size_t)plaintext_length,
 
456
                                   plaintext_capacity);
 
457
    if(plaintext_capacity == 0){
 
458
      perror_plus("incbuffer");
 
459
      plaintext_length = -1;
 
460
      goto decrypt_end;
 
461
    }
 
462
    
 
463
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
 
464
                          BUFFER_SIZE);
 
465
    /* Print the data, if any */
 
466
    if(ret == 0){
 
467
      /* EOF */
 
468
      break;
 
469
    }
 
470
    if(ret < 0){
 
471
      perror_plus("gpgme_data_read");
 
472
      plaintext_length = -1;
 
473
      goto decrypt_end;
 
474
    }
 
475
    plaintext_length += ret;
 
476
  }
 
477
  
 
478
  if(debug){
 
479
    fprintf_plus(stderr, "Decrypted password is: ");
 
480
    for(ssize_t i = 0; i < plaintext_length; i++){
 
481
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
 
482
    }
 
483
    fprintf(stderr, "\n");
 
484
  }
 
485
  
 
486
 decrypt_end:
 
487
  
 
488
  /* Delete the GPGME cryptotext data buffer */
 
489
  gpgme_data_release(dh_crypto);
 
490
  
 
491
  /* Delete the GPGME plaintext data buffer */
 
492
  gpgme_data_release(dh_plain);
 
493
  return plaintext_length;
 
494
}
 
495
 
 
496
__attribute__((warn_unused_result, const))
 
497
static const char *safe_string(const char *str){
 
498
  if(str == NULL)
 
499
    return "(unknown)";
 
500
  return str;
 
501
}
 
502
 
 
503
__attribute__((warn_unused_result))
 
504
static const char *safer_gnutls_strerror(int value){
 
505
  const char *ret = gnutls_strerror(value);
 
506
  return safe_string(ret);
 
507
}
 
508
 
 
509
/* GnuTLS log function callback */
 
510
__attribute__((nonnull))
 
511
static void debuggnutls(__attribute__((unused)) int level,
 
512
                        const char* string){
 
513
  fprintf_plus(stderr, "GnuTLS: %s", string);
 
514
}
 
515
 
 
516
__attribute__((nonnull(1, 2, 4), warn_unused_result))
 
517
static int init_gnutls_global(const char *pubkeyfilename,
 
518
                              const char *seckeyfilename,
 
519
                              const char *dhparamsfilename,
 
520
                              mandos_context *mc){
 
521
  int ret;
 
522
  unsigned int uret;
 
523
  
 
524
  if(debug){
 
525
    fprintf_plus(stderr, "Initializing GnuTLS\n");
 
526
  }
 
527
  
 
528
  if(debug){
 
529
    /* "Use a log level over 10 to enable all debugging options."
 
530
     * - GnuTLS manual
 
531
     */
 
532
    gnutls_global_set_log_level(11);
 
533
    gnutls_global_set_log_function(debuggnutls);
 
534
  }
 
535
  
 
536
  /* OpenPGP credentials */
 
537
  ret = gnutls_certificate_allocate_credentials(&mc->cred);
 
538
  if(ret != GNUTLS_E_SUCCESS){
 
539
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
 
540
                 safer_gnutls_strerror(ret));
 
541
    return -1;
 
542
  }
 
543
  
 
544
  if(debug){
 
545
    fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
 
546
                 " secret key %s as GnuTLS credentials\n",
 
547
                 pubkeyfilename,
 
548
                 seckeyfilename);
 
549
  }
 
550
  
 
551
  ret = gnutls_certificate_set_openpgp_key_file
 
552
    (mc->cred, pubkeyfilename, seckeyfilename,
 
553
     GNUTLS_OPENPGP_FMT_BASE64);
 
554
  if(ret != GNUTLS_E_SUCCESS){
 
555
    fprintf_plus(stderr,
 
556
                 "Error[%d] while reading the OpenPGP key pair ('%s',"
 
557
                 " '%s')\n", ret, pubkeyfilename, seckeyfilename);
 
558
    fprintf_plus(stderr, "The GnuTLS error is: %s\n",
 
559
                 safer_gnutls_strerror(ret));
 
560
    goto globalfail;
 
561
  }
 
562
  
 
563
  /* GnuTLS server initialization */
 
564
  ret = gnutls_dh_params_init(&mc->dh_params);
 
565
  if(ret != GNUTLS_E_SUCCESS){
 
566
    fprintf_plus(stderr, "Error in GnuTLS DH parameter"
 
567
                 " initialization: %s\n",
 
568
                 safer_gnutls_strerror(ret));
 
569
    goto globalfail;
 
570
  }
 
571
  /* If a Diffie-Hellman parameters file was given, try to use it */
 
572
  if(dhparamsfilename != NULL){
 
573
    gnutls_datum_t params = { .data = NULL, .size = 0 };
 
574
    do {
 
575
      int dhpfile = open(dhparamsfilename, O_RDONLY);
 
576
      if(dhpfile == -1){
 
577
        perror_plus("open");
 
578
        dhparamsfilename = NULL;
 
579
        break;
 
580
      }
 
581
      size_t params_capacity = 0;
 
582
      while(true){
 
583
        params_capacity = incbuffer((char **)&params.data,
 
584
                                    (size_t)params.size,
 
585
                                    (size_t)params_capacity);
 
586
        if(params_capacity == 0){
 
587
          perror_plus("incbuffer");
 
588
          free(params.data);
 
589
          params.data = NULL;
 
590
          dhparamsfilename = NULL;
 
591
          break;
 
592
        }
 
593
        ssize_t bytes_read = read(dhpfile,
 
594
                                  params.data + params.size,
 
595
                                  BUFFER_SIZE);
 
596
        /* EOF */
 
597
        if(bytes_read == 0){
 
598
          break;
 
599
        }
 
600
        /* check bytes_read for failure */
 
601
        if(bytes_read < 0){
 
602
          perror_plus("read");
 
603
          free(params.data);
 
604
          params.data = NULL;
 
605
          dhparamsfilename = NULL;
 
606
          break;
 
607
        }
 
608
        params.size += (unsigned int)bytes_read;
 
609
      }
 
610
      if(params.data == NULL){
 
611
        dhparamsfilename = NULL;
 
612
      }
 
613
      if(dhparamsfilename == NULL){
 
614
        break;
 
615
      }
 
616
      ret = gnutls_dh_params_import_pkcs3(mc->dh_params, &params,
 
617
                                          GNUTLS_X509_FMT_PEM);
 
618
      if(ret != GNUTLS_E_SUCCESS){
 
619
        fprintf_plus(stderr, "Failed to parse DH parameters in file"
 
620
                     " \"%s\": %s\n", dhparamsfilename,
 
621
                     safer_gnutls_strerror(ret));
 
622
        dhparamsfilename = NULL;
 
623
      }
 
624
    } while(false);
 
625
  }
 
626
  if(dhparamsfilename == NULL){
 
627
    if(mc->dh_bits == 0){
 
628
      /* Find out the optimal number of DH bits */
 
629
      /* Try to read the private key file */
 
630
      gnutls_datum_t buffer = { .data = NULL, .size = 0 };
 
631
      do {
 
632
        int secfile = open(seckeyfilename, O_RDONLY);
 
633
        if(secfile == -1){
 
634
          perror_plus("open");
 
635
          break;
 
636
        }
 
637
        size_t buffer_capacity = 0;
 
638
        while(true){
 
639
          buffer_capacity = incbuffer((char **)&buffer.data,
 
640
                                      (size_t)buffer.size,
 
641
                                      (size_t)buffer_capacity);
 
642
          if(buffer_capacity == 0){
 
643
            perror_plus("incbuffer");
 
644
            free(buffer.data);
 
645
            buffer.data = NULL;
 
646
            break;
 
647
          }
 
648
          ssize_t bytes_read = read(secfile,
 
649
                                    buffer.data + buffer.size,
 
650
                                    BUFFER_SIZE);
 
651
          /* EOF */
 
652
          if(bytes_read == 0){
 
653
            break;
 
654
          }
 
655
          /* check bytes_read for failure */
 
656
          if(bytes_read < 0){
 
657
            perror_plus("read");
 
658
            free(buffer.data);
 
659
            buffer.data = NULL;
 
660
            break;
 
661
          }
 
662
          buffer.size += (unsigned int)bytes_read;
 
663
        }
 
664
        close(secfile);
 
665
      } while(false);
 
666
      /* If successful, use buffer to parse private key */
 
667
      gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
668
      if(buffer.data != NULL){
 
669
        {
 
670
          gnutls_openpgp_privkey_t privkey = NULL;
 
671
          ret = gnutls_openpgp_privkey_init(&privkey);
 
672
          if(ret != GNUTLS_E_SUCCESS){
 
673
            fprintf_plus(stderr, "Error initializing OpenPGP key"
 
674
                         " structure: %s",
 
675
                         safer_gnutls_strerror(ret));
 
676
            free(buffer.data);
 
677
            buffer.data = NULL;
 
678
          } else {
 
679
            ret = gnutls_openpgp_privkey_import
 
680
              (privkey, &buffer, GNUTLS_OPENPGP_FMT_BASE64, "", 0);
 
681
            if(ret != GNUTLS_E_SUCCESS){
 
682
              fprintf_plus(stderr, "Error importing OpenPGP key : %s",
 
683
                           safer_gnutls_strerror(ret));
 
684
              privkey = NULL;
 
685
            }
 
686
            free(buffer.data);
 
687
            buffer.data = NULL;
 
688
            if(privkey != NULL){
 
689
              /* Use private key to suggest an appropriate
 
690
                 sec_param */
 
691
              sec_param = gnutls_openpgp_privkey_sec_param(privkey);
 
692
              gnutls_openpgp_privkey_deinit(privkey);
 
693
              if(debug){
 
694
                fprintf_plus(stderr, "This OpenPGP key implies using"
 
695
                             " a GnuTLS security parameter \"%s\".\n",
 
696
                             safe_string(gnutls_sec_param_get_name
 
697
                                         (sec_param)));
 
698
              }
 
699
            }
 
700
          }
 
701
        }
 
702
        if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
 
703
          /* Err on the side of caution */
 
704
          sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
705
          if(debug){
 
706
            fprintf_plus(stderr, "Falling back to security parameter"
 
707
                         " \"%s\"\n",
 
708
                         safe_string(gnutls_sec_param_get_name
 
709
                                     (sec_param)));
 
710
          }
 
711
        }
 
712
      }
 
713
      uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
 
714
      if(uret != 0){
 
715
        mc->dh_bits = uret;
 
716
        if(debug){
 
717
          fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
 
718
                       " implies %u DH bits; using that.\n",
 
719
                       safe_string(gnutls_sec_param_get_name
 
720
                                   (sec_param)),
 
721
                       mc->dh_bits);
 
722
        }
 
723
      } else {
 
724
        fprintf_plus(stderr, "Failed to get implied number of DH"
 
725
                     " bits for security parameter \"%s\"): %s\n",
 
726
                     safe_string(gnutls_sec_param_get_name
 
727
                                 (sec_param)),
 
728
                     safer_gnutls_strerror(ret));
 
729
        goto globalfail;
 
730
      }
 
731
    } else if(debug){
 
732
      fprintf_plus(stderr, "DH bits explicitly set to %u\n",
 
733
                   mc->dh_bits);
 
734
    }
 
735
    ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
 
736
    if(ret != GNUTLS_E_SUCCESS){
 
737
      fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
 
738
                   " bits): %s\n", mc->dh_bits,
 
739
                   safer_gnutls_strerror(ret));
 
740
      goto globalfail;
 
741
    }
 
742
  }
 
743
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
 
744
  
 
745
  return 0;
 
746
  
 
747
 globalfail:
 
748
  
 
749
  gnutls_certificate_free_credentials(mc->cred);
 
750
  gnutls_dh_params_deinit(mc->dh_params);
 
751
  return -1;
 
752
}
 
753
 
 
754
__attribute__((nonnull, warn_unused_result))
 
755
static int init_gnutls_session(gnutls_session_t *session,
 
756
                               mandos_context *mc){
 
757
  int ret;
 
758
  /* GnuTLS session creation */
 
759
  do {
 
760
    ret = gnutls_init(session, GNUTLS_SERVER);
 
761
    if(quit_now){
 
762
      return -1;
 
763
    }
 
764
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
765
  if(ret != GNUTLS_E_SUCCESS){
 
766
    fprintf_plus(stderr,
 
767
                 "Error in GnuTLS session initialization: %s\n",
 
768
                 safer_gnutls_strerror(ret));
 
769
  }
 
770
  
 
771
  {
 
772
    const char *err;
 
773
    do {
 
774
      ret = gnutls_priority_set_direct(*session, mc->priority, &err);
 
775
      if(quit_now){
 
776
        gnutls_deinit(*session);
 
777
        return -1;
 
778
      }
 
779
    } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
780
    if(ret != GNUTLS_E_SUCCESS){
 
781
      fprintf_plus(stderr, "Syntax error at: %s\n", err);
 
782
      fprintf_plus(stderr, "GnuTLS error: %s\n",
 
783
                   safer_gnutls_strerror(ret));
 
784
      gnutls_deinit(*session);
 
785
      return -1;
 
786
    }
 
787
  }
 
788
  
 
789
  do {
 
790
    ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
791
                                 mc->cred);
 
792
    if(quit_now){
 
793
      gnutls_deinit(*session);
 
794
      return -1;
 
795
    }
 
796
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
797
  if(ret != GNUTLS_E_SUCCESS){
 
798
    fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
 
799
                 safer_gnutls_strerror(ret));
 
800
    gnutls_deinit(*session);
 
801
    return -1;
 
802
  }
 
803
  
 
804
  /* ignore client certificate if any. */
 
805
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
 
806
  
 
807
  return 0;
 
808
}
 
809
 
 
810
/* Avahi log function callback */
 
811
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
812
                      __attribute__((unused)) const char *txt){}
 
813
 
 
814
/* Set effective uid to 0, return errno */
 
815
__attribute__((warn_unused_result))
 
816
error_t raise_privileges(void){
 
817
  error_t old_errno = errno;
 
818
  error_t ret_errno = 0;
 
819
  if(seteuid(0) == -1){
 
820
    ret_errno = errno;
 
821
  }
 
822
  errno = old_errno;
 
823
  return ret_errno;
 
824
}
 
825
 
 
826
/* Set effective and real user ID to 0.  Return errno. */
 
827
__attribute__((warn_unused_result))
 
828
error_t raise_privileges_permanently(void){
 
829
  error_t old_errno = errno;
 
830
  error_t ret_errno = raise_privileges();
 
831
  if(ret_errno != 0){
 
832
    errno = old_errno;
 
833
    return ret_errno;
 
834
  }
 
835
  if(setuid(0) == -1){
 
836
    ret_errno = errno;
 
837
  }
 
838
  errno = old_errno;
 
839
  return ret_errno;
 
840
}
 
841
 
 
842
/* Set effective user ID to unprivileged saved user ID */
 
843
__attribute__((warn_unused_result))
 
844
error_t lower_privileges(void){
 
845
  error_t old_errno = errno;
 
846
  error_t ret_errno = 0;
 
847
  if(seteuid(uid) == -1){
 
848
    ret_errno = errno;
 
849
  }
 
850
  errno = old_errno;
 
851
  return ret_errno;
 
852
}
 
853
 
 
854
/* Lower privileges permanently */
 
855
__attribute__((warn_unused_result))
 
856
error_t lower_privileges_permanently(void){
 
857
  error_t old_errno = errno;
 
858
  error_t ret_errno = 0;
 
859
  if(setuid(uid) == -1){
 
860
    ret_errno = errno;
 
861
  }
 
862
  errno = old_errno;
 
863
  return ret_errno;
 
864
}
 
865
 
 
866
/* Helper function to add_local_route() and delete_local_route() */
 
867
__attribute__((nonnull, warn_unused_result))
 
868
static bool add_delete_local_route(const bool add,
 
869
                                   const char *address,
 
870
                                   AvahiIfIndex if_index){
 
871
  int ret;
 
872
  char helper[] = "mandos-client-iprouteadddel";
 
873
  char add_arg[] = "add";
 
874
  char delete_arg[] = "delete";
 
875
  char debug_flag[] = "--debug";
 
876
  char *pluginhelperdir = getenv("MANDOSPLUGINHELPERDIR");
 
877
  if(pluginhelperdir == NULL){
 
878
    if(debug){
 
879
      fprintf_plus(stderr, "MANDOSPLUGINHELPERDIR environment"
 
880
                   " variable not set; cannot run helper\n");
 
881
    }
 
882
    return false;
 
883
  }
 
884
  
 
885
  char interface[IF_NAMESIZE];
 
886
  if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
887
    perror_plus("if_indextoname");
 
888
    return false;
 
889
  }
 
890
  
 
891
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
892
  if(devnull == -1){
 
893
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
894
    return false;
 
895
  }
 
896
  pid_t pid = fork();
 
897
  if(pid == 0){
 
898
    /* Child */
 
899
    /* Raise privileges */
 
900
    errno = raise_privileges_permanently();
 
901
    if(errno != 0){
 
902
      perror_plus("Failed to raise privileges");
 
903
      /* _exit(EX_NOPERM); */
 
904
    } else {
 
905
      /* Set group */
 
906
      errno = 0;
 
907
      ret = setgid(0);
 
908
      if(ret == -1){
 
909
        perror_plus("setgid");
 
910
        _exit(EX_NOPERM);
 
911
      }
 
912
      /* Reset supplementary groups */
 
913
      errno = 0;
 
914
      ret = setgroups(0, NULL);
 
915
      if(ret == -1){
 
916
        perror_plus("setgroups");
 
917
        _exit(EX_NOPERM);
 
918
      }
 
919
    }
 
920
    ret = dup2(devnull, STDIN_FILENO);
 
921
    if(ret == -1){
 
922
      perror_plus("dup2(devnull, STDIN_FILENO)");
 
923
      _exit(EX_OSERR);
 
924
    }
 
925
    ret = close(devnull);
 
926
    if(ret == -1){
 
927
      perror_plus("close");
 
928
      _exit(EX_OSERR);
 
929
    }
 
930
    ret = dup2(STDERR_FILENO, STDOUT_FILENO);
 
931
    if(ret == -1){
 
932
      perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
 
933
      _exit(EX_OSERR);
 
934
    }
 
935
    int helperdir_fd = (int)TEMP_FAILURE_RETRY(open(pluginhelperdir,
 
936
                                                    O_RDONLY
 
937
                                                    | O_DIRECTORY
 
938
                                                    | O_PATH
 
939
                                                    | O_CLOEXEC));
 
940
    if(helperdir_fd == -1){
 
941
      perror_plus("open");
 
942
      _exit(EX_UNAVAILABLE);
 
943
    }
 
944
    int helper_fd = (int)TEMP_FAILURE_RETRY(openat(helperdir_fd,
 
945
                                                   helper, O_RDONLY));
 
946
    if(helper_fd == -1){
 
947
      perror_plus("openat");
 
948
      close(helperdir_fd);
 
949
      _exit(EX_UNAVAILABLE);
 
950
    }
 
951
    close(helperdir_fd);
 
952
#ifdef __GNUC__
 
953
#pragma GCC diagnostic push
 
954
#pragma GCC diagnostic ignored "-Wcast-qual"
 
955
#endif
 
956
    if(fexecve(helper_fd, (char *const [])
 
957
               { helper, add ? add_arg : delete_arg, (char *)address,
 
958
                   interface, debug ? debug_flag : NULL, NULL },
 
959
               environ) == -1){
 
960
#ifdef __GNUC__
 
961
#pragma GCC diagnostic pop
 
962
#endif
 
963
      perror_plus("fexecve");
 
964
      _exit(EXIT_FAILURE);
 
965
    }
 
966
  }
 
967
  if(pid == -1){
 
968
    perror_plus("fork");
 
969
    return false;
 
970
  }
 
971
  int status;
 
972
  pid_t pret = -1;
 
973
  errno = 0;
 
974
  do {
 
975
    pret = waitpid(pid, &status, 0);
 
976
    if(pret == -1 and errno == EINTR and quit_now){
 
977
      int errno_raising = 0;
 
978
      if((errno = raise_privileges()) != 0){
 
979
        errno_raising = errno;
 
980
        perror_plus("Failed to raise privileges in order to"
 
981
                    " kill helper program");
 
982
      }
 
983
      if(kill(pid, SIGTERM) == -1){
 
984
        perror_plus("kill");
 
985
      }
 
986
      if((errno_raising == 0) and (errno = lower_privileges()) != 0){
 
987
        perror_plus("Failed to lower privileges after killing"
 
988
                    " helper program");
 
989
      }
 
990
      return false;
 
991
    }
 
992
  } while(pret == -1 and errno == EINTR);
 
993
  if(pret == -1){
 
994
    perror_plus("waitpid");
 
995
    return false;
 
996
  }
 
997
  if(WIFEXITED(status)){
 
998
    if(WEXITSTATUS(status) != 0){
 
999
      fprintf_plus(stderr, "Error: iprouteadddel exited"
 
1000
                   " with status %d\n", WEXITSTATUS(status));
 
1001
      return false;
 
1002
    }
 
1003
    return true;
 
1004
  }
 
1005
  if(WIFSIGNALED(status)){
 
1006
    fprintf_plus(stderr, "Error: iprouteadddel died by"
 
1007
                 " signal %d\n", WTERMSIG(status));
 
1008
    return false;
 
1009
  }
 
1010
  fprintf_plus(stderr, "Error: iprouteadddel crashed\n");
 
1011
  return false;
 
1012
}
 
1013
 
 
1014
__attribute__((nonnull, warn_unused_result))
 
1015
static bool add_local_route(const char *address,
 
1016
                            AvahiIfIndex if_index){
 
1017
  if(debug){
 
1018
    fprintf_plus(stderr, "Adding route to %s\n", address);
 
1019
  }
 
1020
  return add_delete_local_route(true, address, if_index);
 
1021
}
 
1022
 
 
1023
__attribute__((nonnull, warn_unused_result))
 
1024
static bool delete_local_route(const char *address,
 
1025
                               AvahiIfIndex if_index){
 
1026
  if(debug){
 
1027
    fprintf_plus(stderr, "Removing route to %s\n", address);
 
1028
  }
 
1029
  return add_delete_local_route(false, address, if_index);
 
1030
}
 
1031
 
 
1032
/* Called when a Mandos server is found */
 
1033
__attribute__((nonnull, warn_unused_result))
 
1034
static int start_mandos_communication(const char *ip, in_port_t port,
 
1035
                                      AvahiIfIndex if_index,
 
1036
                                      int af, mandos_context *mc){
 
1037
  int ret, tcp_sd = -1;
 
1038
  ssize_t sret;
 
1039
  struct sockaddr_storage to;
 
1040
  char *buffer = NULL;
 
1041
  char *decrypted_buffer = NULL;
 
1042
  size_t buffer_length = 0;
 
1043
  size_t buffer_capacity = 0;
 
1044
  size_t written;
 
1045
  int retval = -1;
 
1046
  gnutls_session_t session;
 
1047
  int pf;                       /* Protocol family */
 
1048
  bool route_added = false;
 
1049
  
 
1050
  errno = 0;
 
1051
  
 
1052
  if(quit_now){
 
1053
    errno = EINTR;
 
1054
    return -1;
 
1055
  }
 
1056
  
 
1057
  switch(af){
 
1058
  case AF_INET6:
 
1059
    pf = PF_INET6;
 
1060
    break;
 
1061
  case AF_INET:
 
1062
    pf = PF_INET;
 
1063
    break;
 
1064
  default:
 
1065
    fprintf_plus(stderr, "Bad address family: %d\n", af);
 
1066
    errno = EINVAL;
 
1067
    return -1;
 
1068
  }
 
1069
  
 
1070
  /* If the interface is specified and we have a list of interfaces */
 
1071
  if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
 
1072
    /* Check if the interface is one of the interfaces we are using */
 
1073
    bool match = false;
 
1074
    {
 
1075
      char *interface = NULL;
 
1076
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
 
1077
                                 interface))){
 
1078
        if(if_nametoindex(interface) == (unsigned int)if_index){
 
1079
          match = true;
 
1080
          break;
 
1081
        }
 
1082
      }
 
1083
    }
 
1084
    if(not match){
 
1085
      /* This interface does not match any in the list, so we don't
 
1086
         connect to the server */
 
1087
      if(debug){
 
1088
        char interface[IF_NAMESIZE];
 
1089
        if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
1090
          perror_plus("if_indextoname");
 
1091
        } else {
 
1092
          fprintf_plus(stderr, "Skipping server on non-used interface"
 
1093
                       " \"%s\"\n",
 
1094
                       if_indextoname((unsigned int)if_index,
 
1095
                                      interface));
 
1096
        }
 
1097
      }
 
1098
      return -1;
 
1099
    }
 
1100
  }
 
1101
  
 
1102
  ret = init_gnutls_session(&session, mc);
 
1103
  if(ret != 0){
 
1104
    return -1;
 
1105
  }
 
1106
  
 
1107
  if(debug){
 
1108
    fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
 
1109
                 PRIuMAX "\n", ip, (uintmax_t)port);
 
1110
  }
 
1111
  
 
1112
  tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
 
1113
  if(tcp_sd < 0){
 
1114
    int e = errno;
 
1115
    perror_plus("socket");
 
1116
    errno = e;
 
1117
    goto mandos_end;
 
1118
  }
 
1119
  
 
1120
  if(quit_now){
 
1121
    errno = EINTR;
 
1122
    goto mandos_end;
 
1123
  }
 
1124
  
 
1125
  if(af == AF_INET6){
 
1126
    struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
 
1127
    *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
 
1128
    ret = inet_pton(af, ip, &to6->sin6_addr);
 
1129
  } else {                      /* IPv4 */
 
1130
    struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
 
1131
    *to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
 
1132
    ret = inet_pton(af, ip, &to4->sin_addr);
 
1133
  }
 
1134
  if(ret < 0 ){
 
1135
    int e = errno;
 
1136
    perror_plus("inet_pton");
 
1137
    errno = e;
 
1138
    goto mandos_end;
 
1139
  }
 
1140
  if(ret == 0){
 
1141
    int e = errno;
 
1142
    fprintf_plus(stderr, "Bad address: %s\n", ip);
 
1143
    errno = e;
 
1144
    goto mandos_end;
 
1145
  }
 
1146
  if(af == AF_INET6){
 
1147
    ((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
 
1148
    if(IN6_IS_ADDR_LINKLOCAL
 
1149
       (&((struct sockaddr_in6 *)&to)->sin6_addr)){
 
1150
      if(if_index == AVAHI_IF_UNSPEC){
 
1151
        fprintf_plus(stderr, "An IPv6 link-local address is"
 
1152
                     " incomplete without a network interface\n");
 
1153
        errno = EINVAL;
 
1154
        goto mandos_end;
 
1155
      }
 
1156
      /* Set the network interface number as scope */
 
1157
      ((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
 
1158
    }
 
1159
  } else {
 
1160
    ((struct sockaddr_in *)&to)->sin_port = htons(port);
 
1161
  }
 
1162
  
 
1163
  if(quit_now){
 
1164
    errno = EINTR;
 
1165
    goto mandos_end;
 
1166
  }
 
1167
  
 
1168
  if(debug){
 
1169
    if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
 
1170
      char interface[IF_NAMESIZE];
 
1171
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
1172
        perror_plus("if_indextoname");
 
1173
      } else {
 
1174
        fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
 
1175
                     "\n", ip, interface, (uintmax_t)port);
 
1176
      }
 
1177
    } else {
 
1178
      fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
 
1179
                   ip, (uintmax_t)port);
 
1180
    }
 
1181
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
 
1182
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
 
1183
    if(af == AF_INET6){
 
1184
      ret = getnameinfo((struct sockaddr *)&to,
 
1185
                        sizeof(struct sockaddr_in6),
 
1186
                        addrstr, sizeof(addrstr), NULL, 0,
 
1187
                        NI_NUMERICHOST);
 
1188
    } else {
 
1189
      ret = getnameinfo((struct sockaddr *)&to,
 
1190
                        sizeof(struct sockaddr_in),
 
1191
                        addrstr, sizeof(addrstr), NULL, 0,
 
1192
                        NI_NUMERICHOST);
 
1193
    }
 
1194
    if(ret == EAI_SYSTEM){
 
1195
      perror_plus("getnameinfo");
 
1196
    } else if(ret != 0) {
 
1197
      fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
 
1198
    } else if(strcmp(addrstr, ip) != 0){
 
1199
      fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
 
1200
    }
 
1201
  }
 
1202
  
 
1203
  if(quit_now){
 
1204
    errno = EINTR;
 
1205
    goto mandos_end;
 
1206
  }
 
1207
  
 
1208
  while(true){
 
1209
    if(af == AF_INET6){
 
1210
      ret = connect(tcp_sd, (struct sockaddr *)&to,
 
1211
                    sizeof(struct sockaddr_in6));
 
1212
    } else {
 
1213
      ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
 
1214
                    sizeof(struct sockaddr_in));
 
1215
    }
 
1216
    if(ret < 0){
 
1217
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
 
1218
         and if_index != AVAHI_IF_UNSPEC
 
1219
         and connect_to == NULL
 
1220
         and not route_added and
 
1221
         ((af == AF_INET6 and not
 
1222
           IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6 *)
 
1223
                                    &to)->sin6_addr)))
 
1224
          or (af == AF_INET and
 
1225
              /* Not a a IPv4LL address */
 
1226
              (ntohl(((struct sockaddr_in *)&to)->sin_addr.s_addr)
 
1227
               & 0xFFFF0000L) != 0xA9FE0000L))){
 
1228
        /* Work around Avahi bug - Avahi does not announce link-local
 
1229
           addresses if it has a global address, so local hosts with
 
1230
           *only* a link-local address (e.g. Mandos clients) cannot
 
1231
           connect to a Mandos server announced by Avahi on a server
 
1232
           host with a global address.  Work around this by retrying
 
1233
           with an explicit route added with the server's address.
 
1234
           
 
1235
           Avahi bug reference:
 
1236
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1237
           https://bugs.debian.org/587961
 
1238
        */
 
1239
        if(debug){
 
1240
          fprintf_plus(stderr, "Mandos server unreachable, trying"
 
1241
                       " direct route\n");
 
1242
        }
 
1243
        int e = errno;
 
1244
        route_added = add_local_route(ip, if_index);
 
1245
        if(route_added){
 
1246
          continue;
 
1247
        }
 
1248
        errno = e;
 
1249
      }
 
1250
      if(errno != ECONNREFUSED or debug){
 
1251
        int e = errno;
 
1252
        perror_plus("connect");
 
1253
        errno = e;
 
1254
      }
 
1255
      goto mandos_end;
 
1256
    }
 
1257
    
 
1258
    if(quit_now){
 
1259
      errno = EINTR;
 
1260
      goto mandos_end;
 
1261
    }
 
1262
    break;
 
1263
  }
 
1264
  
 
1265
  const char *out = mandos_protocol_version;
 
1266
  written = 0;
 
1267
  while(true){
 
1268
    size_t out_size = strlen(out);
 
1269
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
1270
                                        out_size - written));
 
1271
    if(ret == -1){
 
1272
      int e = errno;
 
1273
      perror_plus("write");
 
1274
      errno = e;
 
1275
      goto mandos_end;
 
1276
    }
 
1277
    written += (size_t)ret;
 
1278
    if(written < out_size){
 
1279
      continue;
 
1280
    } else {
 
1281
      if(out == mandos_protocol_version){
 
1282
        written = 0;
 
1283
        out = "\r\n";
 
1284
      } else {
 
1285
        break;
 
1286
      }
 
1287
    }
 
1288
  
 
1289
    if(quit_now){
 
1290
      errno = EINTR;
 
1291
      goto mandos_end;
 
1292
    }
 
1293
  }
 
1294
  
 
1295
  if(debug){
 
1296
    fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
 
1297
  }
 
1298
  
 
1299
  if(quit_now){
 
1300
    errno = EINTR;
 
1301
    goto mandos_end;
 
1302
  }
 
1303
  
 
1304
  /* This casting via intptr_t is to eliminate warning about casting
 
1305
     an int to a pointer type.  This is exactly how the GnuTLS Guile
 
1306
     function "set-session-transport-fd!" does it. */
 
1307
  gnutls_transport_set_ptr(session,
 
1308
                           (gnutls_transport_ptr_t)(intptr_t)tcp_sd);
 
1309
  
 
1310
  if(quit_now){
 
1311
    errno = EINTR;
 
1312
    goto mandos_end;
 
1313
  }
 
1314
  
 
1315
  do {
 
1316
    ret = gnutls_handshake(session);
 
1317
    if(quit_now){
 
1318
      errno = EINTR;
 
1319
      goto mandos_end;
 
1320
    }
 
1321
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
1322
  
 
1323
  if(ret != GNUTLS_E_SUCCESS){
 
1324
    if(debug){
 
1325
      fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
 
1326
      gnutls_perror(ret);
 
1327
    }
 
1328
    errno = EPROTO;
 
1329
    goto mandos_end;
 
1330
  }
 
1331
  
 
1332
  /* Read OpenPGP packet that contains the wanted password */
 
1333
  
 
1334
  if(debug){
 
1335
    fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
 
1336
                 " %s\n", ip);
 
1337
  }
 
1338
  
 
1339
  while(true){
 
1340
    
 
1341
    if(quit_now){
 
1342
      errno = EINTR;
 
1343
      goto mandos_end;
 
1344
    }
 
1345
    
 
1346
    buffer_capacity = incbuffer(&buffer, buffer_length,
 
1347
                                buffer_capacity);
 
1348
    if(buffer_capacity == 0){
 
1349
      int e = errno;
 
1350
      perror_plus("incbuffer");
 
1351
      errno = e;
 
1352
      goto mandos_end;
 
1353
    }
 
1354
    
 
1355
    if(quit_now){
 
1356
      errno = EINTR;
 
1357
      goto mandos_end;
 
1358
    }
 
1359
    
 
1360
    sret = gnutls_record_recv(session, buffer+buffer_length,
 
1361
                              BUFFER_SIZE);
 
1362
    if(sret == 0){
 
1363
      break;
 
1364
    }
 
1365
    if(sret < 0){
 
1366
      switch(sret){
 
1367
      case GNUTLS_E_INTERRUPTED:
 
1368
      case GNUTLS_E_AGAIN:
 
1369
        break;
 
1370
      case GNUTLS_E_REHANDSHAKE:
 
1371
        do {
 
1372
          ret = gnutls_handshake(session);
 
1373
          
 
1374
          if(quit_now){
 
1375
            errno = EINTR;
 
1376
            goto mandos_end;
 
1377
          }
 
1378
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
1379
        if(ret < 0){
 
1380
          fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
 
1381
                       "***\n");
 
1382
          gnutls_perror(ret);
 
1383
          errno = EPROTO;
 
1384
          goto mandos_end;
 
1385
        }
 
1386
        break;
 
1387
      default:
 
1388
        fprintf_plus(stderr, "Unknown error while reading data from"
 
1389
                     " encrypted session with Mandos server\n");
 
1390
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
1391
        errno = EIO;
 
1392
        goto mandos_end;
 
1393
      }
 
1394
    } else {
 
1395
      buffer_length += (size_t) sret;
 
1396
    }
 
1397
  }
 
1398
  
 
1399
  if(debug){
 
1400
    fprintf_plus(stderr, "Closing TLS session\n");
 
1401
  }
 
1402
  
 
1403
  if(quit_now){
 
1404
    errno = EINTR;
 
1405
    goto mandos_end;
 
1406
  }
 
1407
  
 
1408
  do {
 
1409
    ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
1410
    if(quit_now){
 
1411
      errno = EINTR;
 
1412
      goto mandos_end;
 
1413
    }
 
1414
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
1415
  
 
1416
  if(buffer_length > 0){
 
1417
    ssize_t decrypted_buffer_size;
 
1418
    decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
 
1419
                                               &decrypted_buffer, mc);
 
1420
    if(decrypted_buffer_size >= 0){
 
1421
      
 
1422
      written = 0;
 
1423
      while(written < (size_t) decrypted_buffer_size){
 
1424
        if(quit_now){
 
1425
          errno = EINTR;
 
1426
          goto mandos_end;
 
1427
        }
 
1428
        
 
1429
        ret = (int)fwrite(decrypted_buffer + written, 1,
 
1430
                          (size_t)decrypted_buffer_size - written,
 
1431
                          stdout);
 
1432
        if(ret == 0 and ferror(stdout)){
 
1433
          int e = errno;
 
1434
          if(debug){
 
1435
            fprintf_plus(stderr, "Error writing encrypted data: %s\n",
 
1436
                         strerror(errno));
 
1437
          }
 
1438
          errno = e;
 
1439
          goto mandos_end;
 
1440
        }
 
1441
        written += (size_t)ret;
 
1442
      }
 
1443
      retval = 0;
 
1444
    }
 
1445
  }
 
1446
  
 
1447
  /* Shutdown procedure */
 
1448
  
 
1449
 mandos_end:
 
1450
  {
 
1451
    if(route_added){
 
1452
      if(not delete_local_route(ip, if_index)){
 
1453
        fprintf_plus(stderr, "Failed to delete local route to %s on"
 
1454
                     " interface %d", ip, if_index);
 
1455
      }
 
1456
    }
 
1457
    int e = errno;
 
1458
    free(decrypted_buffer);
 
1459
    free(buffer);
 
1460
    if(tcp_sd >= 0){
 
1461
      ret = close(tcp_sd);
 
1462
    }
 
1463
    if(ret == -1){
 
1464
      if(e == 0){
 
1465
        e = errno;
 
1466
      }
 
1467
      perror_plus("close");
 
1468
    }
 
1469
    gnutls_deinit(session);
 
1470
    errno = e;
 
1471
    if(quit_now){
 
1472
      errno = EINTR;
 
1473
      retval = -1;
 
1474
    }
 
1475
  }
 
1476
  return retval;
 
1477
}
 
1478
 
 
1479
__attribute__((nonnull))
 
1480
static void resolve_callback(AvahiSServiceResolver *r,
 
1481
                             AvahiIfIndex interface,
 
1482
                             AvahiProtocol proto,
 
1483
                             AvahiResolverEvent event,
 
1484
                             const char *name,
 
1485
                             const char *type,
 
1486
                             const char *domain,
 
1487
                             const char *host_name,
 
1488
                             const AvahiAddress *address,
 
1489
                             uint16_t port,
 
1490
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
 
1491
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
1492
                             flags,
 
1493
                             void *mc){
 
1494
  if(r == NULL){
 
1495
    return;
 
1496
  }
 
1497
  
 
1498
  /* Called whenever a service has been resolved successfully or
 
1499
     timed out */
 
1500
  
 
1501
  if(quit_now){
 
1502
    avahi_s_service_resolver_free(r);
 
1503
    return;
 
1504
  }
 
1505
  
 
1506
  switch(event){
 
1507
  default:
 
1508
  case AVAHI_RESOLVER_FAILURE:
 
1509
    fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
 
1510
                 "'%s' of type '%s' in domain '%s': %s\n", name, type,
 
1511
                 domain,
 
1512
                 avahi_strerror(avahi_server_errno
 
1513
                                (((mandos_context*)mc)->server)));
 
1514
    break;
 
1515
    
 
1516
  case AVAHI_RESOLVER_FOUND:
 
1517
    {
 
1518
      char ip[AVAHI_ADDRESS_STR_MAX];
 
1519
      avahi_address_snprint(ip, sizeof(ip), address);
 
1520
      if(debug){
 
1521
        fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
1522
                     PRIdMAX ") on port %" PRIu16 "\n", name,
 
1523
                     host_name, ip, (intmax_t)interface, port);
 
1524
      }
 
1525
      int ret = start_mandos_communication(ip, (in_port_t)port,
 
1526
                                           interface,
 
1527
                                           avahi_proto_to_af(proto),
 
1528
                                           mc);
 
1529
      if(ret == 0){
 
1530
        avahi_simple_poll_quit(simple_poll);
 
1531
      } else {
 
1532
        if(not add_server(ip, (in_port_t)port, interface,
 
1533
                          avahi_proto_to_af(proto),
 
1534
                          &((mandos_context*)mc)->current_server)){
 
1535
          fprintf_plus(stderr, "Failed to add server \"%s\" to server"
 
1536
                       " list\n", name);
 
1537
        }
 
1538
      }
 
1539
    }
 
1540
  }
 
1541
  avahi_s_service_resolver_free(r);
 
1542
}
 
1543
 
 
1544
static void browse_callback(AvahiSServiceBrowser *b,
 
1545
                            AvahiIfIndex interface,
 
1546
                            AvahiProtocol protocol,
 
1547
                            AvahiBrowserEvent event,
 
1548
                            const char *name,
 
1549
                            const char *type,
 
1550
                            const char *domain,
 
1551
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
1552
                            flags,
 
1553
                            void *mc){
 
1554
  if(b == NULL){
 
1555
    return;
 
1556
  }
 
1557
  
 
1558
  /* Called whenever a new services becomes available on the LAN or
 
1559
     is removed from the LAN */
 
1560
  
 
1561
  if(quit_now){
 
1562
    return;
 
1563
  }
 
1564
  
 
1565
  switch(event){
 
1566
  default:
 
1567
  case AVAHI_BROWSER_FAILURE:
 
1568
    
 
1569
    fprintf_plus(stderr, "(Avahi browser) %s\n",
 
1570
                 avahi_strerror(avahi_server_errno
 
1571
                                (((mandos_context*)mc)->server)));
 
1572
    avahi_simple_poll_quit(simple_poll);
 
1573
    return;
 
1574
    
 
1575
  case AVAHI_BROWSER_NEW:
 
1576
    /* We ignore the returned Avahi resolver object. In the callback
 
1577
       function we free it. If the Avahi server is terminated before
 
1578
       the callback function is called the Avahi server will free the
 
1579
       resolver for us. */
 
1580
    
 
1581
    if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
 
1582
                                    interface, protocol, name, type,
 
1583
                                    domain, protocol, 0,
 
1584
                                    resolve_callback, mc) == NULL)
 
1585
      fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
 
1586
                   " %s\n", name,
 
1587
                   avahi_strerror(avahi_server_errno
 
1588
                                  (((mandos_context*)mc)->server)));
 
1589
    break;
 
1590
    
 
1591
  case AVAHI_BROWSER_REMOVE:
 
1592
    break;
 
1593
    
 
1594
  case AVAHI_BROWSER_ALL_FOR_NOW:
 
1595
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
1596
    if(debug){
 
1597
      fprintf_plus(stderr, "No Mandos server found, still"
 
1598
                   " searching...\n");
 
1599
    }
 
1600
    break;
 
1601
  }
 
1602
}
 
1603
 
 
1604
/* Signal handler that stops main loop after SIGTERM */
 
1605
static void handle_sigterm(int sig){
 
1606
  if(quit_now){
 
1607
    return;
 
1608
  }
 
1609
  quit_now = 1;
 
1610
  signal_received = sig;
 
1611
  int old_errno = errno;
 
1612
  /* set main loop to exit */
 
1613
  if(simple_poll != NULL){
 
1614
    avahi_simple_poll_quit(simple_poll);
 
1615
  }
 
1616
  errno = old_errno;
 
1617
}
 
1618
 
 
1619
__attribute__((nonnull, warn_unused_result))
 
1620
bool get_flags(const char *ifname, struct ifreq *ifr){
 
1621
  int ret;
 
1622
  error_t ret_errno;
 
1623
  
 
1624
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1625
  if(s < 0){
 
1626
    ret_errno = errno;
 
1627
    perror_plus("socket");
 
1628
    errno = ret_errno;
 
1629
    return false;
 
1630
  }
 
1631
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
 
1632
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
 
1633
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
 
1634
  if(ret == -1){
 
1635
    if(debug){
 
1636
      ret_errno = errno;
 
1637
      perror_plus("ioctl SIOCGIFFLAGS");
 
1638
      errno = ret_errno;
 
1639
    }
 
1640
    return false;
 
1641
  }
 
1642
  return true;
 
1643
}
 
1644
 
 
1645
__attribute__((nonnull, warn_unused_result))
 
1646
bool good_flags(const char *ifname, const struct ifreq *ifr){
 
1647
  
 
1648
  /* Reject the loopback device */
 
1649
  if(ifr->ifr_flags & IFF_LOOPBACK){
 
1650
    if(debug){
 
1651
      fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
 
1652
                   ifname);
 
1653
    }
 
1654
    return false;
 
1655
  }
 
1656
  /* Accept point-to-point devices only if connect_to is specified */
 
1657
  if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
 
1658
    if(debug){
 
1659
      fprintf_plus(stderr, "Accepting point-to-point interface"
 
1660
                   " \"%s\"\n", ifname);
 
1661
    }
 
1662
    return true;
 
1663
  }
 
1664
  /* Otherwise, reject non-broadcast-capable devices */
 
1665
  if(not (ifr->ifr_flags & IFF_BROADCAST)){
 
1666
    if(debug){
 
1667
      fprintf_plus(stderr, "Rejecting non-broadcast interface"
 
1668
                   " \"%s\"\n", ifname);
 
1669
    }
 
1670
    return false;
 
1671
  }
 
1672
  /* Reject non-ARP interfaces (including dummy interfaces) */
 
1673
  if(ifr->ifr_flags & IFF_NOARP){
 
1674
    if(debug){
 
1675
      fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
 
1676
                   ifname);
 
1677
    }
 
1678
    return false;
 
1679
  }
 
1680
  
 
1681
  /* Accept this device */
 
1682
  if(debug){
 
1683
    fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
 
1684
  }
 
1685
  return true;
 
1686
}
 
1687
 
 
1688
/* 
 
1689
 * This function determines if a directory entry in /sys/class/net
 
1690
 * corresponds to an acceptable network device.
 
1691
 * (This function is passed to scandir(3) as a filter function.)
 
1692
 */
 
1693
__attribute__((nonnull, warn_unused_result))
 
1694
int good_interface(const struct dirent *if_entry){
 
1695
  if(if_entry->d_name[0] == '.'){
 
1696
    return 0;
 
1697
  }
 
1698
  
 
1699
  struct ifreq ifr;
 
1700
  if(not get_flags(if_entry->d_name, &ifr)){
 
1701
    if(debug){
 
1702
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1703
                   "\"%s\"\n", if_entry->d_name);
 
1704
    }
 
1705
    return 0;
 
1706
  }
 
1707
  
 
1708
  if(not good_flags(if_entry->d_name, &ifr)){
 
1709
    return 0;
 
1710
  }
 
1711
  return 1;
 
1712
}
 
1713
 
 
1714
/* 
 
1715
 * This function determines if a network interface is up.
 
1716
 */
 
1717
__attribute__((nonnull, warn_unused_result))
 
1718
bool interface_is_up(const char *interface){
 
1719
  struct ifreq ifr;
 
1720
  if(not get_flags(interface, &ifr)){
 
1721
    if(debug){
 
1722
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1723
                   "\"%s\"\n", interface);
 
1724
    }
 
1725
    return false;
 
1726
  }
 
1727
  
 
1728
  return (bool)(ifr.ifr_flags & IFF_UP);
 
1729
}
 
1730
 
 
1731
/* 
 
1732
 * This function determines if a network interface is running
 
1733
 */
 
1734
__attribute__((nonnull, warn_unused_result))
 
1735
bool interface_is_running(const char *interface){
 
1736
  struct ifreq ifr;
 
1737
  if(not get_flags(interface, &ifr)){
 
1738
    if(debug){
 
1739
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1740
                   "\"%s\"\n", interface);
 
1741
    }
 
1742
    return false;
 
1743
  }
 
1744
  
 
1745
  return (bool)(ifr.ifr_flags & IFF_RUNNING);
 
1746
}
 
1747
 
 
1748
__attribute__((nonnull, pure, warn_unused_result))
 
1749
int notdotentries(const struct dirent *direntry){
 
1750
  /* Skip "." and ".." */
 
1751
  if(direntry->d_name[0] == '.'
 
1752
     and (direntry->d_name[1] == '\0'
 
1753
          or (direntry->d_name[1] == '.'
 
1754
              and direntry->d_name[2] == '\0'))){
 
1755
    return 0;
 
1756
  }
 
1757
  return 1;
 
1758
}
 
1759
 
 
1760
/* Is this directory entry a runnable program? */
 
1761
__attribute__((nonnull, warn_unused_result))
 
1762
int runnable_hook(const struct dirent *direntry){
 
1763
  int ret;
 
1764
  size_t sret;
 
1765
  struct stat st;
 
1766
  
 
1767
  if((direntry->d_name)[0] == '\0'){
 
1768
    /* Empty name? */
 
1769
    return 0;
 
1770
  }
 
1771
  
 
1772
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 
1773
                "abcdefghijklmnopqrstuvwxyz"
 
1774
                "0123456789"
 
1775
                "_.-");
 
1776
  if((direntry->d_name)[sret] != '\0'){
 
1777
    /* Contains non-allowed characters */
 
1778
    if(debug){
 
1779
      fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
 
1780
                   direntry->d_name);
 
1781
    }
 
1782
    return 0;
 
1783
  }
 
1784
  
 
1785
  ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
 
1786
  if(ret == -1){
 
1787
    if(debug){
 
1788
      perror_plus("Could not stat hook");
 
1789
    }
 
1790
    return 0;
 
1791
  }
 
1792
  if(not (S_ISREG(st.st_mode))){
 
1793
    /* Not a regular file */
 
1794
    if(debug){
 
1795
      fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
 
1796
                   direntry->d_name);
 
1797
    }
 
1798
    return 0;
 
1799
  }
 
1800
  if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
 
1801
    /* Not executable */
 
1802
    if(debug){
 
1803
      fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
 
1804
                   direntry->d_name);
 
1805
    }
 
1806
    return 0;
 
1807
  }
 
1808
  if(debug){
 
1809
    fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
 
1810
                 direntry->d_name);
 
1811
  }
 
1812
  return 1;
 
1813
}
 
1814
 
 
1815
__attribute__((nonnull, warn_unused_result))
 
1816
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
 
1817
                            mandos_context *mc){
 
1818
  int ret;
 
1819
  struct timespec now;
 
1820
  struct timespec waited_time;
 
1821
  intmax_t block_time;
 
1822
  
 
1823
  while(true){
 
1824
    if(mc->current_server == NULL){
 
1825
      if(debug){
 
1826
        fprintf_plus(stderr, "Wait until first server is found."
 
1827
                     " No timeout!\n");
 
1828
      }
 
1829
      ret = avahi_simple_poll_iterate(s, -1);
 
1830
    } else {
 
1831
      if(debug){
 
1832
        fprintf_plus(stderr, "Check current_server if we should run"
 
1833
                     " it, or wait\n");
 
1834
      }
 
1835
      /* the current time */
 
1836
      ret = clock_gettime(CLOCK_MONOTONIC, &now);
 
1837
      if(ret == -1){
 
1838
        perror_plus("clock_gettime");
 
1839
        return -1;
 
1840
      }
 
1841
      /* Calculating in ms how long time between now and server
 
1842
         who we visted longest time ago. Now - last seen.  */
 
1843
      waited_time.tv_sec = (now.tv_sec
 
1844
                            - mc->current_server->last_seen.tv_sec);
 
1845
      waited_time.tv_nsec = (now.tv_nsec
 
1846
                             - mc->current_server->last_seen.tv_nsec);
 
1847
      /* total time is 10s/10,000ms.
 
1848
         Converting to s from ms by dividing by 1,000,
 
1849
         and ns to ms by dividing by 1,000,000. */
 
1850
      block_time = ((retry_interval
 
1851
                     - ((intmax_t)waited_time.tv_sec * 1000))
 
1852
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
 
1853
      
 
1854
      if(debug){
 
1855
        fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
 
1856
                     block_time);
 
1857
      }
 
1858
      
 
1859
      if(block_time <= 0){
 
1860
        ret = start_mandos_communication(mc->current_server->ip,
 
1861
                                         mc->current_server->port,
 
1862
                                         mc->current_server->if_index,
 
1863
                                         mc->current_server->af, mc);
 
1864
        if(ret == 0){
 
1865
          avahi_simple_poll_quit(s);
 
1866
          return 0;
 
1867
        }
 
1868
        ret = clock_gettime(CLOCK_MONOTONIC,
 
1869
                            &mc->current_server->last_seen);
 
1870
        if(ret == -1){
 
1871
          perror_plus("clock_gettime");
 
1872
          return -1;
 
1873
        }
 
1874
        mc->current_server = mc->current_server->next;
 
1875
        block_time = 0;         /* Call avahi to find new Mandos
 
1876
                                   servers, but don't block */
 
1877
      }
 
1878
      
 
1879
      ret = avahi_simple_poll_iterate(s, (int)block_time);
 
1880
    }
 
1881
    if(ret != 0){
 
1882
      if(ret > 0 or errno != EINTR){
 
1883
        return (ret != 1) ? ret : 0;
 
1884
      }
 
1885
    }
 
1886
  }
 
1887
}
 
1888
 
 
1889
__attribute__((nonnull))
 
1890
void run_network_hooks(const char *mode, const char *interface,
 
1891
                       const float delay){
 
1892
  struct dirent **direntries = NULL;
 
1893
  if(hookdir_fd == -1){
 
1894
    hookdir_fd = open(hookdir, O_RDONLY | O_DIRECTORY | O_PATH
 
1895
                      | O_CLOEXEC);
 
1896
    if(hookdir_fd == -1){
 
1897
      if(errno == ENOENT){
 
1898
        if(debug){
 
1899
          fprintf_plus(stderr, "Network hook directory \"%s\" not"
 
1900
                       " found\n", hookdir);
 
1901
        }
 
1902
      } else {
 
1903
        perror_plus("open");
 
1904
      }
 
1905
      return;
 
1906
    }
 
1907
  }
 
1908
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
 
1909
                           runnable_hook, alphasort);
 
1910
  if(numhooks == -1){
 
1911
    perror_plus("scandir");
 
1912
    return;
 
1913
  }
 
1914
  struct dirent *direntry;
 
1915
  int ret;
 
1916
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
1917
  if(devnull == -1){
 
1918
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
1919
    return;
 
1920
  }
 
1921
  for(int i = 0; i < numhooks; i++){
 
1922
    direntry = direntries[i];
 
1923
    if(debug){
 
1924
      fprintf_plus(stderr, "Running network hook \"%s\"\n",
 
1925
                   direntry->d_name);
 
1926
    }
 
1927
    pid_t hook_pid = fork();
 
1928
    if(hook_pid == 0){
 
1929
      /* Child */
 
1930
      /* Raise privileges */
 
1931
      errno = raise_privileges_permanently();
 
1932
      if(errno != 0){
 
1933
        perror_plus("Failed to raise privileges");
 
1934
        _exit(EX_NOPERM);
 
1935
      }
 
1936
      /* Set group */
 
1937
      errno = 0;
 
1938
      ret = setgid(0);
 
1939
      if(ret == -1){
 
1940
        perror_plus("setgid");
 
1941
        _exit(EX_NOPERM);
 
1942
      }
 
1943
      /* Reset supplementary groups */
 
1944
      errno = 0;
 
1945
      ret = setgroups(0, NULL);
 
1946
      if(ret == -1){
 
1947
        perror_plus("setgroups");
 
1948
        _exit(EX_NOPERM);
 
1949
      }
 
1950
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
 
1951
      if(ret == -1){
 
1952
        perror_plus("setenv");
 
1953
        _exit(EX_OSERR);
 
1954
      }
 
1955
      ret = setenv("DEVICE", interface, 1);
 
1956
      if(ret == -1){
 
1957
        perror_plus("setenv");
 
1958
        _exit(EX_OSERR);
 
1959
      }
 
1960
      ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
 
1961
      if(ret == -1){
 
1962
        perror_plus("setenv");
 
1963
        _exit(EX_OSERR);
 
1964
      }
 
1965
      ret = setenv("MODE", mode, 1);
 
1966
      if(ret == -1){
 
1967
        perror_plus("setenv");
 
1968
        _exit(EX_OSERR);
 
1969
      }
 
1970
      char *delaystring;
 
1971
      ret = asprintf(&delaystring, "%f", (double)delay);
 
1972
      if(ret == -1){
 
1973
        perror_plus("asprintf");
 
1974
        _exit(EX_OSERR);
 
1975
      }
 
1976
      ret = setenv("DELAY", delaystring, 1);
 
1977
      if(ret == -1){
 
1978
        free(delaystring);
 
1979
        perror_plus("setenv");
 
1980
        _exit(EX_OSERR);
 
1981
      }
 
1982
      free(delaystring);
 
1983
      if(connect_to != NULL){
 
1984
        ret = setenv("CONNECT", connect_to, 1);
 
1985
        if(ret == -1){
 
1986
          perror_plus("setenv");
 
1987
          _exit(EX_OSERR);
 
1988
        }
 
1989
      }
 
1990
      int hook_fd = (int)TEMP_FAILURE_RETRY(openat(hookdir_fd,
 
1991
                                                   direntry->d_name,
 
1992
                                                   O_RDONLY));
 
1993
      if(hook_fd == -1){
 
1994
        perror_plus("openat");
 
1995
        _exit(EXIT_FAILURE);
 
1996
      }
 
1997
      if(close(hookdir_fd) == -1){
 
1998
        perror_plus("close");
 
1999
        _exit(EXIT_FAILURE);
 
2000
      }
 
2001
      ret = dup2(devnull, STDIN_FILENO);
 
2002
      if(ret == -1){
 
2003
        perror_plus("dup2(devnull, STDIN_FILENO)");
 
2004
        _exit(EX_OSERR);
 
2005
      }
 
2006
      ret = close(devnull);
 
2007
      if(ret == -1){
 
2008
        perror_plus("close");
 
2009
        _exit(EX_OSERR);
 
2010
      }
 
2011
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
 
2012
      if(ret == -1){
 
2013
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
 
2014
        _exit(EX_OSERR);
 
2015
      }
 
2016
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
 
2017
                 environ) == -1){
 
2018
        perror_plus("fexecve");
 
2019
        _exit(EXIT_FAILURE);
 
2020
      }
 
2021
    } else {
 
2022
      if(hook_pid == -1){
 
2023
        perror_plus("fork");
 
2024
        free(direntry);
 
2025
        continue;
 
2026
      }
 
2027
      int status;
 
2028
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
 
2029
        perror_plus("waitpid");
 
2030
        free(direntry);
 
2031
        continue;
 
2032
      }
 
2033
      if(WIFEXITED(status)){
 
2034
        if(WEXITSTATUS(status) != 0){
 
2035
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
 
2036
                       " with status %d\n", direntry->d_name,
 
2037
                       WEXITSTATUS(status));
 
2038
          free(direntry);
 
2039
          continue;
 
2040
        }
 
2041
      } else if(WIFSIGNALED(status)){
 
2042
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
 
2043
                     " signal %d\n", direntry->d_name,
 
2044
                     WTERMSIG(status));
 
2045
        free(direntry);
 
2046
        continue;
 
2047
      } else {
 
2048
        fprintf_plus(stderr, "Warning: network hook \"%s\""
 
2049
                     " crashed\n", direntry->d_name);
 
2050
        free(direntry);
 
2051
        continue;
 
2052
      }
 
2053
    }
 
2054
    if(debug){
 
2055
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
 
2056
                   direntry->d_name);
 
2057
    }
 
2058
    free(direntry);
 
2059
  }
 
2060
  free(direntries);
 
2061
  if(close(hookdir_fd) == -1){
 
2062
    perror_plus("close");
 
2063
  } else {
 
2064
    hookdir_fd = -1;
 
2065
  }
 
2066
  close(devnull);
 
2067
}
 
2068
 
 
2069
__attribute__((nonnull, warn_unused_result))
 
2070
error_t bring_up_interface(const char *const interface,
 
2071
                           const float delay){
 
2072
  error_t old_errno = errno;
 
2073
  int ret;
 
2074
  struct ifreq network;
 
2075
  unsigned int if_index = if_nametoindex(interface);
 
2076
  if(if_index == 0){
 
2077
    fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
 
2078
    errno = old_errno;
 
2079
    return ENXIO;
 
2080
  }
 
2081
  
 
2082
  if(quit_now){
 
2083
    errno = old_errno;
 
2084
    return EINTR;
 
2085
  }
 
2086
  
 
2087
  if(not interface_is_up(interface)){
 
2088
    error_t ret_errno = 0, ioctl_errno = 0;
 
2089
    if(not get_flags(interface, &network)){
 
2090
      ret_errno = errno;
 
2091
      fprintf_plus(stderr, "Failed to get flags for interface "
 
2092
                   "\"%s\"\n", interface);
 
2093
      errno = old_errno;
 
2094
      return ret_errno;
 
2095
    }
 
2096
    network.ifr_flags |= IFF_UP; /* set flag */
 
2097
    
 
2098
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
2099
    if(sd == -1){
 
2100
      ret_errno = errno;
 
2101
      perror_plus("socket");
 
2102
      errno = old_errno;
 
2103
      return ret_errno;
 
2104
    }
 
2105
    
 
2106
    if(quit_now){
 
2107
      ret = close(sd);
 
2108
      if(ret == -1){
 
2109
        perror_plus("close");
 
2110
      }
 
2111
      errno = old_errno;
 
2112
      return EINTR;
 
2113
    }
 
2114
    
 
2115
    if(debug){
 
2116
      fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
 
2117
                   interface);
 
2118
    }
 
2119
    
 
2120
    /* Raise privileges */
 
2121
    ret_errno = raise_privileges();
 
2122
    if(ret_errno != 0){
 
2123
      errno = ret_errno;
 
2124
      perror_plus("Failed to raise privileges");
 
2125
    }
 
2126
    
 
2127
#ifdef __linux__
 
2128
    int ret_linux;
 
2129
    bool restore_loglevel = false;
 
2130
    if(ret_errno == 0){
 
2131
      /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
2132
         messages about the network interface to mess up the prompt */
 
2133
      ret_linux = klogctl(8, NULL, 5);
 
2134
      if(ret_linux == -1){
 
2135
        perror_plus("klogctl");
 
2136
      } else {
 
2137
        restore_loglevel = true;
 
2138
      }
 
2139
    }
 
2140
#endif  /* __linux__ */
 
2141
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
2142
    ioctl_errno = errno;
 
2143
#ifdef __linux__
 
2144
    if(restore_loglevel){
 
2145
      ret_linux = klogctl(7, NULL, 0);
 
2146
      if(ret_linux == -1){
 
2147
        perror_plus("klogctl");
 
2148
      }
 
2149
    }
 
2150
#endif  /* __linux__ */
 
2151
    
 
2152
    /* If raise_privileges() succeeded above */
 
2153
    if(ret_errno == 0){
 
2154
      /* Lower privileges */
 
2155
      ret_errno = lower_privileges();
 
2156
      if(ret_errno != 0){
 
2157
        errno = ret_errno;
 
2158
        perror_plus("Failed to lower privileges");
 
2159
      }
 
2160
    }
 
2161
    
 
2162
    /* Close the socket */
 
2163
    ret = close(sd);
 
2164
    if(ret == -1){
 
2165
      perror_plus("close");
 
2166
    }
 
2167
    
 
2168
    if(ret_setflags == -1){
 
2169
      errno = ioctl_errno;
 
2170
      perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
 
2171
      errno = old_errno;
 
2172
      return ioctl_errno;
 
2173
    }
 
2174
  } else if(debug){
 
2175
    fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
 
2176
                 interface);
 
2177
  }
 
2178
  
 
2179
  /* Sleep checking until interface is running.
 
2180
     Check every 0.25s, up to total time of delay */
 
2181
  for(int i=0; i < delay * 4; i++){
 
2182
    if(interface_is_running(interface)){
 
2183
      break;
 
2184
    }
 
2185
    struct timespec sleeptime = { .tv_nsec = 250000000 };
 
2186
    ret = nanosleep(&sleeptime, NULL);
 
2187
    if(ret == -1 and errno != EINTR){
 
2188
      perror_plus("nanosleep");
 
2189
    }
 
2190
  }
 
2191
  
 
2192
  errno = old_errno;
 
2193
  return 0;
 
2194
}
 
2195
 
 
2196
__attribute__((nonnull, warn_unused_result))
 
2197
error_t take_down_interface(const char *const interface){
 
2198
  error_t old_errno = errno;
 
2199
  struct ifreq network;
 
2200
  unsigned int if_index = if_nametoindex(interface);
 
2201
  if(if_index == 0){
 
2202
    fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
 
2203
    errno = old_errno;
 
2204
    return ENXIO;
 
2205
  }
 
2206
  if(interface_is_up(interface)){
 
2207
    error_t ret_errno = 0, ioctl_errno = 0;
 
2208
    if(not get_flags(interface, &network) and debug){
 
2209
      ret_errno = errno;
 
2210
      fprintf_plus(stderr, "Failed to get flags for interface "
 
2211
                   "\"%s\"\n", interface);
 
2212
      errno = old_errno;
 
2213
      return ret_errno;
 
2214
    }
 
2215
    network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
 
2216
    
 
2217
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
2218
    if(sd == -1){
 
2219
      ret_errno = errno;
 
2220
      perror_plus("socket");
 
2221
      errno = old_errno;
 
2222
      return ret_errno;
 
2223
    }
 
2224
    
 
2225
    if(debug){
 
2226
      fprintf_plus(stderr, "Taking down interface \"%s\"\n",
 
2227
                   interface);
 
2228
    }
 
2229
    
 
2230
    /* Raise privileges */
 
2231
    ret_errno = raise_privileges();
 
2232
    if(ret_errno != 0){
 
2233
      errno = ret_errno;
 
2234
      perror_plus("Failed to raise privileges");
 
2235
    }
 
2236
    
 
2237
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
2238
    ioctl_errno = errno;
 
2239
    
 
2240
    /* If raise_privileges() succeeded above */
 
2241
    if(ret_errno == 0){
 
2242
      /* Lower privileges */
 
2243
      ret_errno = lower_privileges();
 
2244
      if(ret_errno != 0){
 
2245
        errno = ret_errno;
 
2246
        perror_plus("Failed to lower privileges");
 
2247
      }
 
2248
    }
 
2249
    
 
2250
    /* Close the socket */
 
2251
    int ret = close(sd);
 
2252
    if(ret == -1){
 
2253
      perror_plus("close");
 
2254
    }
 
2255
    
 
2256
    if(ret_setflags == -1){
 
2257
      errno = ioctl_errno;
 
2258
      perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
 
2259
      errno = old_errno;
 
2260
      return ioctl_errno;
 
2261
    }
 
2262
  } else if(debug){
 
2263
    fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
 
2264
                 interface);
 
2265
  }
 
2266
  
 
2267
  errno = old_errno;
 
2268
  return 0;
 
2269
}
 
2270
 
 
2271
int main(int argc, char *argv[]){
 
2272
  mandos_context mc = { .server = NULL, .dh_bits = 0,
 
2273
                        .priority = "SECURE256:!CTYPE-X.509"
 
2274
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
 
2275
                        .current_server = NULL, .interfaces = NULL,
 
2276
                        .interfaces_size = 0 };
 
2277
  AvahiSServiceBrowser *sb = NULL;
 
2278
  error_t ret_errno;
 
2279
  int ret;
 
2280
  intmax_t tmpmax;
 
2281
  char *tmp;
 
2282
  int exitcode = EXIT_SUCCESS;
 
2283
  char *interfaces_to_take_down = NULL;
 
2284
  size_t interfaces_to_take_down_size = 0;
 
2285
  char run_tempdir[] = "/run/tmp/mandosXXXXXX";
 
2286
  char old_tempdir[] = "/tmp/mandosXXXXXX";
 
2287
  char *tempdir = NULL;
 
2288
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
2289
  const char *seckey = PATHDIR "/" SECKEY;
 
2290
  const char *pubkey = PATHDIR "/" PUBKEY;
 
2291
  const char *dh_params_file = NULL;
 
2292
  char *interfaces_hooks = NULL;
 
2293
  
 
2294
  bool gnutls_initialized = false;
 
2295
  bool gpgme_initialized = false;
 
2296
  float delay = 2.5f;
 
2297
  double retry_interval = 10; /* 10s between trying a server and
 
2298
                                 retrying the same server again */
 
2299
  
 
2300
  struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
 
2301
  struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
 
2302
  
 
2303
  uid = getuid();
 
2304
  gid = getgid();
 
2305
  
 
2306
  /* Lower any group privileges we might have, just to be safe */
 
2307
  errno = 0;
 
2308
  ret = setgid(gid);
 
2309
  if(ret == -1){
 
2310
    perror_plus("setgid");
 
2311
  }
 
2312
  
 
2313
  /* Lower user privileges (temporarily) */
 
2314
  errno = 0;
 
2315
  ret = seteuid(uid);
 
2316
  if(ret == -1){
 
2317
    perror_plus("seteuid");
 
2318
  }
 
2319
  
 
2320
  if(quit_now){
 
2321
    goto end;
 
2322
  }
 
2323
  
 
2324
  {
 
2325
    struct argp_option options[] = {
 
2326
      { .name = "debug", .key = 128,
 
2327
        .doc = "Debug mode", .group = 3 },
 
2328
      { .name = "connect", .key = 'c',
 
2329
        .arg = "ADDRESS:PORT",
 
2330
        .doc = "Connect directly to a specific Mandos server",
 
2331
        .group = 1 },
 
2332
      { .name = "interface", .key = 'i',
 
2333
        .arg = "NAME",
 
2334
        .doc = "Network interface that will be used to search for"
 
2335
        " Mandos servers",
 
2336
        .group = 1 },
 
2337
      { .name = "seckey", .key = 's',
 
2338
        .arg = "FILE",
 
2339
        .doc = "OpenPGP secret key file base name",
 
2340
        .group = 1 },
 
2341
      { .name = "pubkey", .key = 'p',
 
2342
        .arg = "FILE",
 
2343
        .doc = "OpenPGP public key file base name",
 
2344
        .group = 2 },
 
2345
      { .name = "dh-bits", .key = 129,
 
2346
        .arg = "BITS",
 
2347
        .doc = "Bit length of the prime number used in the"
 
2348
        " Diffie-Hellman key exchange",
 
2349
        .group = 2 },
 
2350
      { .name = "dh-params", .key = 134,
 
2351
        .arg = "FILE",
 
2352
        .doc = "PEM-encoded PKCS#3 file with pre-generated parameters"
 
2353
        " for the Diffie-Hellman key exchange",
 
2354
        .group = 2 },
 
2355
      { .name = "priority", .key = 130,
 
2356
        .arg = "STRING",
 
2357
        .doc = "GnuTLS priority string for the TLS handshake",
 
2358
        .group = 1 },
 
2359
      { .name = "delay", .key = 131,
 
2360
        .arg = "SECONDS",
 
2361
        .doc = "Maximum delay to wait for interface startup",
 
2362
        .group = 2 },
 
2363
      { .name = "retry", .key = 132,
 
2364
        .arg = "SECONDS",
 
2365
        .doc = "Retry interval used when denied by the Mandos server",
 
2366
        .group = 2 },
 
2367
      { .name = "network-hook-dir", .key = 133,
 
2368
        .arg = "DIR",
 
2369
        .doc = "Directory where network hooks are located",
 
2370
        .group = 2 },
 
2371
      /*
 
2372
       * These reproduce what we would get without ARGP_NO_HELP
 
2373
       */
 
2374
      { .name = "help", .key = '?',
 
2375
        .doc = "Give this help list", .group = -1 },
 
2376
      { .name = "usage", .key = -3,
 
2377
        .doc = "Give a short usage message", .group = -1 },
 
2378
      { .name = "version", .key = 'V',
 
2379
        .doc = "Print program version", .group = -1 },
 
2380
      { .name = NULL }
 
2381
    };
 
2382
    
 
2383
    error_t parse_opt(int key, char *arg,
 
2384
                      struct argp_state *state){
 
2385
      errno = 0;
 
2386
      switch(key){
 
2387
      case 128:                 /* --debug */
 
2388
        debug = true;
 
2389
        break;
 
2390
      case 'c':                 /* --connect */
 
2391
        connect_to = arg;
 
2392
        break;
 
2393
      case 'i':                 /* --interface */
 
2394
        ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
 
2395
                                 arg, (int)',');
 
2396
        if(ret_errno != 0){
 
2397
          argp_error(state, "%s", strerror(ret_errno));
 
2398
        }
 
2399
        break;
 
2400
      case 's':                 /* --seckey */
 
2401
        seckey = arg;
 
2402
        break;
 
2403
      case 'p':                 /* --pubkey */
 
2404
        pubkey = arg;
 
2405
        break;
 
2406
      case 129:                 /* --dh-bits */
 
2407
        errno = 0;
 
2408
        tmpmax = strtoimax(arg, &tmp, 10);
 
2409
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
2410
           or tmpmax != (typeof(mc.dh_bits))tmpmax){
 
2411
          argp_error(state, "Bad number of DH bits");
 
2412
        }
 
2413
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
 
2414
        break;
 
2415
      case 134:                 /* --dh-params */
 
2416
        dh_params_file = arg;
 
2417
        break;
 
2418
      case 130:                 /* --priority */
 
2419
        mc.priority = arg;
 
2420
        break;
 
2421
      case 131:                 /* --delay */
 
2422
        errno = 0;
 
2423
        delay = strtof(arg, &tmp);
 
2424
        if(errno != 0 or tmp == arg or *tmp != '\0'){
 
2425
          argp_error(state, "Bad delay");
 
2426
        }
 
2427
      case 132:                 /* --retry */
 
2428
        errno = 0;
 
2429
        retry_interval = strtod(arg, &tmp);
 
2430
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
2431
           or (retry_interval * 1000) > INT_MAX
 
2432
           or retry_interval < 0){
 
2433
          argp_error(state, "Bad retry interval");
 
2434
        }
 
2435
        break;
 
2436
      case 133:                 /* --network-hook-dir */
 
2437
        hookdir = arg;
 
2438
        break;
 
2439
        /*
 
2440
         * These reproduce what we would get without ARGP_NO_HELP
 
2441
         */
 
2442
      case '?':                 /* --help */
 
2443
        argp_state_help(state, state->out_stream,
 
2444
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
 
2445
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
2446
      case -3:                  /* --usage */
 
2447
        argp_state_help(state, state->out_stream,
 
2448
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
2449
      case 'V':                 /* --version */
 
2450
        fprintf_plus(state->out_stream, "%s\n", argp_program_version);
 
2451
        exit(argp_err_exit_status);
 
2452
        break;
 
2453
      default:
 
2454
        return ARGP_ERR_UNKNOWN;
 
2455
      }
 
2456
      return errno;
 
2457
    }
 
2458
    
 
2459
    struct argp argp = { .options = options, .parser = parse_opt,
 
2460
                         .args_doc = "",
 
2461
                         .doc = "Mandos client -- Get and decrypt"
 
2462
                         " passwords from a Mandos server" };
 
2463
    ret = argp_parse(&argp, argc, argv,
 
2464
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2465
    switch(ret){
 
2466
    case 0:
 
2467
      break;
 
2468
    case ENOMEM:
 
2469
    default:
 
2470
      errno = ret;
 
2471
      perror_plus("argp_parse");
 
2472
      exitcode = EX_OSERR;
 
2473
      goto end;
 
2474
    case EINVAL:
 
2475
      exitcode = EX_USAGE;
 
2476
      goto end;
 
2477
    }
 
2478
  }
 
2479
  
 
2480
  {
 
2481
    /* Work around Debian bug #633582:
 
2482
       <http://bugs.debian.org/633582> */
 
2483
    
 
2484
    /* Re-raise privileges */
 
2485
    ret_errno = raise_privileges();
 
2486
    if(ret_errno != 0){
 
2487
      errno = ret_errno;
 
2488
      perror_plus("Failed to raise privileges");
 
2489
    } else {
 
2490
      struct stat st;
 
2491
      
 
2492
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
 
2493
        int seckey_fd = open(seckey, O_RDONLY);
 
2494
        if(seckey_fd == -1){
 
2495
          perror_plus("open");
 
2496
        } else {
 
2497
          ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
 
2498
          if(ret == -1){
 
2499
            perror_plus("fstat");
 
2500
          } else {
 
2501
            if(S_ISREG(st.st_mode)
 
2502
               and st.st_uid == 0 and st.st_gid == 0){
 
2503
              ret = fchown(seckey_fd, uid, gid);
 
2504
              if(ret == -1){
 
2505
                perror_plus("fchown");
 
2506
              }
 
2507
            }
 
2508
          }
 
2509
          close(seckey_fd);
 
2510
        }
 
2511
      }
 
2512
      
 
2513
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
 
2514
        int pubkey_fd = open(pubkey, O_RDONLY);
 
2515
        if(pubkey_fd == -1){
 
2516
          perror_plus("open");
 
2517
        } else {
 
2518
          ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
 
2519
          if(ret == -1){
 
2520
            perror_plus("fstat");
 
2521
          } else {
 
2522
            if(S_ISREG(st.st_mode)
 
2523
               and st.st_uid == 0 and st.st_gid == 0){
 
2524
              ret = fchown(pubkey_fd, uid, gid);
 
2525
              if(ret == -1){
 
2526
                perror_plus("fchown");
 
2527
              }
 
2528
            }
 
2529
          }
 
2530
          close(pubkey_fd);
 
2531
        }
 
2532
      }
 
2533
      
 
2534
      if(dh_params_file != NULL
 
2535
         and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
 
2536
        int dhparams_fd = open(dh_params_file, O_RDONLY);
 
2537
        if(dhparams_fd == -1){
 
2538
          perror_plus("open");
 
2539
        } else {
 
2540
          ret = (int)TEMP_FAILURE_RETRY(fstat(dhparams_fd, &st));
 
2541
          if(ret == -1){
 
2542
            perror_plus("fstat");
 
2543
          } else {
 
2544
            if(S_ISREG(st.st_mode)
 
2545
               and st.st_uid == 0 and st.st_gid == 0){
 
2546
              ret = fchown(dhparams_fd, uid, gid);
 
2547
              if(ret == -1){
 
2548
                perror_plus("fchown");
 
2549
              }
 
2550
            }
 
2551
          }
 
2552
          close(dhparams_fd);
 
2553
        }
 
2554
      }
 
2555
      
 
2556
      /* Lower privileges */
 
2557
      ret_errno = lower_privileges();
 
2558
      if(ret_errno != 0){
 
2559
        errno = ret_errno;
 
2560
        perror_plus("Failed to lower privileges");
 
2561
      }
 
2562
    }
 
2563
  }
 
2564
  
 
2565
  /* Remove invalid interface names (except "none") */
 
2566
  {
 
2567
    char *interface = NULL;
 
2568
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
 
2569
                                 interface))){
 
2570
      if(strcmp(interface, "none") != 0
 
2571
         and if_nametoindex(interface) == 0){
 
2572
        if(interface[0] != '\0'){
 
2573
          fprintf_plus(stderr, "Not using nonexisting interface"
 
2574
                       " \"%s\"\n", interface);
 
2575
        }
 
2576
        argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
 
2577
        interface = NULL;
 
2578
      }
 
2579
    }
 
2580
  }
 
2581
  
 
2582
  /* Run network hooks */
 
2583
  {
 
2584
    if(mc.interfaces != NULL){
 
2585
      interfaces_hooks = malloc(mc.interfaces_size);
 
2586
      if(interfaces_hooks == NULL){
 
2587
        perror_plus("malloc");
 
2588
        goto end;
 
2589
      }
 
2590
      memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
 
2591
      argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
 
2592
    }
 
2593
    run_network_hooks("start", interfaces_hooks != NULL ?
 
2594
                      interfaces_hooks : "", delay);
 
2595
  }
 
2596
  
 
2597
  if(not debug){
 
2598
    avahi_set_log_function(empty_log);
 
2599
  }
 
2600
  
 
2601
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
 
2602
     from the signal handler */
 
2603
  /* Initialize the pseudo-RNG for Avahi */
 
2604
  srand((unsigned int) time(NULL));
 
2605
  simple_poll = avahi_simple_poll_new();
 
2606
  if(simple_poll == NULL){
 
2607
    fprintf_plus(stderr,
 
2608
                 "Avahi: Failed to create simple poll object.\n");
 
2609
    exitcode = EX_UNAVAILABLE;
 
2610
    goto end;
 
2611
  }
 
2612
  
 
2613
  sigemptyset(&sigterm_action.sa_mask);
 
2614
  ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
 
2615
  if(ret == -1){
 
2616
    perror_plus("sigaddset");
 
2617
    exitcode = EX_OSERR;
 
2618
    goto end;
 
2619
  }
 
2620
  ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
 
2621
  if(ret == -1){
 
2622
    perror_plus("sigaddset");
 
2623
    exitcode = EX_OSERR;
 
2624
    goto end;
 
2625
  }
 
2626
  ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
 
2627
  if(ret == -1){
 
2628
    perror_plus("sigaddset");
 
2629
    exitcode = EX_OSERR;
 
2630
    goto end;
 
2631
  }
 
2632
  /* Need to check if the handler is SIG_IGN before handling:
 
2633
     | [[info:libc:Initial Signal Actions]] |
 
2634
     | [[info:libc:Basic Signal Handling]]  |
 
2635
  */
 
2636
  ret = sigaction(SIGINT, NULL, &old_sigterm_action);
 
2637
  if(ret == -1){
 
2638
    perror_plus("sigaction");
 
2639
    return EX_OSERR;
 
2640
  }
 
2641
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
2642
    ret = sigaction(SIGINT, &sigterm_action, NULL);
 
2643
    if(ret == -1){
 
2644
      perror_plus("sigaction");
 
2645
      exitcode = EX_OSERR;
 
2646
      goto end;
 
2647
    }
 
2648
  }
 
2649
  ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
 
2650
  if(ret == -1){
 
2651
    perror_plus("sigaction");
 
2652
    return EX_OSERR;
 
2653
  }
 
2654
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
2655
    ret = sigaction(SIGHUP, &sigterm_action, NULL);
 
2656
    if(ret == -1){
 
2657
      perror_plus("sigaction");
 
2658
      exitcode = EX_OSERR;
 
2659
      goto end;
 
2660
    }
 
2661
  }
 
2662
  ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
 
2663
  if(ret == -1){
 
2664
    perror_plus("sigaction");
 
2665
    return EX_OSERR;
 
2666
  }
 
2667
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
2668
    ret = sigaction(SIGTERM, &sigterm_action, NULL);
 
2669
    if(ret == -1){
 
2670
      perror_plus("sigaction");
 
2671
      exitcode = EX_OSERR;
 
2672
      goto end;
 
2673
    }
 
2674
  }
 
2675
  
 
2676
  /* If no interfaces were specified, make a list */
 
2677
  if(mc.interfaces == NULL){
 
2678
    struct dirent **direntries = NULL;
 
2679
    /* Look for any good interfaces */
 
2680
    ret = scandir(sys_class_net, &direntries, good_interface,
 
2681
                  alphasort);
 
2682
    if(ret >= 1){
 
2683
      /* Add all found interfaces to interfaces list */
 
2684
      for(int i = 0; i < ret; ++i){
 
2685
        ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
 
2686
                             direntries[i]->d_name);
 
2687
        if(ret_errno != 0){
 
2688
          errno = ret_errno;
 
2689
          perror_plus("argz_add");
 
2690
          free(direntries[i]);
 
2691
          continue;
 
2692
        }
 
2693
        if(debug){
 
2694
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
 
2695
                       direntries[i]->d_name);
 
2696
        }
 
2697
        free(direntries[i]);
 
2698
      }
 
2699
      free(direntries);
 
2700
    } else {
 
2701
      if(ret == 0){
 
2702
        free(direntries);
 
2703
      }
 
2704
      fprintf_plus(stderr, "Could not find a network interface\n");
 
2705
      exitcode = EXIT_FAILURE;
 
2706
      goto end;
 
2707
    }
 
2708
  }
 
2709
  
 
2710
  /* Bring up interfaces which are down, and remove any "none"s */
 
2711
  {
 
2712
    char *interface = NULL;
 
2713
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
 
2714
                                 interface))){
 
2715
      /* If interface name is "none", stop bringing up interfaces.
 
2716
         Also remove all instances of "none" from the list */
 
2717
      if(strcmp(interface, "none") == 0){
 
2718
        argz_delete(&mc.interfaces, &mc.interfaces_size,
 
2719
                    interface);
 
2720
        interface = NULL;
 
2721
        while((interface = argz_next(mc.interfaces,
 
2722
                                     mc.interfaces_size, interface))){
 
2723
          if(strcmp(interface, "none") == 0){
 
2724
            argz_delete(&mc.interfaces, &mc.interfaces_size,
 
2725
                        interface);
 
2726
            interface = NULL;
 
2727
          }
 
2728
        }
 
2729
        break;
 
2730
      }
 
2731
      bool interface_was_up = interface_is_up(interface);
 
2732
      errno = bring_up_interface(interface, delay);
 
2733
      if(not interface_was_up){
 
2734
        if(errno != 0){
 
2735
          fprintf_plus(stderr, "Failed to bring up interface \"%s\":"
 
2736
                       " %s\n", interface, strerror(errno));
 
2737
        } else {
 
2738
          errno = argz_add(&interfaces_to_take_down,
 
2739
                           &interfaces_to_take_down_size,
 
2740
                           interface);
 
2741
          if(errno != 0){
 
2742
            perror_plus("argz_add");
 
2743
          }
 
2744
        }
 
2745
      }
 
2746
    }
 
2747
    if(debug and (interfaces_to_take_down == NULL)){
 
2748
      fprintf_plus(stderr, "No interfaces were brought up\n");
 
2749
    }
 
2750
  }
 
2751
  
 
2752
  /* If we only got one interface, explicitly use only that one */
 
2753
  if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
 
2754
    if(debug){
 
2755
      fprintf_plus(stderr, "Using only interface \"%s\"\n",
 
2756
                   mc.interfaces);
 
2757
    }
 
2758
    if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
 
2759
  }
 
2760
  
 
2761
  if(quit_now){
 
2762
    goto end;
 
2763
  }
 
2764
  
 
2765
  ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
 
2766
  if(ret == -1){
 
2767
    fprintf_plus(stderr, "init_gnutls_global failed\n");
 
2768
    exitcode = EX_UNAVAILABLE;
 
2769
    goto end;
 
2770
  } else {
 
2771
    gnutls_initialized = true;
 
2772
  }
 
2773
  
 
2774
  if(quit_now){
 
2775
    goto end;
 
2776
  }
 
2777
  
 
2778
  /* Try /run/tmp before /tmp */
 
2779
  tempdir = mkdtemp(run_tempdir);
 
2780
  if(tempdir == NULL and errno == ENOENT){
 
2781
      if(debug){
 
2782
        fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
 
2783
                     run_tempdir, old_tempdir);
 
2784
      }
 
2785
      tempdir = mkdtemp(old_tempdir);
 
2786
  }
 
2787
  if(tempdir == NULL){
 
2788
    perror_plus("mkdtemp");
 
2789
    goto end;
 
2790
  }
 
2791
  
 
2792
  if(quit_now){
 
2793
    goto end;
 
2794
  }
 
2795
  
 
2796
  if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
 
2797
    fprintf_plus(stderr, "init_gpgme failed\n");
 
2798
    exitcode = EX_UNAVAILABLE;
 
2799
    goto end;
 
2800
  } else {
 
2801
    gpgme_initialized = true;
 
2802
  }
 
2803
  
 
2804
  if(quit_now){
 
2805
    goto end;
 
2806
  }
 
2807
  
 
2808
  if(connect_to != NULL){
 
2809
    /* Connect directly, do not use Zeroconf */
 
2810
    /* (Mainly meant for debugging) */
 
2811
    char *address = strrchr(connect_to, ':');
 
2812
    
 
2813
    if(address == NULL){
 
2814
      fprintf_plus(stderr, "No colon in address\n");
 
2815
      exitcode = EX_USAGE;
 
2816
      goto end;
 
2817
    }
 
2818
    
 
2819
    if(quit_now){
 
2820
      goto end;
 
2821
    }
 
2822
    
 
2823
    in_port_t port;
 
2824
    errno = 0;
 
2825
    tmpmax = strtoimax(address+1, &tmp, 10);
 
2826
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
 
2827
       or tmpmax != (in_port_t)tmpmax){
 
2828
      fprintf_plus(stderr, "Bad port number\n");
 
2829
      exitcode = EX_USAGE;
 
2830
      goto end;
 
2831
    }
 
2832
    
 
2833
    if(quit_now){
 
2834
      goto end;
 
2835
    }
 
2836
    
 
2837
    port = (in_port_t)tmpmax;
 
2838
    *address = '\0';
 
2839
    /* Colon in address indicates IPv6 */
 
2840
    int af;
 
2841
    if(strchr(connect_to, ':') != NULL){
 
2842
      af = AF_INET6;
 
2843
      /* Accept [] around IPv6 address - see RFC 5952 */
 
2844
      if(connect_to[0] == '[' and address[-1] == ']')
 
2845
        {
 
2846
          connect_to++;
 
2847
          address[-1] = '\0';
 
2848
        }
 
2849
    } else {
 
2850
      af = AF_INET;
 
2851
    }
 
2852
    address = connect_to;
 
2853
    
 
2854
    if(quit_now){
 
2855
      goto end;
 
2856
    }
 
2857
    
 
2858
    while(not quit_now){
 
2859
      ret = start_mandos_communication(address, port, if_index, af,
 
2860
                                       &mc);
 
2861
      if(quit_now or ret == 0){
 
2862
        break;
 
2863
      }
 
2864
      if(debug){
 
2865
        fprintf_plus(stderr, "Retrying in %d seconds\n",
 
2866
                     (int)retry_interval);
 
2867
      }
 
2868
      sleep((unsigned int)retry_interval);
 
2869
    }
 
2870
    
 
2871
    if(not quit_now){
 
2872
      exitcode = EXIT_SUCCESS;
 
2873
    }
 
2874
    
 
2875
    goto end;
 
2876
  }
 
2877
  
 
2878
  if(quit_now){
 
2879
    goto end;
 
2880
  }
 
2881
  
 
2882
  {
 
2883
    AvahiServerConfig config;
 
2884
    /* Do not publish any local Zeroconf records */
 
2885
    avahi_server_config_init(&config);
 
2886
    config.publish_hinfo = 0;
 
2887
    config.publish_addresses = 0;
 
2888
    config.publish_workstation = 0;
 
2889
    config.publish_domain = 0;
 
2890
    
 
2891
    /* Allocate a new server */
 
2892
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
 
2893
                                 &config, NULL, NULL, &ret_errno);
 
2894
    
 
2895
    /* Free the Avahi configuration data */
 
2896
    avahi_server_config_free(&config);
 
2897
  }
 
2898
  
 
2899
  /* Check if creating the Avahi server object succeeded */
 
2900
  if(mc.server == NULL){
 
2901
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
 
2902
                 avahi_strerror(ret_errno));
 
2903
    exitcode = EX_UNAVAILABLE;
 
2904
    goto end;
 
2905
  }
 
2906
  
 
2907
  if(quit_now){
 
2908
    goto end;
 
2909
  }
 
2910
  
 
2911
  /* Create the Avahi service browser */
 
2912
  sb = avahi_s_service_browser_new(mc.server, if_index,
 
2913
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
 
2914
                                   NULL, 0, browse_callback,
 
2915
                                   (void *)&mc);
 
2916
  if(sb == NULL){
 
2917
    fprintf_plus(stderr, "Failed to create service browser: %s\n",
 
2918
                 avahi_strerror(avahi_server_errno(mc.server)));
 
2919
    exitcode = EX_UNAVAILABLE;
 
2920
    goto end;
 
2921
  }
 
2922
  
 
2923
  if(quit_now){
 
2924
    goto end;
 
2925
  }
 
2926
  
 
2927
  /* Run the main loop */
 
2928
  
 
2929
  if(debug){
 
2930
    fprintf_plus(stderr, "Starting Avahi loop search\n");
 
2931
  }
 
2932
  
 
2933
  ret = avahi_loop_with_timeout(simple_poll,
 
2934
                                (int)(retry_interval * 1000), &mc);
 
2935
  if(debug){
 
2936
    fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
 
2937
                 (ret == 0) ? "successfully" : "with error");
 
2938
  }
 
2939
  
 
2940
 end:
 
2941
  
 
2942
  if(debug){
 
2943
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
2944
  }
 
2945
  
 
2946
  /* Cleanup things */
 
2947
  free(mc.interfaces);
 
2948
  
 
2949
  if(sb != NULL)
 
2950
    avahi_s_service_browser_free(sb);
 
2951
  
 
2952
  if(mc.server != NULL)
 
2953
    avahi_server_free(mc.server);
 
2954
  
 
2955
  if(simple_poll != NULL)
 
2956
    avahi_simple_poll_free(simple_poll);
 
2957
  
 
2958
  if(gnutls_initialized){
 
2959
    gnutls_certificate_free_credentials(mc.cred);
 
2960
    gnutls_dh_params_deinit(mc.dh_params);
 
2961
  }
 
2962
  
 
2963
  if(gpgme_initialized){
 
2964
    gpgme_release(mc.ctx);
 
2965
  }
 
2966
  
 
2967
  /* Cleans up the circular linked list of Mandos servers the client
 
2968
     has seen */
 
2969
  if(mc.current_server != NULL){
 
2970
    mc.current_server->prev->next = NULL;
 
2971
    while(mc.current_server != NULL){
 
2972
      server *next = mc.current_server->next;
 
2973
#ifdef __GNUC__
 
2974
#pragma GCC diagnostic push
 
2975
#pragma GCC diagnostic ignored "-Wcast-qual"
 
2976
#endif
 
2977
      free((char *)(mc.current_server->ip));
 
2978
#ifdef __GNUC__
 
2979
#pragma GCC diagnostic pop
 
2980
#endif
 
2981
      free(mc.current_server);
 
2982
      mc.current_server = next;
 
2983
    }
 
2984
  }
 
2985
  
 
2986
  /* Re-raise privileges */
 
2987
  {
 
2988
    ret_errno = raise_privileges();
 
2989
    if(ret_errno != 0){
 
2990
      errno = ret_errno;
 
2991
      perror_plus("Failed to raise privileges");
 
2992
    } else {
 
2993
      
 
2994
      /* Run network hooks */
 
2995
      run_network_hooks("stop", interfaces_hooks != NULL ?
 
2996
                        interfaces_hooks : "", delay);
 
2997
      
 
2998
      /* Take down the network interfaces which were brought up */
 
2999
      {
 
3000
        char *interface = NULL;
 
3001
        while((interface=argz_next(interfaces_to_take_down,
 
3002
                                   interfaces_to_take_down_size,
 
3003
                                   interface))){
 
3004
          ret_errno = take_down_interface(interface);
 
3005
          if(ret_errno != 0){
 
3006
            errno = ret_errno;
 
3007
            perror_plus("Failed to take down interface");
 
3008
          }
 
3009
        }
 
3010
        if(debug and (interfaces_to_take_down == NULL)){
 
3011
          fprintf_plus(stderr, "No interfaces needed to be taken"
 
3012
                       " down\n");
 
3013
        }
 
3014
      }
 
3015
    }
 
3016
    
 
3017
    ret_errno = lower_privileges_permanently();
 
3018
    if(ret_errno != 0){
 
3019
      errno = ret_errno;
 
3020
      perror_plus("Failed to lower privileges permanently");
 
3021
    }
 
3022
  }
 
3023
  
 
3024
  free(interfaces_to_take_down);
 
3025
  free(interfaces_hooks);
 
3026
  
 
3027
  /* Removes the GPGME temp directory and all files inside */
 
3028
  if(tempdir != NULL){
 
3029
    struct dirent **direntries = NULL;
 
3030
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
 
3031
                                                  | O_NOFOLLOW
 
3032
                                                  | O_DIRECTORY
 
3033
                                                  | O_PATH));
 
3034
    if(tempdir_fd == -1){
 
3035
      perror_plus("open");
 
3036
    } else {
 
3037
      int numentries = scandirat(tempdir_fd, ".", &direntries,
 
3038
                                 notdotentries, alphasort);
 
3039
      if(numentries >= 0){
 
3040
        for(int i = 0; i < numentries; i++){
 
3041
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
 
3042
          if(ret == -1){
 
3043
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
 
3044
                         " \"%s\", 0): %s\n", tempdir,
 
3045
                         direntries[i]->d_name, strerror(errno));
 
3046
          }
 
3047
          free(direntries[i]);
 
3048
        }
 
3049
        
 
3050
        /* need to clean even if 0 because man page doesn't specify */
 
3051
        free(direntries);
 
3052
        if(numentries == -1){
 
3053
          perror_plus("scandir");
 
3054
        }
 
3055
        ret = rmdir(tempdir);
 
3056
        if(ret == -1 and errno != ENOENT){
 
3057
          perror_plus("rmdir");
 
3058
        }
 
3059
      }
 
3060
      close(tempdir_fd);
 
3061
    }
 
3062
  }
 
3063
  
 
3064
  if(quit_now){
 
3065
    sigemptyset(&old_sigterm_action.sa_mask);
 
3066
    old_sigterm_action.sa_handler = SIG_DFL;
 
3067
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
 
3068
                                            &old_sigterm_action,
 
3069
                                            NULL));
 
3070
    if(ret == -1){
 
3071
      perror_plus("sigaction");
 
3072
    }
 
3073
    do {
 
3074
      ret = raise(signal_received);
 
3075
    } while(ret != 0 and errno == EINTR);
 
3076
    if(ret != 0){
 
3077
      perror_plus("raise");
 
3078
      abort();
 
3079
    }
 
3080
    TEMP_FAILURE_RETRY(pause());
 
3081
  }
 
3082
  
 
3083
  return exitcode;
 
3084
}