/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/mandosclient.c

  • Committer: Teddy Hogeborn
  • Date: 2008-06-30 01:43:39 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080630014339-rsuztydpl2w5ml83
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
             old broadcast-UDP-to-port-49001 method.

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 © 2007-2008 Teddy Hogeborn & Björn Påhlsson
13
 
 * 
14
 
 * This program is free software: you can redistribute it and/or
15
 
 * modify it under the terms of the GNU General Public License as
16
 
 * published by the Free Software Foundation, either version 3 of the
17
 
 * License, or (at your option) any later version.
18
 
 * 
19
 
 * This program is distributed in the hope that it will be useful, but
20
 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
21
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22
 
 * General Public License for more details.
23
 
 * 
24
 
 * You should have received a copy of the GNU General Public License
25
 
 * along with this program.  If not, see
26
 
 * <http://www.gnu.org/licenses/>.
27
 
 * 
28
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
29
 
 */
30
 
 
31
 
/* Needed by GPGME, specifically gpgme_data_seek() */
32
 
#define _LARGEFILE_SOURCE
33
 
#define _FILE_OFFSET_BITS 64
34
 
 
35
 
#include <stdio.h>
36
 
#include <assert.h>
37
 
#include <stdlib.h>
38
 
#include <time.h>
39
 
#include <net/if.h>             /* if_nametoindex */
40
 
#include <sys/ioctl.h>          // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
41
 
#include <net/if.h>             // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
42
 
 
43
 
#include <avahi-core/core.h>
44
 
#include <avahi-core/lookup.h>
45
 
#include <avahi-core/log.h>
46
 
#include <avahi-common/simple-watch.h>
47
 
#include <avahi-common/malloc.h>
48
 
#include <avahi-common/error.h>
49
 
 
50
 
//mandos client part
51
 
#include <sys/types.h>          /* socket(), inet_pton() */
52
 
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
53
 
                                   struct in6_addr, inet_pton() */
54
 
#include <gnutls/gnutls.h>      /* All GnuTLS stuff */
55
 
#include <gnutls/openpgp.h>     /* GnuTLS with openpgp stuff */
56
 
 
57
 
#include <unistd.h>             /* close() */
58
 
#include <netinet/in.h>
59
 
#include <stdbool.h>            /* true */
60
 
#include <string.h>             /* memset */
61
 
#include <arpa/inet.h>          /* inet_pton() */
62
 
#include <iso646.h>             /* not */
63
 
 
64
 
// gpgme
65
 
#include <errno.h>              /* perror() */
66
 
#include <gpgme.h>
67
 
 
68
 
// getopt long
69
 
#include <getopt.h>
70
 
 
71
 
#define BUFFER_SIZE 256
72
 
#define DH_BITS 1024
73
 
 
74
 
static const char *certdir = "/conf/conf.d/mandos";
75
 
static const char *certfile = "openpgp-client.txt";
76
 
static const char *certkey = "openpgp-client-key.txt";
77
 
 
78
 
bool debug = false;
79
 
 
80
 
typedef struct {
81
 
  AvahiSimplePoll *simple_poll;
82
 
  AvahiServer *server;
83
 
  gnutls_certificate_credentials_t cred;
84
 
  unsigned int dh_bits;
85
 
  const char *priority;
86
 
} mandos_context;
87
 
 
88
 
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
89
 
                                   char **new_packet,
90
 
                                   const char *homedir){
91
 
  gpgme_data_t dh_crypto, dh_plain;
92
 
  gpgme_ctx_t ctx;
93
 
  gpgme_error_t rc;
94
 
  ssize_t ret;
95
 
  ssize_t new_packet_capacity = 0;
96
 
  ssize_t new_packet_length = 0;
97
 
  gpgme_engine_info_t engine_info;
98
 
 
99
 
  if (debug){
100
 
    fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
101
 
  }
102
 
  
103
 
  /* Init GPGME */
104
 
  gpgme_check_version(NULL);
105
 
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
106
 
  if (rc != GPG_ERR_NO_ERROR){
107
 
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
108
 
            gpgme_strsource(rc), gpgme_strerror(rc));
109
 
    return -1;
110
 
  }
