/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Björn Påhlsson
  • Date: 2011-11-14 19:10:50 UTC
  • mto: (505.3.9 client-network-hooks)
  • mto: This revision was merged to the branch mainline in revision 525.
  • Revision ID: belorn@fukt.bsnet.se-20111114191050-g11po6084bl9j5kf
replace calls to fprintf with fprintf_plus.

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