/mandos/release

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

« back to all changes in this revision

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

* plugins.d/mandos-client.c (quit_now): Move up declaration before
                                        first use.
  (resolve_callback, ): Check "quit_now" flag.
  (main): Renamed "interface_taken_up" to "take_down_interface" for
          clarity.  Check existence of interface before taking it up.
          Set "take_down_interface" and "tempdir_created" flags before
          any action is taken, to make sure cleanup is never missed.
          Check "quit_now" flag throughout.

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,2009 Teddy Hogeborn
 
13
 * Copyright © 2008,2009 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, EXIT_FAILURE,
 
47
                                   srand(), strtof() */
 
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
#include <time.h>               /* nanosleep(), time() */
 
67
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
 
68
                                   SIOCSIFFLAGS, if_indextoname(),
 
69
                                   if_nametoindex(), IF_NAMESIZE */
 
70
#include <netinet/in.h>         /* IN6_IS_ADDR_LINKLOCAL,
 
71
                                   INET_ADDRSTRLEN, INET6_ADDRSTRLEN
 
72
                                */
 
73
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
 
74
                                   getuid(), getgid(), setuid(),
 
75
                                   setgid() */
 
76
#include <arpa/inet.h>          /* inet_pton(), htons */
 
77
#include <iso646.h>             /* not, or, and */
 
78
#include <argp.h>               /* struct argp_option, error_t, struct
 
79
                                   argp_state, struct argp,
 
80
                                   argp_parse(), ARGP_KEY_ARG,
 
81
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
 
82
#include <signal.h>             /* sigemptyset(), sigaddset(),
 
83
                                   sigaction(), SIGTERM, sigaction,
 
84
                                   sig_atomic_t */
 
85
 
 
86
#ifdef __linux__
 
87
#include <sys/klog.h>           /* klogctl() */
 
88
#endif  /* __linux__ */
 
89
 
 
90
/* Avahi */
 
91
/* All Avahi types, constants and functions
 
92
 Avahi*, avahi_*,
 
93
 AVAHI_* */
 
94
#include <avahi-core/core.h>
 
95
#include <avahi-core/lookup.h>
 
96
#include <avahi-core/log.h>
 
97
#include <avahi-common/simple-watch.h>
 
98
#include <avahi-common/malloc.h>
 
99
#include <avahi-common/error.h>
 
100
 
 
101
/* GnuTLS */
 
102
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
 
103
                                   functions:
 
104
                                   gnutls_*
 
105
                                   init_gnutls_session(),
 
106
                                   GNUTLS_* */
 
107
#include <gnutls/openpgp.h>
 
108
                          /* gnutls_certificate_set_openpgp_key_file(),
 
109
                                   GNUTLS_OPENPGP_FMT_BASE64 */
 
110
 
 
111
/* GPGME */
 
112
#include <gpgme.h>              /* All GPGME types, constants and
 
113
                                   functions:
 
114
                                   gpgme_*
 
115
                                   GPGME_PROTOCOL_OpenPGP,
 
116
                                   GPG_ERR_NO_* */
 
117
 
 
118
#define BUFFER_SIZE 256
 
119
 
 
120
#define PATHDIR "/conf/conf.d/mandos"
 
121
#define SECKEY "seckey.txt"
 
122
#define PUBKEY "pubkey.txt"
 
123
 
 
124
bool debug = false;
 
125
static const char mandos_protocol_version[] = "1";
 
126
const char *argp_program_version = "mandos-client " VERSION;
 
127
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
128
 
 
129
/* Used for passing in values through the Avahi callback functions */
 
130
typedef struct {
 
131
  AvahiSimplePoll *simple_poll;
 
132
  AvahiServer *server;
 
133
  gnutls_certificate_credentials_t cred;
 
134
  unsigned int dh_bits;
 
135
  gnutls_dh_params_t dh_params;
 
136
  const char *priority;
 
137
  gpgme_ctx_t ctx;
 
138
} mandos_context;
 
139
 
 
140
/* global context so signal handler can reach it*/
 
141
mandos_context mc = { .simple_poll = NULL, .server = NULL,
 
142
                      .dh_bits = 1024, .priority = "SECURE256"
 
143
                      ":!CTYPE-X.509:+CTYPE-OPENPGP" };
 
144
 
 
145
/*
 
146
 * Make additional room in "buffer" for at least BUFFER_SIZE more
 
147
 * bytes. "buffer_capacity" is how much is currently allocated,
 
148
 * "buffer_length" is how much is already used.
 
149
 */
 
150
size_t incbuffer(char **buffer, size_t buffer_length,
 
151
                  size_t buffer_capacity){
 
152
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
 
153
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
154
    if(buffer == NULL){
 
155
      return 0;
 
156
    }
 
157
    buffer_capacity += BUFFER_SIZE;
 
158
  }
 
159
  return buffer_capacity;
 
160
}
 
161
 
 
162
/* 
 
163
 * Initialize GPGME.
 
164
 */
 
165
static bool init_gpgme(const char *seckey,
 
166
                       const char *pubkey, const char *tempdir){
 
167
  int ret;
 
168
  gpgme_error_t rc;
 
169
  gpgme_engine_info_t engine_info;
 
170
  
 
171
  
 
172
  /*
 
173
   * Helper function to insert pub and seckey to the engine keyring.
 
174
   */
 
175
  bool import_key(const char *filename){
 
176
    int fd;
 
177
    gpgme_data_t pgp_data;
 
178
    
 
179
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
 
180
    if(fd == -1){
 
181
      perror("open");
 
182
      return false;
 
183
    }
 
184
    
 
185
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
 
186
    if(rc != GPG_ERR_NO_ERROR){
 
187
      fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
 
188
              gpgme_strsource(rc), gpgme_strerror(rc));
 
189
      return false;
 
190
    }
 
191
    
 
192
    rc = gpgme_op_import(mc.ctx, pgp_data);
 
193
    if(rc != GPG_ERR_NO_ERROR){
 
194
      fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
 
195
              gpgme_strsource(rc), gpgme_strerror(rc));
 
196
      return false;
 
197
    }
 
198
    
 
199
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
200
    if(ret == -1){
 
201
      perror("close");
 
202
    }
 
203
    gpgme_data_release(pgp_data);
 
204
    return true;
 
205
  }
 