111
 
  
112
 
  /* Set GPGME home directory */
113
 
  rc = gpgme_get_engine_info (&engine_info);
114
 
  if (rc != GPG_ERR_NO_ERROR){
115
 
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
116
 
            gpgme_strsource(rc), gpgme_strerror(rc));
117
 
    return -1;
118
 
  }
119
 
  while(engine_info != NULL){
120
 
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
121
 
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
122
 
                            engine_info->file_name, homedir);
123
 
      break;
124
 
    }
125
 
    engine_info = engine_info->next;
126
 
  }
127
 
  if(engine_info == NULL){
128
 
    fprintf(stderr, "Could not set home dir to %s\n", homedir);
129
 
    return -1;
130
 
  }
131
 
  
132
 
  /* Create new GPGME data buffer from packet buffer */
133
 
  rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
134
 
  if (rc != GPG_ERR_NO_ERROR){
135
 
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
136
 
            gpgme_strsource(rc), gpgme_strerror(rc));
137
 
    return -1;
138
 
  }
139
 
  
140
 
  /* Create new empty GPGME data buffer for the plaintext */
141
 
  rc = gpgme_data_new(&dh_plain);
142
 
  if (rc != GPG_ERR_NO_ERROR){
143
 
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
144
 
            gpgme_strsource(rc), gpgme_strerror(rc));
145
 
    return -1;
146
 
  }
147
 
  
148
 
  /* Create new GPGME "context" */
149
 
  rc = gpgme_new(&ctx);
150
 
  if (rc != GPG_ERR_NO_ERROR){
151
 
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
152
 
            gpgme_strsource(rc), gpgme_strerror(rc));
153
 
    return -1;
154
 
  }
155
 
  
156
 
  /* Decrypt data from the FILE pointer to the plaintext data
157
 
     buffer */
158
 
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
159
 
  if (rc != GPG_ERR_NO_ERROR){
160
 
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
161
 
            gpgme_strsource(rc), gpgme_strerror(rc));
162
 
    return -1;
163
 
  }
164
 
 
165
 
  if(debug){
166
 
    fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
167
 
  }
168
 
 
169
 
  if (debug){
170
 
    gpgme_decrypt_result_t result;
171
 
    result = gpgme_op_decrypt_result(ctx);
172
 
    if (result == NULL){
173
 
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
174
 
    } else {
175
 
      fprintf(stderr, "Unsupported algorithm: %s\n",
176
 
              result->unsupported_algorithm);
177
 
      fprintf(stderr, "Wrong key usage: %d\n",
178
 
              result->wrong_key_usage);
179
 
      if(result->file_name != NULL){
180
 
        fprintf(stderr, "File name: %s\n", result->file_name);
181
 
      }
182
 
      gpgme_recipient_t recipient;
183
 
      recipient = result->recipients;
184
 
      if(recipient){
185
 
        while(recipient != NULL){
186
 
          fprintf(stderr, "Public key algorithm: %s\n",
187
 
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
188
 
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
189
 
          fprintf(stderr, "Secret key available: %s\n",
190
 
                  recipient->status == GPG_ERR_NO_SECKEY
191
 
                  ? "No" : "Yes");
192
 
          recipient = recipient->next;
193
 
        }
194
 
      }
195
 
    }
196
 
  }
197
 
  
198
 
  /* Delete the GPGME FILE pointer cryptotext data buffer */
199
 
  gpgme_data_release(dh_crypto);
200
 
  
201
 
  /* Seek back to the beginning of the GPGME plaintext data buffer */
202
 
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
203
 
    perror("pgpme_data_seek");
204
 
  }
205
 
  
206
 
  *new_packet = 0;
