/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

plugin-runner: Added support for empty string arguments
               Fixed bug where IP:port argument failed
mandos-client: Fixed bug where files wasnt removed after sigterm signal

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