206
  
 
207
  if(debug){
 
208
    fprintf(stderr, "Initializing GPGME\n");
 
209
  }
 
210
  
 
211
  /* Init GPGME */
 
212
  gpgme_check_version(NULL);
 
213
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
214
  if(rc != GPG_ERR_NO_ERROR){
 
215
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
 
216
            gpgme_strsource(rc), gpgme_strerror(rc));
 
217
    return false;
 
218
  }
 
219
  
 
220
    /* Set GPGME home directory for the OpenPGP engine only */
 
221
  rc = gpgme_get_engine_info(&engine_info);
 
222
  if(rc != GPG_ERR_NO_ERROR){
 
223
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
 
224
            gpgme_strsource(rc), gpgme_strerror(rc));
 
225
    return false;
 
226
  }
 
227
  while(engine_info != NULL){
 
228
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
 
229
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
 
230
                            engine_info->file_name, tempdir);
 
231
      break;
 
232
    }
 
233
    engine_info = engine_info->next;
 
234
  }
 
235
  if(engine_info == NULL){
 
236
    fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
 
237
    return false;
 
238
  }
 
239
  
 
240
  /* Create new GPGME "context" */
 
241
  rc = gpgme_new(&(mc.ctx));
 
242
  if(rc != GPG_ERR_NO_ERROR){
 
243
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
 
244
            gpgme_strsource(rc), gpgme_strerror(rc));
 
245
    return false;
 
246
  }
 
247
  
 
248
  if(not import_key(pubkey) or not import_key(seckey)){
 
249
    return false;
 
250
  }
 
251
  
 
252
  return true; 
 
253
}
 
254
 
 
255
/* 
 
256
 * Decrypt OpenPGP data.
 
257
 * Returns -1 on error
 
258
 */
 
259
static ssize_t pgp_packet_decrypt(const char *cryptotext,
 
260
                                  size_t crypto_size,
 
261
                                  char **plaintext){
 
262
  gpgme_data_t dh_crypto, dh_plain;
 
263
  gpgme_error_t rc;
 
264
  ssize_t ret;
 
265
  size_t plaintext_capacity = 0;
 
266
  ssize_t plaintext_length = 0;
 
267
  
 
268
  if(debug){
 
269
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
 
270
  }
 
271
  
 
272
  /* Create new GPGME data buffer from memory cryptotext */
 
273
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
 
274
                               0);
 
275
  if(rc != GPG_ERR_NO_ERROR){
 
276
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
 
277
            gpgme_strsource(rc), gpgme_strerror(rc));
 
278
    return -1;
 
279
  }
 
280
  
 
281
  /* Create new empty GPGME data buffer for the plaintext */
 
282
  rc = gpgme_data_new(&dh_plain);
 
283
  if(rc != GPG_ERR_NO_ERROR){
 
284
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
 
285
            gpgme_strsource(rc), gpgme_strerror(rc));
 
286
    gpgme_data_release(dh_crypto);
 
287
    return -1;
 
288
  }
 
289
  
 
290
  /* Decrypt data from the cryptotext data buffer to the plaintext
 
291
     data buffer */
 
292
  rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
 
293
  if(rc != GPG_ERR_NO_ERROR){
 
294
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
 
295
            gpgme_strsource(rc), gpgme_strerror(rc));
 
296
    plaintext_length = -1;
 
297
    if(debug){
 
298
      gpgme_decrypt_result_t result;
 
299
      result = gpgme_op_decrypt_result(mc.ctx);
 
300
      if(result == NULL){
 
301
        fprintf(stderr, "gpgme_op_decrypt_result failed\n");
 
302
      } else {
 
303
        fprintf(stderr, "Unsupported algorithm: %s\n",
 
304
                result->unsupported_algorithm);
 
305
        fprintf(stderr, "Wrong key usage: %u\n",
 
306
                result->wrong_key_usage);
 
307
        if(result->file_name != NULL){
 
308
          fprintf(stderr, "File name: %s\n", result->file_name);
 
309
        }
 
310
        gpgme_recipient_t recipient;
 
311
        recipient = result->recipients;
 
312
        while(recipient != NULL){
 
313
          fprintf(stderr, "Public key algorithm: %s\n",
 
314
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
 
315
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
 
316
          fprintf(stderr, "Secret key available: %s\n",
 
317
                  recipient->status == GPG_ERR_NO_SECKEY
 
318
                  ? "No" : "Yes");
 
319
          recipient = recipient->next;
 
320
        }
 
321
      }
 
322
    }
 
323
    goto decrypt_end;
 
324
  }
 
325
  
 
326
  if(debug){
 
327
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
 
328
  }
 
329
  
 
330
  /* Seek back to the beginning of the GPGME plaintext data buffer */
 
331
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
 
332
    perror("gpgme_data_seek");
 
333
    plaintext_length = -1;
 
334
    goto decrypt_end;
 
335
  }
 
336
  
 
337
  *plaintext = NULL;
 
338
  while(true){
 
339
    plaintext_capacity = incbuffer(plaintext,
 
340
                                      (size_t)plaintext_length,
 
341
                                      plaintext_capacity);
 
342
    if(plaintext_capacity == 0){
 
343
        perror("incbuffer");
 
344
        plaintext_length = -1;
 
345
        goto decrypt_end;
 
346
    }
 
347
    
 
348
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
 
349
                          BUFFER_SIZE);
 
350
    /* Print the data, if any */
 
351
    if(ret == 0){
 
352
      /* EOF */
 
353
      break;
 
354
    }
 
355
    if(ret < 0){
 
356
      perror("gpgme_data_read");
 
357
      plaintext_length = -1;
 
358
      goto decrypt_end;
 
359
    }
 
360
    plaintext_length += ret;
 
361
  }
 
362
  
 
363
  if(debug){
 
364
    fprintf(stderr, "Decrypted password is: ");
 
365
    for(ssize_t i = 0; i < plaintext_length; i++){
 
366
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
 
367
    }
 
368
    fprintf(stderr, "\n");
 
369
  }
 
370
  
 
371
 decrypt_end:
 
372
  
 
373
  /* Delete the GPGME cryptotext data buffer */
 
374
  gpgme_data_release(dh_crypto);
 
375
  
 
376
  /* Delete the GPGME plaintext data buffer */
 
377
  gpgme_data_release(dh_plain);
 
378
  return plaintext_length;
 
379
}
 