207
 
  while(true){
208
 
    if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
209
 
      *new_packet = realloc(*new_packet,
210
 
                            (unsigned int)new_packet_capacity
211
 
                            + BUFFER_SIZE);
212
 
      if (*new_packet == NULL){
213
 
        perror("realloc");
214
 
        return -1;
215
 
      }
216
 
      new_packet_capacity += BUFFER_SIZE;
217
 
    }
218
 
    
219
 
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
220
 
                          BUFFER_SIZE);
221
 
    /* Print the data, if any */
222
 
    if (ret == 0){
223
 
      break;
224
 
    }
225
 
    if(ret < 0){
226
 
      perror("gpgme_data_read");
227
 
      return -1;
228
 
    }
229
 
    new_packet_length += ret;
230
 
  }
231
 
 
232
 
  /* FIXME: check characters before printing to screen so to not print
233
 
     terminal control characters */
234
 
  /*   if(debug){ */
235
 
  /*     fprintf(stderr, "decrypted password is: "); */
236
 
  /*     fwrite(*new_packet, 1, new_packet_length, stderr); */
237
 
  /*     fprintf(stderr, "\n"); */
238
 
  /*   } */
239
 
  
240
 
  /* Delete the GPGME plaintext data buffer */
241
 
  gpgme_data_release(dh_plain);
242
 
  return new_packet_length;
243
 
}
244
 
 
245
 
static const char * safer_gnutls_strerror (int value) {
246
 
  const char *ret = gnutls_strerror (value);
247
 
  if (ret == NULL)
248
 
    ret = "(unknown)";
249
 
  return ret;
250
 
}
251
 
 
252
 
static void debuggnutls(__attribute__((unused)) int level,
253
 
                        const char* string){
254
 
  fprintf(stderr, "%s", string);
255
 
}
256
 
 
257
 
static int initgnutls(mandos_context *mc){
258
 
  const char *err;
259
 
  int ret;
260
 
  
261
 
  if(debug){
262
 
    fprintf(stderr, "Initializing GnuTLS\n");
263
 
  }
264
 
 
265
 
  if ((ret = gnutls_global_init ())
266
 
      != GNUTLS_E_SUCCESS) {
267
 
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
268
 
    return -1;
269
 
  }
270
 
 
271
 
  if (debug){
272
 
    gnutls_global_set_log_level(11);
273
 
    gnutls_global_set_log_function(debuggnutls);
274
 
  }
275
 
  
276
 
  /* openpgp credentials */
277
 
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
278
 
      != GNUTLS_E_SUCCESS) {
279
 
    fprintf (stderr, "memory error: %s\n",
280
 
             safer_gnutls_strerror(ret));
281
 
    return -1;
282
 
  }
283
 
  
284
 
  if(debug){
285
 
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
286
 
            " and keyfile %s as GnuTLS credentials\n", certfile,
287
 
            certkey);
288
 
  }
289
 
  
290
 
  ret = gnutls_certificate_set_openpgp_key_file
291
 
    (es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
292
 
  if (ret != GNUTLS_E_SUCCESS) {
293
 
    fprintf
294
 
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
295
 
       " '%s')\n",
296
 
       ret, certfile, certkey);
297
 
    fprintf(stdout, "The Error is: %s\n",
298
 
            safer_gnutls_strerror(ret));
299
 
    return -1;
300
 
  }
301
 
  
302
 
  //GnuTLS server initialization
303
 
  if ((ret = gnutls_dh_params_init (&es->dh_params))
304
 
      != GNUTLS_E_SUCCESS) {
305
 
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
306
 
             safer_gnutls_strerror(ret));
307
 
    return -1;
308
 
  }
309
 
  
310
 
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
311
 
      != GNUTLS_E_SUCCESS) {
312
 
    fprintf (stderr, "Error in prime generation: %s\n",
313
 
             safer_gnutls_strerror(ret));
314
 
    return -1;
315
 
  }
316
 
  
317
 
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
318
 
  
319
 
  // GnuTLS session creation
