/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

Earlier signal handling

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