380
 
 
381
static const char * safer_gnutls_strerror(int value){
 
382
  const char *ret = gnutls_strerror(value); /* Spurious warning from
 
383
                                               -Wunreachable-code */
 
384
  if(ret == NULL)
 
385
    ret = "(unknown)";
 
386
  return ret;
 
387
}
 
388
 
 
389
/* GnuTLS log function callback */
 
390
static void debuggnutls(__attribute__((unused)) int level,
 
391
                        const char* string){
 
392
  fprintf(stderr, "GnuTLS: %s", string);
 
393
}
 
394
 
 
395
static int init_gnutls_global(const char *pubkeyfilename,
 
396
                              const char *seckeyfilename){
 
397
  int ret;
 
398
  
 
399
  if(debug){
 
400
    fprintf(stderr, "Initializing GnuTLS\n");
 
401
  }
 
402
  
 
403
  ret = gnutls_global_init();
 
404
  if(ret != GNUTLS_E_SUCCESS){
 
405
    fprintf(stderr, "GnuTLS global_init: %s\n",
 
406
            safer_gnutls_strerror(ret));
 
407
    return -1;
 
408
  }
 
409
  
 
410
  if(debug){
 
411
    /* "Use a log level over 10 to enable all debugging options."
 
412
     * - GnuTLS manual
 
413
     */
 
414
    gnutls_global_set_log_level(11);
 
415
    gnutls_global_set_log_function(debuggnutls);
 
416
  }
 
417
  
 
418
  /* OpenPGP credentials */
 
419
  gnutls_certificate_allocate_credentials(&mc.cred);
 
420
  if(ret != GNUTLS_E_SUCCESS){
 
421
    fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
 
422
                                                    from
 
423
                                                    -Wunreachable-code
 
424
                                                 */
 
425
            safer_gnutls_strerror(ret));
 
426
    gnutls_global_deinit();
 
427
    return -1;
 
428
  }
 
429
  
 
430
  if(debug){
 
431
    fprintf(stderr, "Attempting to use OpenPGP public key %s and"
 
432
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
 
433
            seckeyfilename);
 
434
  }
 
435
  
 
436
  ret = gnutls_certificate_set_openpgp_key_file
 
437
    (mc.cred, pubkeyfilename, seckeyfilename,
 
438
     GNUTLS_OPENPGP_FMT_BASE64);
 
439
  if(ret != GNUTLS_E_SUCCESS){
 
440
    fprintf(stderr,
 
441
            "Error[%d] while reading the OpenPGP key pair ('%s',"
 
442
            " '%s')\n", ret, pubkeyfilename, seckeyfilename);
 
443
    fprintf(stderr, "The GnuTLS error is: %s\n",
 
444
            safer_gnutls_strerror(ret));
 
445
    goto globalfail;
 
446
  }
 
447
  
 
448
  /* GnuTLS server initialization */
 
449
  ret = gnutls_dh_params_init(&mc.dh_params);
 
450
  if(ret != GNUTLS_E_SUCCESS){
 
451
    fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
 
452
            " %s\n", safer_gnutls_strerror(ret));
 
453
    goto globalfail;
 
454
  }
 
455
  ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
 
456
  if(ret != GNUTLS_E_SUCCESS){
 
457
    fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
 
458
            safer_gnutls_strerror(ret));
 
459
    goto globalfail;
 
460
  }
 
461
  
 
462
  gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
 
463
  
 
464
  return 0;
 
465
  
 
466
 globalfail:
 
467
  
 
468
  gnutls_certificate_free_credentials(mc.cred);
 
469
  gnutls_global_deinit();
 
470
  gnutls_dh_params_deinit(mc.dh_params);
 
471
  return -1;
 
472
}
 
473
 
 
474
static int init_gnutls_session(gnutls_session_t *session){
 
475
  int ret;
 
476
  /* GnuTLS session creation */
 
477
  ret = gnutls_init(session, GNUTLS_SERVER);
 
478
  if(ret != GNUTLS_E_SUCCESS){
 
479
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
 
480
            safer_gnutls_strerror(ret));
 
481
  }
 
482
  
 
483
  {
 
484
    const char *err;
 
485
    ret = gnutls_priority_set_direct(*session, mc.priority, &err);
 
486
    if(ret != GNUTLS_E_SUCCESS){
 
487
      fprintf(stderr, "Syntax error at: %s\n", err);
 
488
      fprintf(stderr, "GnuTLS error: %s\n",
 
489
              safer_gnutls_strerror(ret));
 
490
      gnutls_deinit(*session);
 
491
      return -1;
 
492
    }
 
493
  }
 
494
  
 
495
  ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
496
                               mc.cred);
 
497
  if(ret != GNUTLS_E_SUCCESS){
 
498
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
 
499
            safer_gnutls_strerror(ret));
 
500
    gnutls_deinit(*session);
 
501
    return -1;
 
502
  }
 
503
  
 
504
  /* ignore client certificate if any. */
 
505
  gnutls_certificate_server_set_request(*session,
 
506
                                        GNUTLS_CERT_IGNORE);
 
507
  
 
508
  gnutls_dh_set_prime_bits(*session, mc.dh_bits);
 
509
  
 
510
  return 0;
 
511
}
 
512
 
 
513
/* Avahi log function callback */
 
514
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
515
                      __attribute__((unused)) const char *txt){}
 
516
 
 
517
/* Called when a Mandos server is found */
 
518
static int start_mandos_communication(const char *ip, uint16_t port,
 
519
                                      AvahiIfIndex if_index,
 
520
                                      int af){
 
521
  int ret, tcp_sd;
 
522
  ssize_t sret;
 
523
  union {
 
524
    struct sockaddr_in in;
 
525
    struct sockaddr_in6 in6;
 
526
  } to;
 
527
  char *buffer = NULL;
 
528
  char *decrypted_buffer;
 
529
  size_t buffer_length = 0;
 
530
  size_t buffer_capacity = 0;
 
531
  ssize_t decrypted_buffer_size;
 
532
  size_t written;
 
533
  int retval = 0;
 
534
  gnutls_session_t session;
 
535
  int pf;                       /* Protocol family */
 
536
  
 
537
  switch(af){
 
538
  case AF_INET6:
 
539
    pf = PF_INET6;
 
540
    break;
 
541
  case AF_INET:
 
542
    pf = PF_INET;
 
543
    break;
 
544
  default:
 
545
    fprintf(stderr, "Bad address family: %d\n", af);
 
546
    return -1;
 
547
  }
 
548
  
 
549
  ret = init_gnutls_session(&session);
 
550
  if(ret != 0){
 
551
    return -1;
 
552
  }
 
553
  
 
554
  if(debug){
 
555
    fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
 
556
            "\n", ip, port);
 
557
  }
 