320
 
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
321
 
      != GNUTLS_E_SUCCESS){
322
 
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
323
 
            safer_gnutls_strerror(ret));
324
 
  }
325
 
  
326
 
  if ((ret = gnutls_priority_set_direct (es->session, mc->priority, &err))
327
 
      != GNUTLS_E_SUCCESS) {
328
 
    fprintf(stderr, "Syntax error at: %s\n", err);
329
 
    fprintf(stderr, "GnuTLS error: %s\n",
330
 
            safer_gnutls_strerror(ret));
331
 
    return -1;
332
 
  }
333
 
  
334
 
  if ((ret = gnutls_credentials_set
335
 
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
336
 
      != GNUTLS_E_SUCCESS) {
337
 
    fprintf(stderr, "Error setting a credentials set: %s\n",
338
 
            safer_gnutls_strerror(ret));
339
 
    return -1;
340
 
  }
341
 
  
342
 
  /* ignore client certificate if any. */
343
 
  gnutls_certificate_server_set_request (es->session,
344
 
                                         GNUTLS_CERT_IGNORE);
345
 
  
346
 
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
347
 
  
348
 
  return 0;
349
 
}
350
 
 
351
 
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
352
 
                      __attribute__((unused)) const char *txt){}
353
 
 
354
 
static int start_mandos_communication(const char *ip, uint16_t port,
355
 
                                      AvahiIfIndex if_index,
356
 
                                      mandos_context *mc){
357
 
  int ret, tcp_sd;
358
 
  struct sockaddr_in6 to;
359
 
  encrypted_session es;
360
 
  char *buffer = NULL;
361
 
  char *decrypted_buffer;
362
 
  size_t buffer_length = 0;
363
 
  size_t buffer_capacity = 0;
364
 
  ssize_t decrypted_buffer_size;
365
 
  size_t written = 0;
366
 
  int retval = 0;
367
 
  char interface[IF_NAMESIZE];
368
 
  
369
 
  if(debug){
370
 
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
371
 
            ip, port);
372
 
  }
373
 
  
374
 
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
375
 
  if(tcp_sd < 0) {
376
 
    perror("socket");
377
 
    return -1;
378
 
  }
379
 
 
380
 
  if(debug){
381
 
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
382
 
      if(debug){
383
 
        perror("if_indextoname");
384
 
      }
385
 
      return -1;
386
 
    }
387
 
    
388
 
    fprintf(stderr, "Binding to interface %s\n", interface);
389
 
  }
390
 
  
391
 
  memset(&to,0,sizeof(to));     /* Spurious warning */
392
 
  to.sin6_family = AF_INET6;
393
 
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
394
 
  if (ret < 0 ){
395
 
    perror("inet_pton");
396
 
    return -1;
397
 
  }  
398
 
  if(ret == 0){
399
 
    fprintf(stderr, "Bad address: %s\n", ip);
400
 
    return -1;
401
 
  }
402
 
  to.sin6_port = htons(port);   /* Spurious warning */
403
 
  
404
 
  to.sin6_scope_id = (uint32_t)if_index;
405
 
  
406
 
  if(debug){
407
 
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
408
 
/*     char addrstr[INET6_ADDRSTRLEN]; */
409
 
/*     if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
410
 
/*               sizeof(addrstr)) == NULL){ */
411
 
/*       perror("inet_ntop"); */
412
 
/*     } else { */
413
 
/*       fprintf(stderr, "Really connecting to: %s, port %d\n", */
414
 
/*            addrstr, ntohs(to.sin6_port)); */
415
 
/*     } */
416
 
  }
417
 
  
418
 
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
419
 
  if (ret < 0){
420
 
    perror("connect");
421
 
    return -1;
422
 
  }
423
 
  
424
 
  ret = initgnutls (&es);
425
 
  if (ret != 0){
426
 
    retval = -1;
427
 
    return -1;
428
 
  }
429
 
  
430
 
  gnutls_transport_set_ptr (es.session,
431
 
                            (gnutls_transport_ptr_t) tcp_sd);
