/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: teddy at bsnet
  • Date: 2011-07-31 13:05:34 UTC
  • Revision ID: teddy@fukt.bsnet.se-20110731130534-v4d9hsvek80ms9uo
* plugins.d/mandos-client.c (avahi_loop_with_timeout): Fix warning.
  (main): Disallow "--retry" arguments < 0.  Allow brackets [] around
          IPv6 addresses, as recommended by RFC 5952.  Bug fix: When
          using --connect, really use retry_interval, not 1 second.
* plugins.d/mandos-client.xml (DESCRIPTION): Add retry info.
  (--retry): Remove repeated word.

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