558
  
 
559
  tcp_sd = socket(pf, SOCK_STREAM, 0);
 
560
  if(tcp_sd < 0){
 
561
    perror("socket");
 
562
    return -1;
 
563
  }
 
564
  
 
565
  memset(&to, 0, sizeof(to));
 
566
  if(af == AF_INET6){
 
567
    to.in6.sin6_family = (sa_family_t)af;
 
568
    ret = inet_pton(af, ip, &to.in6.sin6_addr);
 
569
  } else {                      /* IPv4 */
 
570
    to.in.sin_family = (sa_family_t)af;
 
571
    ret = inet_pton(af, ip, &to.in.sin_addr);
 
572
  }
 
573
  if(ret < 0 ){
 
574
    perror("inet_pton");
 
575
    return -1;
 
576
  }
 
577
  if(ret == 0){
 
578
    fprintf(stderr, "Bad address: %s\n", ip);
 
579
    return -1;
 
580
  }
 
581
  if(af == AF_INET6){
 
582
    to.in6.sin6_port = htons(port); /* Spurious warnings from
 
583
                                       -Wconversion and
 
584
                                       -Wunreachable-code */
 
585
    
 
586
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
 
587
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
 
588
                              -Wunreachable-code*/
 
589
      if(if_index == AVAHI_IF_UNSPEC){
 
590
        fprintf(stderr, "An IPv6 link-local address is incomplete"
 
591
                " without a network interface\n");
 
592
        return -1;
 
593
      }
 
594
      /* Set the network interface number as scope */
 
595
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
596
    }
 
597
  } else {
 
598
    to.in.sin_port = htons(port); /* Spurious warnings from
 
599
                                     -Wconversion and
 
600
                                     -Wunreachable-code */
 
601
  }
 
602
  
 
603
  if(debug){
 
604
    if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
 
605
      char interface[IF_NAMESIZE];
 
606
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
607
        perror("if_indextoname");
 
608
      } else {
 
609
        fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
 
610
                ip, interface, port);
 
611
      }
 
612
    } else {
 
613
      fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
 
614
              port);
 
615
    }
 
616
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
 
617
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
 
618
    const char *pcret;
 
619
    if(af == AF_INET6){
 
620
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
 
621
                        sizeof(addrstr));
 
622
    } else {
 
623
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
 
624
                        sizeof(addrstr));
 
625
    }
 
626
    if(pcret == NULL){
 
627
      perror("inet_ntop");
 
628
    } else {
 
629
      if(strcmp(addrstr, ip) != 0){
 
630
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
 
631
      }
 
632
    }
 
633
  }
 
634
  
 
635
  if(af == AF_INET6){
 
636
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
637
  } else {
 
638
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
639
  }
 
640
  if(ret < 0){
 
641
    perror("connect");
 
642
    return -1;
 
643
  }
 
644
  
 
645
  const char *out = mandos_protocol_version;
 
646
  written = 0;
 
647
  while(true){
 
648
    size_t out_size = strlen(out);
 
649
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
650
                                   out_size - written));
 
651
    if(ret == -1){
 
652
      perror("write");
 
653
      retval = -1;
 
654
      goto mandos_end;
 
655
    }
 
656
    written += (size_t)ret;
 
657
    if(written < out_size){
 
658
      continue;
 
659
    } else {
 
660
      if(out == mandos_protocol_version){
 
661
        written = 0;
 
662
        out = "\r\n";
 
663
      } else {
 
664
        break;
 
665
      }
 
666
    }
 
667
  }
 
668
  
 
669
  if(debug){
 
670
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
 
671
  }
 
672
  
 
673
  gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
 
674
  
 
675
  do{
 
676
    ret = gnutls_handshake(session);
 
677
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
678
  
 
679
  if(ret != GNUTLS_E_SUCCESS){
 
680
    if(debug){
 
681
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
 
682
      gnutls_perror(ret);
 
683
    }
 
684
    retval = -1;
 
685
    goto mandos_end;
 
686
  }
 
687
  
 
688
  /* Read OpenPGP packet that contains the wanted password */
 
689
  
 
690
  if(debug){
 
691
    fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
 
692
            ip);
 
693
  }
 
694
  
 
695
  while(true){
 
696
    buffer_capacity = incbuffer(&buffer, buffer_length,
 
697
                                   buffer_capacity);
 
698
    if(buffer_capacity == 0){
 
699
      perror("incbuffer");
 
700
      retval = -1;
 
701
      goto mandos_end;
 
702
    }
 
703
    
 
704
    sret = gnutls_record_recv(session, buffer+buffer_length,
 
705
                              BUFFER_SIZE);
 
706
    if(sret == 0){
 
707
      break;
 
708
    }
 
709
    if(sret < 0){
 
710
      switch(sret){
 
711
      case GNUTLS_E_INTERRUPTED:
 
712
      case GNUTLS_E_AGAIN:
 
713
        break;
 
714
      case GNUTLS_E_REHANDSHAKE:
 
715
        do{
 
716
          ret = gnutls_handshake(session);
 
717
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
718
        if(ret < 0){
 
719
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
 
720
          gnutls_perror(ret);
 
721
          retval = -1;
 
722
          goto mandos_end;
 
723
        }
 
724
        break;
 
725
      default:
 
726
        fprintf(stderr, "Unknown error while reading data from"
 
727
                " encrypted session with Mandos server\n");
 
728
        retval = -1;
 
729
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
730
        goto mandos_end;
 
731
      }
 
732
    } else {
 
733
      buffer_length += (size_t) sret;
 
734
    }
 
735
  }
 
736
  
 
737
  if(debug){
 
738
    fprintf(stderr, "Closing TLS session\n");
 
739
  }
 
740
  
 
741
  gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
742
  
 
743
  if(buffer_length > 0){
 
744
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
 
745
                                               buffer_length,
 
746
                                               &decrypted_buffer);
 
747
    if(decrypted_buffer_size >= 0){
 
748
      written = 0;
 
749
      while(written < (size_t) decrypted_buffer_size){
 
750
        ret = (int)fwrite(decrypted_buffer + written, 1,
 
751
                          (size_t)decrypted_buffer_size - written,
 
752
                          stdout);
 
753
        if(ret == 0 and ferror(stdout)){
 
754
          if(debug){
 
755
            fprintf(stderr, "Error writing encrypted data: %s\n",
 
756
                    strerror(errno));
 
757
          }
 
758
          retval = -1;
 
759
          break;
 
760
        }
 
761
        written += (size_t)ret;
 
762
      }
 
763
      free(decrypted_buffer);
 
764
    } else {
 
765
      retval = -1;
 
766
    }
 
767
  } else {
 
768
    retval = -1;
 
769
  }
 