432
 
  
433
 
  if(debug){
434
 
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
435
 
  }
436
 
  
437
 
  ret = gnutls_handshake (es.session);
438
 
  
439
 
  if (ret != GNUTLS_E_SUCCESS){
440
 
    if(debug){
441
 
      fprintf(stderr, "\n*** Handshake failed ***\n");
442
 
      gnutls_perror (ret);
443
 
    }
444
 
    retval = -1;
445
 
    goto exit;
446
 
  }
447
 
  
448
 
  //Retrieve OpenPGP packet that contains the wanted password
449
 
  
450
 
  if(debug){
451
 
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
452
 
            ip);
453
 
  }
454
 
 
455
 
  while(true){
456
 
    if (buffer_length + BUFFER_SIZE > buffer_capacity){
457
 
      buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
458
 
      if (buffer == NULL){
459
 
        perror("realloc");
460
 
        goto exit;
461
 
      }
462
 
      buffer_capacity += BUFFER_SIZE;
463
 
    }
464
 
    
465
 
    ret = gnutls_record_recv
466
 
      (es.session, buffer+buffer_length, BUFFER_SIZE);
467
 
    if (ret == 0){
468
 
      break;
469
 
    }
470
 
    if (ret < 0){
471
 
      switch(ret){
472
 
      case GNUTLS_E_INTERRUPTED:
473
 
      case GNUTLS_E_AGAIN:
474
 
        break;
475
 
      case GNUTLS_E_REHANDSHAKE:
476
 
        ret = gnutls_handshake (es.session);
477
 
        if (ret < 0){
478
 
          fprintf(stderr, "\n*** Handshake failed ***\n");
479
 
          gnutls_perror (ret);
480
 
          retval = -1;
481
 
          goto exit;
482
 
        }
483
 
        break;
484
 
      default:
485
 
        fprintf(stderr, "Unknown error while reading data from"
486
 
                " encrypted session with mandos server\n");
487
 
        retval = -1;
488
 
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
489
 
        goto exit;
490
 
      }
491
 
    } else {
492
 
      buffer_length += (size_t) ret;
493
 
    }
494
 
  }
495
 
  
496
 
  if (buffer_length > 0){
497
 
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
498
 
                                               buffer_length,
499
 
                                               &decrypted_buffer,
500
 
                                               certdir);
501
 
    if (decrypted_buffer_size >= 0){
502
 
      while(written < (size_t) decrypted_buffer_size){
503
 
        ret = (int)fwrite (decrypted_buffer + written, 1,
504
 
                           (size_t)decrypted_buffer_size - written,
505
 
                           stdout);
506
 
        if(ret == 0 and ferror(stdout)){
507
 
          if(debug){
508
 
            fprintf(stderr, "Error writing encrypted data: %s\n",
509
 
                    strerror(errno));
510
 
          }
511
 
          retval = -1;
512
 
          break;
513
 
        }
514
 
        written += (size_t)ret;
515
 
      }
516
 
      free(decrypted_buffer);
517
 
    } else {
518
 
      retval = -1;
519
 
    }
520
 
  }
521
 
 
522
 
  //shutdown procedure
523
 
 
524
 
  if(debug){
525
 
    fprintf(stderr, "Closing TLS session\n");
526
 
  }
527
 
 
528
 
  free(buffer);
529
 
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
530
 
 exit:
531
 
  close(tcp_sd);
532
 
  gnutls_deinit (es.session);
533
 
  gnutls_certificate_free_credentials (es.cred);
534
 
  gnutls_global_deinit ();
535
 
  return retval;
536
 
}
537
 
 
538
 
static void resolve_callback( AvahiSServiceResolver *r,
539
 
                              AvahiIfIndex interface,
540
 
                              AVAHI_GCC_UNUSED AvahiProtocol protocol,
541
 
                              AvahiResolverEvent event,
542
 
                              const char *name,
543
 
                              const char *type,
544
 
                              const char *domain,
545
 
                              const char *host_name,
546
 
                              const AvahiAddress *address,
547
 
                              uint16_t port,
548
 
                              AVAHI_GCC_UNUSED AvahiStringList *txt,
549
 
                              AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
550
 
                              AVAHI_GCC_UNUSED void* userdata) {
551
 
  mandos_context *mc = userdata;
552
 
  assert(r);                    /* Spurious warning */
553
 
  
554
 
  /* Called whenever a service has been resolved successfully or
555
 
     timed out */
556
 
  
557
 
  switch (event) {
558
 
  default:
559
 
  case AVAHI_RESOLVER_FAILURE:
560
 
    fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
561
 
            " type '%s' in domain '%s': %s\n", name, type, domain,
562
 
            avahi_strerror(avahi_server_errno(mc->server)));
563
 
    break;
564
 
    
565
 
  case AVAHI_RESOLVER_FOUND:
566
 
    {
567
 
      char ip[AVAHI_ADDRESS_STR_MAX];
568
 
      avahi_address_snprint(ip, sizeof(ip), address);
569
 
      if(debug){
570
 
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
571
 
                " port %d\n", name, host_name, ip, port);
572
 
      }
573
 
      int ret = start_mandos_communication(ip, port, interface, mc);
574
 
      if (ret == 0){
575
 
        exit(EXIT_SUCCESS);
576
 
      }
577
 
    }
578
 
  }
579
 
  avahi_s_service_resolver_free(r);
580
 
}
581
 
 
582
 
static void browse_callback( AvahiSServiceBrowser *b,
583
 
                             AvahiIfIndex interface,
584
 
                             AvahiProtocol protocol,
585
 
                             AvahiBrowserEvent event,
586
 
                             const char *name,
587
 
                             const char *type,
588
 
                             const char *domain,
589
 
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
590
 
                             void* userdata) {
591
 
  mandos_context *mc = userdata;
592
 
  assert(b);                    /* Spurious warning */
593
 
  
594
 
  /* Called whenever a new services becomes available on the LAN or
595
 
     is removed from the LAN */
596
 
  
597
 
  switch (event) {
598
 
  default:
599
 
  case AVAHI_BROWSER_FAILURE:
600
 
      
601
 
    fprintf(stderr, "(Browser) %s\n",
602
 
            avahi_strerror(avahi_server_errno(mc->server)));
603
 
    avahi_simple_poll_quit(mc->simple_poll);
604
 
    return;
605
 
      
606
 
  case AVAHI_BROWSER_NEW:
607
 
    /* We ignore the returned resolver object. In the callback
608
 
       function we free it. If the server is terminated before
609
 
       the callback function is called the server will free
610
 
       the resolver for us. */
611
 
      
612
 
    if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name,
613
 
                                       type, domain,
614
 
                                       AVAHI_PROTO_INET6, 0,
615
 
                                       resolve_callback, mc)))
616
 
      fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
617
 
              avahi_strerror(avahi_server_errno(s)));
618
 
    break;
619
 
      
620
 
  case AVAHI_BROWSER_REMOVE:
621
 
    break;
622
 
      
623
 
  case AVAHI_BROWSER_ALL_FOR_NOW:
624
 
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
625
 
    break;
626
 
  }
627
 
}
628
 
 
629
 
/* Combines file name and path and returns the malloced new
630
 
   string. some sane checks could/should be added */
631
 
static const char *combinepath(const char *first, const char *second){
632
 
  size_t f_len = strlen(first);
633
 
  size_t s_len = strlen(second);
634
 
  char *tmp = malloc(f_len + s_len + 2);
635
 
  if (tmp == NULL){
636
 
    return NULL;
637
 
  }
638
 
  if(f_len > 0){
639
 
    memcpy(tmp, first, f_len);
640
 
  }
641
 
  tmp[f_len] = '/';
642
 
  if(s_len > 0){
643
 
    memcpy(tmp + f_len + 1, second, s_len);
644
 
  }
645
 
  tmp[f_len + 1 + s_len] = '\0';
646
 
  return tmp;
647
 
}
648
 
 
649
 
 
650
 
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
651
 
    AvahiServerConfig config;