770
  
 
771
  /* Shutdown procedure */
 
772
  
 
773
 mandos_end:
 
774
  free(buffer);
 
775
  ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
776
  if(ret == -1){
 
777
    perror("close");
 
778
  }
 
779
  gnutls_deinit(session);
 
780
  return retval;
 
781
}
 
782
 
 
783
sig_atomic_t quit_now = 0;
 
784
 
 
785
static void resolve_callback(AvahiSServiceResolver *r,
 
786
                             AvahiIfIndex interface,
 
787
                             AvahiProtocol proto,
 
788
                             AvahiResolverEvent event,
 
789
                             const char *name,
 
790
                             const char *type,
 
791
                             const char *domain,
 
792
                             const char *host_name,
 
793
                             const AvahiAddress *address,
 
794
                             uint16_t port,
 
795
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
 
796
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
797
                             flags,
 
798
                             AVAHI_GCC_UNUSED void* userdata){
 
799
  assert(r);
 
800
  
 
801
  /* Called whenever a service has been resolved successfully or
 
802
     timed out */
 
803
  
 
804
  if(quit_now){
 
805
    return;
 
806
  }
 
807
  
 
808
  switch(event){
 
809
  default:
 
810
  case AVAHI_RESOLVER_FAILURE:
 
811
    fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
 
812
            " of type '%s' in domain '%s': %s\n", name, type, domain,
 
813
            avahi_strerror(avahi_server_errno(mc.server)));
 
814
    break;
 
815
    
 
816
  case AVAHI_RESOLVER_FOUND:
 
817
    {
 
818
      char ip[AVAHI_ADDRESS_STR_MAX];
 
819
      avahi_address_snprint(ip, sizeof(ip), address);
 
820
      if(debug){
 
821
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
822
                PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
 
823
                ip, (intmax_t)interface, port);
 
824
      }
 
825
      int ret = start_mandos_communication(ip, port, interface,
 
826
                                           avahi_proto_to_af(proto));
 
827
      if(ret == 0){
 
828
        avahi_simple_poll_quit(mc.simple_poll);
 
829
      }
 
830
    }
 
831
  }
 
832
  avahi_s_service_resolver_free(r);
 
833
}
 
834
 
 
835
static void browse_callback(AvahiSServiceBrowser *b,
 
836
                            AvahiIfIndex interface,
 
837
                            AvahiProtocol protocol,
 
838
                            AvahiBrowserEvent event,
 
839
                            const char *name,
 
840
                            const char *type,
 
841
                            const char *domain,
 
842
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
843
                            flags,
 
844
                            AVAHI_GCC_UNUSED void* userdata){
 
845
  assert(b);
 
846
  
 
847
  /* Called whenever a new services becomes available on the LAN or
 
848
     is removed from the LAN */
 
849
  
 
850
  switch(event){
 
851
  default:
 
852
  case AVAHI_BROWSER_FAILURE:
 
853
    
 
854
    fprintf(stderr, "(Avahi browser) %s\n",
 
855
            avahi_strerror(avahi_server_errno(mc.server)));
 
856
    avahi_simple_poll_quit(mc.simple_poll);
 
857
    return;
 
858
    
 
859
  case AVAHI_BROWSER_NEW:
 
860
    /* We ignore the returned Avahi resolver object. In the callback
 
861
       function we free it. If the Avahi server is terminated before
 
862
       the callback function is called the Avahi server will free the
 
863
       resolver for us. */
 
864
    
 
865
    if(avahi_s_service_resolver_new(mc.server, interface, protocol,
 
866
                                    name, type, domain, protocol, 0,
 
867
                                    resolve_callback, NULL) == NULL)
 
868
      fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
 
869
              name, avahi_strerror(avahi_server_errno(mc.server)));
 
870
    break;
 
871
    
 
872
  case AVAHI_BROWSER_REMOVE:
 
873
    break;
 
874
    
 
875
  case AVAHI_BROWSER_ALL_FOR_NOW:
 
876
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
877
    if(debug){
 
878
      fprintf(stderr, "No Mandos server found, still searching...\n");
 
879
    }
 
880
    break;
 
881
  }
 
882
}
 
883
 
 
884
/* stop main loop after sigterm has been called */
 
885
static void handle_sigterm(__attribute__((unused)) int sig){
 
886
  if(quit_now){
 
887
    return;
 
888
  }
 
889
  quit_now = 1;
 
890
  int old_errno = errno;
 
891
  if(mc.simple_poll != NULL){
 
892
    avahi_simple_poll_quit(mc.simple_poll);
 
893
  }
 
894
  errno = old_errno;
 
895
}
 