652
 
    AvahiSServiceBrowser *sb = NULL;
653
 
    int error;
654
 
    int ret;
655
 
    int returncode = EXIT_SUCCESS;
656
 
    const char *interface = "eth0";
657
 
    struct ifreq network;
658
 
    int sd;
659
 
    char *connect_to = NULL;
660
 
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
661
 
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
662
 
                          .dh_bits = 2048, .priority = "SECURE256"};
663
 
    
664
 
    while (true){
665
 
      static struct option long_options[] = {
666
 
        {"debug", no_argument, (int *)&debug, 1},
667
 
        {"connect", required_argument, 0, 'C'},
668
 
        {"interface", required_argument, 0, 'i'},
669
 
        {"certdir", required_argument, 0, 'd'},
670
 
        {"certkey", required_argument, 0, 'c'},
671
 
        {"certfile", required_argument, 0, 'k'},
672
 
        {"dh_bits", required_argument, 0, 'D'},
673
 
        {"priority", required_argument, 0, 'p'},
674
 
        {0, 0, 0, 0} };
675
 
      
676
 
      int option_index = 0;
677
 
      ret = getopt_long (argc, argv, "i:", long_options,
678
 
                         &option_index);
679
 
      
680
 
      if (ret == -1){
681
 
        break;
682
 
      }
683
 
      
684
 
      switch(ret){
685
 
      case 0:
686
 
        break;
687
 
      case 'i':
688
 
        interface = optarg;
689
 
        break;
690
 
      case 'C':
691
 
        connect_to = optarg;
692
 
        break;
693
 
      case 'd':
694
 
        certdir = optarg;
695
 
        break;
696
 
      case 'c':
697
 
        certfile = optarg;
698
 
        break;
699
 
      case 'k':
700
 
        certkey = optarg;
701
 
        break;
702
 
      case 'D':
703
 
        {
704
 
          long int tmp;
705
 
          errno = 0;
706
 
          tmp = strtol(optarg, NULL, 10);
707
 
          if (errno == ERANGE){
708
 
            perror("strtol");
709
 
            exit(EXIT_FAILURE);
710
 
          }
711
 
          mc.dh_bits = tmp;
712
 
        }
713
 
        break;
714
 
      case 'p':
715
 
        mc.priority = optarg;
716
 
        break;
717
 
      default:
718
 
        exit(EXIT_FAILURE);
719
 
      }
720
 
    }
721
 
    
722
 
    certfile = combinepath(certdir, certfile);
723
 
    if (certfile == NULL){
724
 
      perror("combinepath");
725
 
      returncode = EXIT_FAILURE;
726
 
      goto exit;
727
 
    }
728
 
 
729
 
    certkey = combinepath(certdir, certkey);
730
 
    if (certkey == NULL){
731
 
      perror("combinepath");
732
 
      returncode = EXIT_FAILURE;
733
 
      goto exit;
734
 
    }
735
 
    
736
 
    if_index = (AvahiIfIndex) if_nametoindex(interface);
737
 
    if(if_index == 0){
738
 
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
739
 
      exit(EXIT_FAILURE);
740
 
    }
741
 
    
742
 
    if(connect_to != NULL){
743
 
      /* Connect directly, do not use Zeroconf */
744
 
      /* (Mainly meant for debugging) */
745
 
      char *address = strrchr(connect_to, ':');
746
 
      if(address == NULL){
747
 
        fprintf(stderr, "No colon in address\n");
748
 
        exit(EXIT_FAILURE);
749
 
      }
750
 
      errno = 0;
751
 
      uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
752
 
      if(errno){
753
 
        perror("Bad port number");
754
 
        exit(EXIT_FAILURE);
755
 
      }
756
 
      *address = '\0';
757
 
      address = connect_to;
758
 
      ret = start_mandos_communication(address, port, if_index);
759
 
      if(ret < 0){
760
 
        exit(EXIT_FAILURE);
761
 
      } else {
762
 
        exit(EXIT_SUCCESS);
763
 
      }
764
 
    }