896
 
 
897
int main(int argc, char *argv[]){
 
898
  AvahiSServiceBrowser *sb = NULL;
 
899
  int error;
 
900
  int ret;
 
901
  intmax_t tmpmax;
 
902
  char *tmp;
 
903
  int exitcode = EXIT_SUCCESS;
 
904
  const char *interface = "eth0";
 
905
  struct ifreq network;
 
906
  int sd = -1;
 
907
  bool take_down_interface = false;
 
908
  uid_t uid;
 
909
  gid_t gid;
 
910
  char *connect_to = NULL;
 
911
  char tempdir[] = "/tmp/mandosXXXXXX";
 
912
  bool tempdir_created = false;
 
913
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
914
  const char *seckey = PATHDIR "/" SECKEY;
 
915
  const char *pubkey = PATHDIR "/" PUBKEY;
 
916
  
 
917
  bool gnutls_initialized = false;
 
918
  bool gpgme_initialized = false;
 
919
  float delay = 2.5f;
 
920
  
 
921
  struct sigaction old_sigterm_action;
 
922
  struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
 
923
  
 
924
  {
 
925
    struct argp_option options[] = {
 
926
      { .name = "debug", .key = 128,
 
927
        .doc = "Debug mode", .group = 3 },
 
928
      { .name = "connect", .key = 'c',
 
929
        .arg = "ADDRESS:PORT",
 
930
        .doc = "Connect directly to a specific Mandos server",
 
931
        .group = 1 },
 
932
      { .name = "interface", .key = 'i',
 
933
        .arg = "NAME",
 
934
        .doc = "Network interface that will be used to search for"
 
935
        " Mandos servers",
 
936
        .group = 1 },
 
937
      { .name = "seckey", .key = 's',
 
938
        .arg = "FILE",
 
939
        .doc = "OpenPGP secret key file base name",
 
940
        .group = 1 },
 
941
      { .name = "pubkey", .key = 'p',
 
942
        .arg = "FILE",
 
943
        .doc = "OpenPGP public key file base name",
 
944
        .group = 2 },
 
945
      { .name = "dh-bits", .key = 129,
 
946
        .arg = "BITS",
 
947
        .doc = "Bit length of the prime number used in the"
 
948
        " Diffie-Hellman key exchange",
 
949
        .group = 2 },
 
950
      { .name = "priority", .key = 130,
 
951
        .arg = "STRING",
 
952
        .doc = "GnuTLS priority string for the TLS handshake",
 
953
        .group = 1 },
 
954
      { .name = "delay", .key = 131,
 
955
        .arg = "SECONDS",
 
956
        .doc = "Maximum delay to wait for interface startup",
 
957
        .group = 2 },
 
958
      { .name = NULL }
 
959
    };
 
960
    
 
961
    error_t parse_opt(int key, char *arg,
 
962
                      struct argp_state *state){
 
963
      switch(key){
 
964
      case 128:                 /* --debug */
 
965
        debug = true;
 
966
        break;
 
967
      case 'c':                 /* --connect */
 
968
        connect_to = arg;
 
969
        break;
 
970
      case 'i':                 /* --interface */
 
971
        interface = arg;
 
972
        break;
 
973
      case 's':                 /* --seckey */
 
974
        seckey = arg;
 
975
        break;
 
976
      case 'p':                 /* --pubkey */
 
977
        pubkey = arg;
 
978
        break;
 
979
      case 129:                 /* --dh-bits */
 
980
        errno = 0;
 
981
        tmpmax = strtoimax(arg, &tmp, 10);
 
982
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
983
           or tmpmax != (typeof(mc.dh_bits))tmpmax){
 
984
          fprintf(stderr, "Bad number of DH bits\n");
 
985
          exit(EXIT_FAILURE);
 
986
        }
 
987
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
 
988
        break;
 
989
      case 130:                 /* --priority */
 
990
        mc.priority = arg;
 
991
        break;
 
992
      case 131:                 /* --delay */
 
993
        errno = 0;
 
994
        delay = strtof(arg, &tmp);
 
995
        if(errno != 0 or tmp == arg or *tmp != '\0'){
 
996
          fprintf(stderr, "Bad delay\n");
 
997
          exit(EXIT_FAILURE);
 
998
        }
 
999
        break;
 
1000
      case ARGP_KEY_ARG:
 
1001
        argp_usage(state);
 
1002
      case ARGP_KEY_END:
 
1003
        break;
 
1004
      default:
 
1005
        return ARGP_ERR_UNKNOWN;
 
1006
      }
 
1007
      return 0;
 
1008
    }
 
1009
    
 
1010
    struct argp argp = { .options = options, .parser = parse_opt,
 
1011
                         .args_doc = "",
 
1012
                         .doc = "Mandos client -- Get and decrypt"
 
1013
                         " passwords from a Mandos server" };
 
1014
    ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
 
1015
    if(ret == ARGP_ERR_UNKNOWN){
 
1016
      fprintf(stderr, "Unknown error while parsing arguments\n");
 
1017
      exitcode = EXIT_FAILURE;
 
1018
      goto end;
 
1019
    }
 
1020
  }
 
1021
  
 
1022
  if(not debug){
 
1023
    avahi_set_log_function(empty_log);
 
1024
  }
 
1025
  
 
1026
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
 
1027
     from the signal handler */
 
1028
  /* Initialize the pseudo-RNG for Avahi */
 
1029
  srand((unsigned int) time(NULL));
 
1030
  mc.simple_poll = avahi_simple_poll_new();
 
1031
  if(mc.simple_poll == NULL){
 
1032
    fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
 
1033
    exitcode = EXIT_FAILURE;
 
1034
    goto end;
 
1035
  }
 
1036
  
 
1037
  sigemptyset(&sigterm_action.sa_mask);
 
1038
  ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
 
1039
  if(ret == -1){
 
1040
    perror("sigaddset");
 
1041
    exitcode = EXIT_FAILURE;
 
1042
    goto end;
 
1043
  }
 
1044
  ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
 
1045
  if(ret == -1){
 
1046
    perror("sigaddset");
 
1047
    exitcode = EXIT_FAILURE;
 
1048
    goto end;
 
1049
  }
 
1050
  ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
 
1051
  if(ret == -1){
 
1052
    perror("sigaddset");
 
1053
    exitcode = EXIT_FAILURE;
 
1054
    goto end;
 
1055
  }
 
1056
  ret = sigaction(SIGTERM, &sigterm_action, &old_sigterm_action);
 
1057
  if(ret == -1){
 
1058
    perror("sigaction");
 
1059
    exitcode = EXIT_FAILURE;
 
1060
    goto end;
 
1061
  }  
 
1062
  
 
1063
  /* If the interface is down, bring it up */
 
1064
  if(interface[0] != '\0'){
 
1065
    if_index = (AvahiIfIndex) if_nametoindex(interface);
 
1066
    if(if_index == 0){
 
1067
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
 
1068
      exitcode = EXIT_FAILURE;
 
1069
      goto end;
 
1070
    }
 
1071
    
 
1072
    if(quit_now){
 
1073
      goto end;
 
1074
    }
 
1075
    
 
1076
#ifdef __linux__
 
1077
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
1078
       messages to mess up the prompt */
 
1079
    ret = klogctl(8, NULL, 5);
 
1080
    bool restore_loglevel = true;
 
1081
    if(ret == -1){
 
1082
      restore_loglevel = false;
 
1083
      perror("klogctl");
 
1084
    }
 
1085
#endif  /* __linux__ */
 
1086
    
 
1087
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1088
    if(sd < 0){
 
1089
      perror("socket");
 
1090
      exitcode = EXIT_FAILURE;
 
1091
#ifdef __linux__
 
1092
      if(restore_loglevel){
 
1093
        ret = klogctl(7, NULL, 0);
 
1094
        if(ret == -1){
 
1095
          perror("klogctl");
 
1096
        }
 
1097
      }
 
1098
#endif  /* __linux__ */
 
1099
      goto end;
 
1100
    }
 
1101
    strcpy(network.ifr_name, interface);
 
1102
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
1103
    if(ret == -1){
 
1104
      perror("ioctl SIOCGIFFLAGS");
 
1105
#ifdef __linux__
 
1106
      if(restore_loglevel){
 
1107
        ret = klogctl(7, NULL, 0);
 
1108
        if(ret == -1){
 
1109
          perror("klogctl");
 
1110
        }
 
1111
      }
 
1112
#endif  /* __linux__ */
 
1113
      exitcode = EXIT_FAILURE;
 
1114
      goto end;
 
1115
    }
 
1116
    if((network.ifr_flags & IFF_UP) == 0){
 
1117
      network.ifr_flags |= IFF_UP;
 
1118
      take_down_interface = true;
 
1119
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
1120
      if(ret == -1){
 
1121
        take_down_interface = false;
 
1122
        perror("ioctl SIOCSIFFLAGS");
 
1123
        exitcode = EXIT_FAILURE;
 
1124
#ifdef __linux__
 
1125
        if(restore_loglevel){
 
1126
          ret = klogctl(7, NULL, 0);
 
1127
          if(ret == -1){
 
1128
            perror("klogctl");
 
1129
          }
 
1130
        }
 
1131
#endif  /* __linux__ */
 
1132
        goto end;
 
1133
      }
 
1134
    }
 
1135
    /* sleep checking until interface is running */
 
1136
    for(int i=0; i < delay * 4; i++){
 
1137
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
1138
      if(ret == -1){
 
1139
        perror("ioctl SIOCGIFFLAGS");
 
1140
      } else if(network.ifr_flags & IFF_RUNNING){
 
1141
        break;
 
1142
      }
 
1143
      struct timespec sleeptime = { .tv_nsec = 250000000 };
 
1144
      ret = nanosleep(&sleeptime, NULL);
 
1145
      if(ret == -1 and errno != EINTR){
 
1146
        perror("nanosleep");
 
1147
      }
 
1148
    }
 
1149
    if(not take_down_interface){
 
1150
      /* We won't need the socket anymore */
 
1151
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1152
      if(ret == -1){
 
1153
        perror("close");
 
1154
      }
 
1155
    }
 
1156
#ifdef __linux__
 
1157
    if(restore_loglevel){
 
1158
      /* Restores kernel loglevel to default */
 
1159
      ret = klogctl(7, NULL, 0);
 
1160
      if(ret == -1){
 
1161
        perror("klogctl");
 
1162
      }
 
1163
    }
 
1164
#endif  /* __linux__ */
 
1165
  }
 
1166
  
 
1167
  if(quit_now){
 
1168
    goto end;
 
1169
  }
 
1170
  
 
1171
  uid = getuid();
 
1172
  gid = getgid();
 
1173
  
 
1174
  errno = 0;
 
1175
  setgid(gid);
 
1176
  if(ret == -1){
 
1177
    perror("setgid");
 
1178
  }
 
1179
  
 
1180
  ret = setuid(uid);
 
1181
  if(ret == -1){
 
1182
    perror("setuid");
 
1183
  }
 
1184
  
 
1185
  if(quit_now){
 
1186
    goto end;
 
1187
  }
 
1188
  
 
1189
  ret = init_gnutls_global(pubkey, seckey);
 
1190
  if(ret == -1){
 
1191
    fprintf(stderr, "init_gnutls_global failed\n");
 
1192
    exitcode = EXIT_FAILURE;
 
1193
    goto end;
 
1194
  } else {
 
1195
    gnutls_initialized = true;
 
1196
  }
 
1197
  
 
1198
  if(quit_now){
 
1199
    goto end;
 
1200
  }
 
1201
  
 
1202
  tempdir_created = true;
 
1203
  if(mkdtemp(tempdir) == NULL){
 
1204
    tempdir_created = false;
 
1205
    perror("mkdtemp");
 
1206
    goto end;
 
1207
  }
 
1208
  
 
1209
  if(quit_now){
 
1210
    goto end;
 
1211
  }
 
1212
  
 
1213
  if(not init_gpgme(pubkey, seckey, tempdir)){
 
1214
    fprintf(stderr, "init_gpgme failed\n");
 
1215
    exitcode = EXIT_FAILURE;
 
1216
    goto end;
 
1217
  } else {
 
1218
    gpgme_initialized = true;
 
1219
  }
 
1220
  
 
1221
  if(quit_now){
 
1222
    goto end;
 
1223
  }
 
1224
  
 
1225
  if(connect_to != NULL){
 
1226
    /* Connect directly, do not use Zeroconf */
 
1227
    /* (Mainly meant for debugging) */
 
1228
    char *address = strrchr(connect_to, ':');
 
1229
    if(address == NULL){
 
1230
      fprintf(stderr, "No colon in address\n");
 
1231
      exitcode = EXIT_FAILURE;
 
1232
      goto end;
 
1233
    }
 
1234
    
 
1235
    if(quit_now){
 
1236
      goto end;
 
1237
    }
 
1238
    
 
1239
    uint16_t port;
 
1240
    errno = 0;
 
1241
    tmpmax = strtoimax(address+1, &tmp, 10);
 
1242
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
 
1243
       or tmpmax != (uint16_t)tmpmax){
 
1244
      fprintf(stderr, "Bad port number\n");
 
1245
      exitcode = EXIT_FAILURE;
 
1246
      goto end;
 
1247
    }
 
1248
  
 
1249
    if(quit_now){
 
1250
      goto end;
 
1251
    }
 
1252
    
 
1253
    port = (uint16_t)tmpmax;
 