765
 
    
766
 
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
767
 
    if(sd < 0) {
768
 
      perror("socket");
769
 
      returncode = EXIT_FAILURE;
770
 
      goto exit;
771
 
    }
772
 
    strcpy(network.ifr_name, interface);    
773
 
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
774
 
    if(ret == -1){
775
 
      
776
 
      perror("ioctl SIOCGIFFLAGS");
777
 
      returncode = EXIT_FAILURE;
778
 
      goto exit;
779
 
    }
780
 
    if((network.ifr_flags & IFF_UP) == 0){
781
 
      network.ifr_flags |= IFF_UP;
782
 
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
783
 
      if(ret == -1){
784
 
        perror("ioctl SIOCSIFFLAGS");
785
 
        returncode = EXIT_FAILURE;
786
 
        goto exit;
787
 
      }
788
 
    }
789
 
    close(sd);
790
 
    
791
 
    if (not debug){
792
 
      avahi_set_log_function(empty_log);
793
 
    }
794
 
    
795
 
    /* Initialize the psuedo-RNG */
796
 
    srand((unsigned int) time(NULL));
797
 
 
798
 
    /* Allocate main loop object */
799
 
    if (!(mc.simple_poll = avahi_simple_poll_new())) {
800
 
        fprintf(stderr, "Failed to create simple poll object.\n");
801
 
        returncode = EXIT_FAILURE;      
802
 
        goto exit;
803
 
    }
804
 
 
805
 
    /* Do not publish any local records */
806
 
    avahi_server_config_init(&config);
807
 
    config.publish_hinfo = 0;
808
 
    config.publish_addresses = 0;
809
 
    config.publish_workstation = 0;
810
 
    config.publish_domain = 0;
811
 
 
812
 
    /* Allocate a new server */
813
 
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
814
 
                              &config, NULL, NULL, &error);
815
 
 
816
 
    /* Free the configuration data */
817
 
    avahi_server_config_free(&config);
818
 
 
819
 
    /* Check if creating the server object succeeded */
820
 
    if (!mc.server) {
821
 
        fprintf(stderr, "Failed to create server: %s\n",
822
 
                avahi_strerror(error));
823
 
        returncode = EXIT_FAILURE;
824
 
        goto exit;
825
 
    }
826
 
    
827
 
    /* Create the service browser */
828
 
    sb = avahi_s_service_browser_new(mc.server, if_index,
829
 
                                     AVAHI_PROTO_INET6,
830
 
                                     "_mandos._tcp", NULL, 0,
831
 
                                     browse_callback, &mc);
832
 
    if (!sb) {
833
 
        fprintf(stderr, "Failed to create service browser: %s\n",
834
 
                avahi_strerror(avahi_server_errno(mc.server)));
835
 
        returncode = EXIT_FAILURE;
836
 
        goto exit;
837
 
    }
838
 
    
839
 
    /* Run the main loop */
840
 
 
841
 
    if (debug){
842
 
      fprintf(stderr, "Starting avahi loop search\n");
843
 
    }
844
 
    
845
 
    avahi_simple_poll_loop(simple_poll);
846
 
    
847
 
 exit:
848
 
 
849
 
    if (debug){
850
 
      fprintf(stderr, "%s exiting\n", argv[0]);
851
 
    }
852
 
    
853
 
    /* Cleanup things */
854
 
    if (sb)
855
 
        avahi_s_service_browser_free(sb);
856
 
    
857
 
    if (mc.server)
858
 
        avahi_server_free(mc.server);
859
 
 
860
 
    if (simple_poll)
861
 
        avahi_simple_poll_free(simple_poll);
862
 
    free(certfile);
863
 
    free(certkey);
864
 
    
865
 
    return returncode;
866
 
}