1254
    *address = '\0';
 
1255
    address = connect_to;
 
1256
    /* Colon in address indicates IPv6 */
 
1257
    int af;
 
1258
    if(strchr(address, ':') != NULL){
 
1259
      af = AF_INET6;
 
1260
    } else {
 
1261
      af = AF_INET;
 
1262
    }
 
1263
    
 
1264
    if(quit_now){
 
1265
      goto end;
 
1266
    }
 
1267
    
 
1268
    ret = start_mandos_communication(address, port, if_index, af);
 
1269
    if(ret < 0){
 
1270
      exitcode = EXIT_FAILURE;
 
1271
    } else {
 
1272
      exitcode = EXIT_SUCCESS;
 
1273
    }
 
1274
    goto end;
 
1275
  }
 
1276
  
 
1277
  if(quit_now){
 
1278
    goto end;
 
1279
  }
 
1280
  
 
1281
  {
 
1282
    AvahiServerConfig config;
 
1283
    /* Do not publish any local Zeroconf records */
 
1284
    avahi_server_config_init(&config);
 
1285
    config.publish_hinfo = 0;
 
1286
    config.publish_addresses = 0;
 
1287
    config.publish_workstation = 0;
 
1288
    config.publish_domain = 0;
 
1289
    
 
1290
    /* Allocate a new server */
 
1291
    mc.server = avahi_server_new(avahi_simple_poll_get
 
1292
                                 (mc.simple_poll), &config, NULL,
 
1293
                                 NULL, &error);
 
1294
    
 
1295
    /* Free the Avahi configuration data */
 
1296
    avahi_server_config_free(&config);
 
1297
  }
 
1298
  
 
1299
  /* Check if creating the Avahi server object succeeded */
 
1300
  if(mc.server == NULL){
 
1301
    fprintf(stderr, "Failed to create Avahi server: %s\n",
 
1302
            avahi_strerror(error));
 
1303
    exitcode = EXIT_FAILURE;
 
1304
    goto end;
 
1305
  }
 
1306
  
 
1307
  if(quit_now){
 
1308
    goto end;
 
1309
  }
 
1310
  
 
1311
  /* Create the Avahi service browser */
 
1312
  sb = avahi_s_service_browser_new(mc.server, if_index,
 
1313
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
 
1314
                                   NULL, 0, browse_callback, NULL);
 
1315
  if(sb == NULL){
 
1316
    fprintf(stderr, "Failed to create service browser: %s\n",
 
1317
            avahi_strerror(avahi_server_errno(mc.server)));
 
1318
    exitcode = EXIT_FAILURE;
 
1319
    goto end;
 
1320
  }
 
1321
  
 
1322
  if(quit_now){
 
1323
    goto end;
 
1324
  }
 
1325
  
 
1326
  /* Run the main loop */
 
1327
  
 
1328
  if(debug){
 
1329
    fprintf(stderr, "Starting Avahi loop search\n");
 
1330
  }
 
1331
  
 
1332
  avahi_simple_poll_loop(mc.simple_poll);
 
1333
  
 
1334
 end:
 
1335
  
 
1336
  if(debug){
 
1337
    fprintf(stderr, "%s exiting\n", argv[0]);
 
1338
  }
 
1339
  
 
1340
  /* Cleanup things */
 
1341
  if(sb != NULL)
 
1342
    avahi_s_service_browser_free(sb);
 
1343
  
 
1344
  if(mc.server != NULL)
 
1345
    avahi_server_free(mc.server);
 
1346
  
 
1347
  if(mc.simple_poll != NULL)
 
1348
    avahi_simple_poll_free(mc.simple_poll);
 
1349
  
 
1350
  if(gnutls_initialized){
 
1351
    gnutls_certificate_free_credentials(mc.cred);
 
1352
    gnutls_global_deinit();
 
1353
    gnutls_dh_params_deinit(mc.dh_params);
 
1354
  }
 
1355
  
 
1356
  if(gpgme_initialized){
 
1357
    gpgme_release(mc.ctx);
 
1358
  }
 
1359
  
 
1360
  /* Take down the network interface */
 
1361
  if(take_down_interface){
 
1362
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
1363
    if(ret == -1){
 
1364
      perror("ioctl SIOCGIFFLAGS");
 
1365
    } else if(network.ifr_flags & IFF_UP) {
 
1366
      network.ifr_flags &= ~IFF_UP; /* clear flag */
 
1367
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
1368
      if(ret == -1){
 
1369
        perror("ioctl SIOCSIFFLAGS");
 
1370
      }
 
1371
    }
 
1372
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1373
    if(ret == -1){
 
1374
      perror("close");
 
1375
    }
 
1376
  }
 
1377
  
 
1378
  /* Removes the temp directory used by GPGME */
 
1379
  if(tempdir_created){
 
1380
    DIR *d;
 
1381
    struct dirent *direntry;
 
1382
    d = opendir(tempdir);
 
1383
    if(d == NULL){
 
1384
      if(errno != ENOENT){
 
1385
        perror("opendir");
 
1386
      }
 
1387
    } else {
 
1388
      while(true){
 
1389
        direntry = readdir(d);
 
1390
        if(direntry == NULL){
 
1391
          break;
 
1392
        }
 
1393
        /* Skip "." and ".." */
 
1394
        if(direntry->d_name[0] == '.'
 
1395
           and (direntry->d_name[1] == '\0'
 
1396
                or (direntry->d_name[1] == '.'
 
1397
                    and direntry->d_name[2] == '\0'))){
 
1398
          continue;
 
1399
        }
 
1400
        char *fullname = NULL;
 
1401
        ret = asprintf(&fullname, "%s/%s", tempdir,
 
1402
                       direntry->d_name);
 
1403
        if(ret < 0){
 
1404
          perror("asprintf");
 
1405
          continue;
 
1406
        }
 
1407
        ret = remove(fullname);
 
1408
        if(ret == -1){
 
1409
          fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
 
1410
                  strerror(errno));
 
1411
        }
 
1412
        free(fullname);
 
1413
      }
 
1414
      closedir(d);
 
1415
    }
 
1416
    ret = rmdir(tempdir);
 
1417
    if(ret == -1 and errno != ENOENT){
 
1418
      perror("rmdir");
 
1419
    }
 
1420
  }
 
1421
  
 
1422
  return exitcode;
 
1423
}