/mandos/release

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

« back to all changes in this revision

Viewing changes to plugins.d/mandosclient.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-03 01:09:36 UTC
  • mfrom: (24.1.9 mandos)
  • Revision ID: teddy@fukt.bsnet.se-20080803010936-ujme8tgxceszfbi1
* plugbasedclient.c (main): New "--userid" and "--groupid" options.
                            Take an additional non-option argument and
                            parse it as a plus-separated and -prefixed
                            list of additional options.

* plugins.d/mandosclient.c (DH_BITS): Replaced with
                                      "mandos_context.dh_bits".  All
                                      users changed.
  (certdir): Renamed to "keydir".  All users changed.
  (certfile): Renamed to "pubkeyfile".  All users changed.
  (certkey): Renamed to "seckeyfile".  All users changed.
  (encrypted_session): Replaced with "mandos_context".  All users
                       changed.
  (initgnutls): Take additional "session" and "dh_params" arguments.
                All callers changed.
  (start_mandos_communication): Take additional "mc" argument.  All
                                callers changed.  Print target IPv6
                                address if different than supplied
                                string.
  (simple_poll) Replaced with "mandos_context.simple_poll".  All users
                changed.
  (server): Replaced with "mandos_context.server".  All users changed.
  (main): Default interface to "eth0".  Rename "--certdir" to
          "--keydir", "--certkey" to "--seckey", and "--certfile" to
          "--pubkey".  New options "--dh-bits" and "--priority".  If
          the interface is not up, bring it up.

Show diffs side-by-side

added added

removed removed

Lines of Context:
8
8
 * includes the following functions: "resolve_callback",
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
 
 * Everything else is Copyright © 2007-2008 Teddy Hogeborn and Björn
12
 
 * Påhlsson.
 
11
 * Everything else is
 
12
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
13
13
 * 
14
14
 * This program is free software: you can redistribute it and/or
15
15
 * modify it under the terms of the GNU General Public License as
25
25
 * along with this program.  If not, see
26
26
 * <http://www.gnu.org/licenses/>.
27
27
 * 
28
 
 * Contact the authors at <https://www.fukt.bsnet.se/~belorn/> and
29
 
 * <https://www.fukt.bsnet.se/~teddy/>.
 
28
 * Contact the authors at <mandos@fukt.bsnet.se>.
30
29
 */
31
30
 
32
 
#define _FORTIFY_SOURCE 2
33
 
 
 
31
/* Needed by GPGME, specifically gpgme_data_seek() */
34
32
#define _LARGEFILE_SOURCE
35
33
#define _FILE_OFFSET_BITS 64
36
34
 
39
37
#include <stdlib.h>
40
38
#include <time.h>
41
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
42
 
43
43
#include <avahi-core/core.h>
44
44
#include <avahi-core/lookup.h>
65
65
#include <errno.h>              /* perror() */
66
66
#include <gpgme.h>
67
67
 
68
 
// getopt long
 
68
// getopt_long
69
69
#include <getopt.h>
70
70
 
71
 
#ifndef CERT_ROOT
72
 
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
73
 
#endif
74
 
#define CERTFILE CERT_ROOT "openpgp-client.txt"
75
 
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
76
71
#define BUFFER_SIZE 256
77
 
#define DH_BITS 1024
 
72
 
 
73
static const char *keydir = "/conf/conf.d/mandos";
 
74
static const char *pubkeyfile = "pubkey.txt";
 
75
static const char *seckeyfile = "seckey.txt";
78
76
 
79
77
bool debug = false;
80
78
 
 
79
/* Used for passing in values through all the callback functions */
81
80
typedef struct {
82
 
  gnutls_session_t session;
 
81
  AvahiSimplePoll *simple_poll;
 
82
  AvahiServer *server;
83
83
  gnutls_certificate_credentials_t cred;
84
 
  gnutls_dh_params_t dh_params;
85
 
} encrypted_session;
86
 
 
87
 
 
88
 
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
89
 
                            char **new_packet, const char *homedir){
 
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){
90
91
  gpgme_data_t dh_crypto, dh_plain;
91
92
  gpgme_ctx_t ctx;
92
93
  gpgme_error_t rc;
101
102
  
102
103
  /* Init GPGME */
103
104
  gpgme_check_version(NULL);
104
 
  gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
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
  }
105
111
  
106
112
  /* Set GPGME home directory */
107
113
  rc = gpgme_get_engine_info (&engine_info);
193
199
  gpgme_data_release(dh_crypto);
194
200
  
195
201
  /* Seek back to the beginning of the GPGME plaintext data buffer */
196
 
  gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET);
197
 
 
 
202
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
 
203
    perror("pgpme_data_seek");
 
204
  }
 
205
  
198
206
  *new_packet = 0;
199
207
  while(true){
200
208
    if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
241
249
  return ret;
242
250
}
243
251
 
244
 
void debuggnutls(__attribute__((unused)) int level,
245
 
                 const char* string){
 
252
static void debuggnutls(__attribute__((unused)) int level,
 
253
                        const char* string){
246
254
  fprintf(stderr, "%s", string);
247
255
}
248
256
 
249
 
int initgnutls(encrypted_session *es){
 
257
static int initgnutls(mandos_context *mc, gnutls_session_t *session,
 
258
                      gnutls_dh_params_t *dh_params){
250
259
  const char *err;
251
260
  int ret;
252
261
  
253
262
  if(debug){
254
263
    fprintf(stderr, "Initializing GnuTLS\n");
255
264
  }
256
 
  
 
265
 
257
266
  if ((ret = gnutls_global_init ())
258
267
      != GNUTLS_E_SUCCESS) {
259
268
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
260
269
    return -1;
261
270
  }
262
 
 
 
271
  
263
272
  if (debug){
264
273
    gnutls_global_set_log_level(11);
265
274
    gnutls_global_set_log_function(debuggnutls);
266
275
  }
267
276
  
268
277
  /* openpgp credentials */
269
 
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
 
278
  if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
270
279
      != GNUTLS_E_SUCCESS) {
271
280
    fprintf (stderr, "memory error: %s\n",
272
281
             safer_gnutls_strerror(ret));
275
284
  
276
285
  if(debug){
277
286
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
278
 
            " and keyfile %s as GnuTLS credentials\n", CERTFILE,
279
 
            KEYFILE);
 
287
            " and keyfile %s as GnuTLS credentials\n", pubkeyfile,
 
288
            seckeyfile);
280
289
  }
281
290
  
282
291
  ret = gnutls_certificate_set_openpgp_key_file
283
 
    (es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
 
292
    (mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
284
293
  if (ret != GNUTLS_E_SUCCESS) {
285
294
    fprintf
286
295
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
287
296
       " '%s')\n",
288
 
       ret, CERTFILE, KEYFILE);
 
297
       ret, pubkeyfile, seckeyfile);
289
298
    fprintf(stdout, "The Error is: %s\n",
290
299
            safer_gnutls_strerror(ret));
291
300
    return -1;
292
301
  }
293
302
  
294
303
  //GnuTLS server initialization
295
 
  if ((ret = gnutls_dh_params_init (&es->dh_params))
 
304
  if ((ret = gnutls_dh_params_init(dh_params))
296
305
      != GNUTLS_E_SUCCESS) {
297
306
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
298
307
             safer_gnutls_strerror(ret));
299
308
    return -1;
300
309
  }
301
310
  
302
 
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
 
311
  if ((ret = gnutls_dh_params_generate2(*dh_params, mc->dh_bits))
303
312
      != GNUTLS_E_SUCCESS) {
304
313
    fprintf (stderr, "Error in prime generation: %s\n",
305
314
             safer_gnutls_strerror(ret));
306
315
    return -1;
307
316
  }
308
317
  
309
 
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
 
318
  gnutls_certificate_set_dh_params(mc->cred, *dh_params);
310
319
  
311
320
  // GnuTLS session creation
312
 
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
 
321
  if ((ret = gnutls_init(session, GNUTLS_SERVER))
313
322
      != GNUTLS_E_SUCCESS){
314
323
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
315
324
            safer_gnutls_strerror(ret));
316
325
  }
317
326
  
318
 
  if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
 
327
  if ((ret = gnutls_priority_set_direct(*session, mc->priority, &err))
319
328
      != GNUTLS_E_SUCCESS) {
320
329
    fprintf(stderr, "Syntax error at: %s\n", err);
321
330
    fprintf(stderr, "GnuTLS error: %s\n",
323
332
    return -1;
324
333
  }
325
334
  
326
 
  if ((ret = gnutls_credentials_set
327
 
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
 
335
  if ((ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
336
                                    mc->cred))
328
337
      != GNUTLS_E_SUCCESS) {
329
338
    fprintf(stderr, "Error setting a credentials set: %s\n",
330
339
            safer_gnutls_strerror(ret));
332
341
  }
333
342
  
334
343
  /* ignore client certificate if any. */
335
 
  gnutls_certificate_server_set_request (es->session,
 
344
  gnutls_certificate_server_set_request (*session,
336
345
                                         GNUTLS_CERT_IGNORE);
337
346
  
338
 
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
 
347
  gnutls_dh_set_prime_bits (*session, mc->dh_bits);
339
348
  
340
349
  return 0;
341
350
}
342
351
 
343
 
void empty_log(__attribute__((unused)) AvahiLogLevel level,
344
 
               __attribute__((unused)) const char *txt){}
 
352
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
353
                      __attribute__((unused)) const char *txt){}
345
354
 
346
 
int start_mandos_communication(const char *ip, uint16_t port,
347
 
                               unsigned int if_index){
 
355
static int start_mandos_communication(const char *ip, uint16_t port,
 
356
                                      AvahiIfIndex if_index,
 
357
                                      mandos_context *mc){
348
358
  int ret, tcp_sd;
349
359
  struct sockaddr_in6 to;
350
 
  encrypted_session es;
351
360
  char *buffer = NULL;
352
361
  char *decrypted_buffer;
353
362
  size_t buffer_length = 0;
356
365
  size_t written = 0;
357
366
  int retval = 0;
358
367
  char interface[IF_NAMESIZE];
 
368
  gnutls_session_t session;
 
369
  gnutls_dh_params_t dh_params;
359
370
  
360
371
  if(debug){
361
 
    fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
 
372
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
 
373
            ip, port);
362
374
  }
363
375
  
364
376
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
366
378
    perror("socket");
367
379
    return -1;
368
380
  }
369
 
  
370
 
  if(if_indextoname(if_index, interface) == NULL){
371
 
    if(debug){
 
381
 
 
382
  if(debug){
 
383
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
372
384
      perror("if_indextoname");
 
385
      return -1;
373
386
    }
374
 
    return -1;
375
 
  }
376
 
  
377
 
  if(debug){
378
387
    fprintf(stderr, "Binding to interface %s\n", interface);
379
388
  }
380
389
  
384
393
  if (ret < 0 ){
385
394
    perror("inet_pton");
386
395
    return -1;
387
 
  }  
 
396
  }
388
397
  if(ret == 0){
389
398
    fprintf(stderr, "Bad address: %s\n", ip);
390
399
    return -1;
394
403
  to.sin6_scope_id = (uint32_t)if_index;
395
404
  
396
405
  if(debug){
397
 
    fprintf(stderr, "Connection to: %s\n", ip);
 
406
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
 
407
    char addrstr[INET6_ADDRSTRLEN] = "";
 
408
    if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr,
 
409
                 sizeof(addrstr)) == NULL){
 
410
      perror("inet_ntop");
 
411
    } else {
 
412
      if(strcmp(addrstr, ip) != 0){
 
413
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
 
414
      }
 
415
    }
398
416
  }
399
417
  
400
418
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
403
421
    return -1;
404
422
  }
405
423
  
406
 
  ret = initgnutls (&es);
 
424
  ret = initgnutls (mc, &session, &dh_params);
407
425
  if (ret != 0){
408
426
    retval = -1;
409
427
    return -1;
410
428
  }
411
429
  
412
 
  gnutls_transport_set_ptr (es.session,
413
 
                            (gnutls_transport_ptr_t) tcp_sd);
 
430
  gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
414
431
  
415
432
  if(debug){
416
433
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
417
434
  }
418
435
  
419
 
  ret = gnutls_handshake (es.session);
 
436
  ret = gnutls_handshake (session);
420
437
  
421
438
  if (ret != GNUTLS_E_SUCCESS){
422
 
    fprintf(stderr, "\n*** Handshake failed ***\n");
423
 
    gnutls_perror (ret);
 
439
    if(debug){
 
440
      fprintf(stderr, "\n*** Handshake failed ***\n");
 
441
      gnutls_perror (ret);
 
442
    }
424
443
    retval = -1;
425
444
    goto exit;
426
445
  }
442
461
      buffer_capacity += BUFFER_SIZE;
443
462
    }
444
463
    
445
 
    ret = gnutls_record_recv
446
 
      (es.session, buffer+buffer_length, BUFFER_SIZE);
 
464
    ret = gnutls_record_recv(session, buffer+buffer_length,
 
465
                             BUFFER_SIZE);
447
466
    if (ret == 0){
448
467
      break;
449
468
    }
453
472
      case GNUTLS_E_AGAIN:
454
473
        break;
455
474
      case GNUTLS_E_REHANDSHAKE:
456
 
        ret = gnutls_handshake (es.session);
 
475
        ret = gnutls_handshake (session);
457
476
        if (ret < 0){
458
477
          fprintf(stderr, "\n*** Handshake failed ***\n");
459
478
          gnutls_perror (ret);
465
484
        fprintf(stderr, "Unknown error while reading data from"
466
485
                " encrypted session with mandos server\n");
467
486
        retval = -1;
468
 
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
 
487
        gnutls_bye (session, GNUTLS_SHUT_RDWR);
469
488
        goto exit;
470
489
      }
471
490
    } else {
477
496
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
478
497
                                               buffer_length,
479
498
                                               &decrypted_buffer,
480
 
                                               CERT_ROOT);
 
499
                                               keydir);
481
500
    if (decrypted_buffer_size >= 0){
482
 
      while(written < decrypted_buffer_size){
 
501
      while(written < (size_t) decrypted_buffer_size){
483
502
        ret = (int)fwrite (decrypted_buffer + written, 1,
484
503
                           (size_t)decrypted_buffer_size - written,
485
504
                           stdout);
506
525
  }
507
526
 
508
527
  free(buffer);
509
 
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
 
528
  gnutls_bye (session, GNUTLS_SHUT_RDWR);
510
529
 exit:
511
530
  close(tcp_sd);
512
 
  gnutls_deinit (es.session);
513
 
  gnutls_certificate_free_credentials (es.cred);
 
531
  gnutls_deinit (session);
 
532
  gnutls_certificate_free_credentials (mc->cred);
514
533
  gnutls_global_deinit ();
515
534
  return retval;
516
535
}
517
536
 
518
 
static AvahiSimplePoll *simple_poll = NULL;
519
 
static AvahiServer *server = NULL;
520
 
 
521
 
static void resolve_callback(
522
 
    AvahiSServiceResolver *r,
523
 
    AvahiIfIndex interface,
524
 
    AVAHI_GCC_UNUSED AvahiProtocol protocol,
525
 
    AvahiResolverEvent event,
526
 
    const char *name,
527
 
    const char *type,
528
 
    const char *domain,
529
 
    const char *host_name,
530
 
    const AvahiAddress *address,
531
 
    uint16_t port,
532
 
    AVAHI_GCC_UNUSED AvahiStringList *txt,
533
 
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
534
 
    AVAHI_GCC_UNUSED void* userdata) {
535
 
    
 
537
static void resolve_callback( AvahiSServiceResolver *r,
 
538
                              AvahiIfIndex interface,
 
539
                              AVAHI_GCC_UNUSED AvahiProtocol protocol,
 
540
                              AvahiResolverEvent event,
 
541
                              const char *name,
 
542
                              const char *type,
 
543
                              const char *domain,
 
544
                              const char *host_name,
 
545
                              const AvahiAddress *address,
 
546
                              uint16_t port,
 
547
                              AVAHI_GCC_UNUSED AvahiStringList *txt,
 
548
                              AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
 
549
                              void* userdata) {
 
550
  mandos_context *mc = userdata;
536
551
  assert(r);                    /* Spurious warning */
537
552
  
538
553
  /* Called whenever a service has been resolved successfully or
543
558
  case AVAHI_RESOLVER_FAILURE:
544
559
    fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
545
560
            " type '%s' in domain '%s': %s\n", name, type, domain,
546
 
            avahi_strerror(avahi_server_errno(server)));
 
561
            avahi_strerror(avahi_server_errno(mc->server)));
547
562
    break;
548
563
    
549
564
  case AVAHI_RESOLVER_FOUND:
551
566
      char ip[AVAHI_ADDRESS_STR_MAX];
552
567
      avahi_address_snprint(ip, sizeof(ip), address);
553
568
      if(debug){
554
 
        fprintf(stderr, "Mandos server found on %s (%s) on port %d\n",
555
 
                host_name, ip, port);
 
569
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
 
570
                " port %d\n", name, host_name, ip, port);
556
571
      }
557
 
      int ret = start_mandos_communication(ip, port,
558
 
                                           (unsigned int) interface);
 
572
      int ret = start_mandos_communication(ip, port, interface, mc);
559
573
      if (ret == 0){
560
574
        exit(EXIT_SUCCESS);
561
 
      } else {
562
 
        exit(EXIT_FAILURE);
563
575
      }
564
576
    }
565
577
  }
566
578
  avahi_s_service_resolver_free(r);
567
579
}
568
580
 
569
 
static void browse_callback(
570
 
    AvahiSServiceBrowser *b,
571
 
    AvahiIfIndex interface,
572
 
    AvahiProtocol protocol,
573
 
    AvahiBrowserEvent event,
574
 
    const char *name,
575
 
    const char *type,
576
 
    const char *domain,
577
 
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
578
 
    void* userdata) {
579
 
    
580
 
    AvahiServer *s = userdata;
581
 
    assert(b);                  /* Spurious warning */
582
 
    
583
 
    /* Called whenever a new services becomes available on the LAN or
584
 
       is removed from the LAN */
585
 
    
586
 
    switch (event) {
587
 
    default:
588
 
    case AVAHI_BROWSER_FAILURE:
589
 
      
590
 
      fprintf(stderr, "(Browser) %s\n",
591
 
              avahi_strerror(avahi_server_errno(server)));
592
 
      avahi_simple_poll_quit(simple_poll);
593
 
      return;
594
 
      
595
 
    case AVAHI_BROWSER_NEW:
596
 
      /* We ignore the returned resolver object. In the callback
597
 
         function we free it. If the server is terminated before
598
 
         the callback function is called the server will free
599
 
         the resolver for us. */
600
 
      
601
 
      if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
602
 
                                         type, domain,
603
 
                                         AVAHI_PROTO_INET6, 0,
604
 
                                         resolve_callback, s)))
605
 
        fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
606
 
                avahi_strerror(avahi_server_errno(s)));
607
 
      break;
608
 
      
609
 
    case AVAHI_BROWSER_REMOVE:
610
 
      break;
611
 
      
612
 
    case AVAHI_BROWSER_ALL_FOR_NOW:
613
 
    case AVAHI_BROWSER_CACHE_EXHAUSTED:
614
 
      break;
615
 
    }
616
 
}
 
581
static void browse_callback( AvahiSServiceBrowser *b,
 
582
                             AvahiIfIndex interface,
 
583
                             AvahiProtocol protocol,
 
584
                             AvahiBrowserEvent event,
 
585
                             const char *name,
 
586
                             const char *type,
 
587
                             const char *domain,
 
588
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
 
589
                             void* userdata) {
 
590
  mandos_context *mc = userdata;
 
591
  assert(b);                    /* Spurious warning */
 
592
  
 
593
  /* Called whenever a new services becomes available on the LAN or
 
594
     is removed from the LAN */
 
595
  
 
596
  switch (event) {
 
597
  default:
 
598
  case AVAHI_BROWSER_FAILURE:
 
599
    
 
600
    fprintf(stderr, "(Browser) %s\n",
 
601
            avahi_strerror(avahi_server_errno(mc->server)));
 
602
    avahi_simple_poll_quit(mc->simple_poll);
 
603
    return;
 
604
    
 
605
  case AVAHI_BROWSER_NEW:
 
606
    /* We ignore the returned resolver object. In the callback
 
607
       function we free it. If the server is terminated before
 
608
       the callback function is called the server will free
 
609
       the resolver for us. */
 
610
    
 
611
    if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name,
 
612
                                       type, domain,
 
613
                                       AVAHI_PROTO_INET6, 0,
 
614
                                       resolve_callback, mc)))
 
615
      fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
 
616
              avahi_strerror(avahi_server_errno(mc->server)));
 
617
    break;
 
618
    
 
619
  case AVAHI_BROWSER_REMOVE:
 
620
    break;
 
621
    
 
622
  case AVAHI_BROWSER_ALL_FOR_NOW:
 
623
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
624
    break;
 
625
  }
 
626
}
 
627
 
 
628
/* Combines file name and path and returns the malloced new
 
629
   string. some sane checks could/should be added */
 
630
static const char *combinepath(const char *first, const char *second){
 
631
  size_t f_len = strlen(first);
 
632
  size_t s_len = strlen(second);
 
633
  char *tmp = malloc(f_len + s_len + 2);
 
634
  if (tmp == NULL){
 
635
    return NULL;
 
636
  }
 
637
  if(f_len > 0){
 
638
    memcpy(tmp, first, f_len);  /* Spurious warning */
 
639
  }
 
640
  tmp[f_len] = '/';
 
641
  if(s_len > 0){
 
642
    memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
 
643
  }
 
644
  tmp[f_len + 1 + s_len] = '\0';
 
645
  return tmp;
 
646
}
 
647
 
617
648
 
618
649
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
619
650
    AvahiServerConfig config;
620
651
    AvahiSServiceBrowser *sb = NULL;
621
652
    int error;
622
653
    int ret;
 
654
    int debug_int;
623
655
    int returncode = EXIT_SUCCESS;
624
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 = 1024, .priority = "SECURE256"};
625
663
    
 
664
    debug_int = debug ? 1 : 0;
626
665
    while (true){
627
 
      static struct option long_options[] = {
628
 
        {"debug", no_argument, (int *)&debug, 1},
629
 
        {"interface", required_argument, 0, 'i'},
 
666
      struct option long_options[] = {
 
667
        {"debug", no_argument, &debug_int, 1},
 
668
        {"connect", required_argument, NULL, 'c'},
 
669
        {"interface", required_argument, NULL, 'i'},
 
670
        {"keydir", required_argument, NULL, 'd'},
 
671
        {"seckey", required_argument, NULL, 's'},
 
672
        {"pubkey", required_argument, NULL, 'p'},
 
673
        {"dh-bits", required_argument, NULL, 'D'},
 
674
        {"priority", required_argument, NULL, 'P'},
630
675
        {0, 0, 0, 0} };
631
676
      
632
677
      int option_index = 0;
643
688
      case 'i':
644
689
        interface = optarg;
645
690
        break;
 
691
      case 'c':
 
692
        connect_to = optarg;
 
693
        break;
 
694
      case 'd':
 
695
        keydir = optarg;
 
696
        break;
 
697
      case 'p':
 
698
        pubkeyfile = optarg;
 
699
        break;
 
700
      case 's':
 
701
        seckeyfile = optarg;
 
702
        break;
 
703
      case 'D':
 
704
        errno = 0;
 
705
        mc.dh_bits = (unsigned int) strtol(optarg, NULL, 10);
 
706
        if (errno){
 
707
          perror("strtol");
 
708
          exit(EXIT_FAILURE);
 
709
        }
 
710
        break;
 
711
      case 'P':
 
712
        mc.priority = optarg;
 
713
        break;
 
714
      case '?':
646
715
      default:
647
716
        exit(EXIT_FAILURE);
648
717
      }
649
718
    }
 
719
    debug = debug_int ? true : false;
 
720
    
 
721
    pubkeyfile = combinepath(keydir, pubkeyfile);
 
722
    if (pubkeyfile == NULL){
 
723
      perror("combinepath");
 
724
      returncode = EXIT_FAILURE;
 
725
      goto exit;
 
726
    }
 
727
    
 
728
    seckeyfile = combinepath(keydir, seckeyfile);
 
729
    if (seckeyfile == NULL){
 
730
      perror("combinepath");
 
731
      goto exit;
 
732
    }
 
733
    
 
734
    if_index = (AvahiIfIndex) if_nametoindex(interface);
 
735
    if(if_index == 0){
 
736
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
 
737
      exit(EXIT_FAILURE);
 
738
    }
 
739
    
 
740
    if(connect_to != NULL){
 
741
      /* Connect directly, do not use Zeroconf */
 
742
      /* (Mainly meant for debugging) */
 
743
      char *address = strrchr(connect_to, ':');
 
744
      if(address == NULL){
 
745
        fprintf(stderr, "No colon in address\n");
 
746
        exit(EXIT_FAILURE);
 
747
      }
 
748
      errno = 0;
 
749
      uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
 
750
      if(errno){
 
751
        perror("Bad port number");
 
752
        exit(EXIT_FAILURE);
 
753
      }
 
754
      *address = '\0';
 
755
      address = connect_to;
 
756
      ret = start_mandos_communication(address, port, if_index, &mc);
 
757
      if(ret < 0){
 
758
        exit(EXIT_FAILURE);
 
759
      } else {
 
760
        exit(EXIT_SUCCESS);
 
761
      }
 
762
    }
 
763
    
 
764
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
765
    if(sd < 0) {
 
766
      perror("socket");
 
767
      returncode = EXIT_FAILURE;
 
768
      goto exit;
 
769
    }
 
770
    strcpy(network.ifr_name, interface); /* Spurious warning */
 
771
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
772
    if(ret == -1){
 
773
      
 
774
      perror("ioctl SIOCGIFFLAGS");
 
775
      returncode = EXIT_FAILURE;
 
776
      goto exit;
 
777
    }
 
778
    if((network.ifr_flags & IFF_UP) == 0){
 
779
      network.ifr_flags |= IFF_UP;
 
780
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
781
      if(ret == -1){
 
782
        perror("ioctl SIOCSIFFLAGS");
 
783
        returncode = EXIT_FAILURE;
 
784
        goto exit;
 
785
      }
 
786
    }
 
787
    close(sd);
650
788
    
651
789
    if (not debug){
652
790
      avahi_set_log_function(empty_log);
656
794
    srand((unsigned int) time(NULL));
657
795
 
658
796
    /* Allocate main loop object */
659
 
    if (!(simple_poll = avahi_simple_poll_new())) {
 
797
    if (!(mc.simple_poll = avahi_simple_poll_new())) {
660
798
        fprintf(stderr, "Failed to create simple poll object.\n");
661
 
        
 
799
        returncode = EXIT_FAILURE;
662
800
        goto exit;
663
801
    }
664
802
 
670
808
    config.publish_domain = 0;
671
809
 
672
810
    /* Allocate a new server */
673
 
    server = avahi_server_new(avahi_simple_poll_get(simple_poll),
674
 
                              &config, NULL, NULL, &error);
675
 
 
 
811
    mc.server=avahi_server_new(avahi_simple_poll_get(mc.simple_poll),
 
812
                               &config, NULL, NULL, &error);
 
813
    
676
814
    /* Free the configuration data */
677
815
    avahi_server_config_free(&config);
678
 
 
 
816
    
679
817
    /* Check if creating the server object succeeded */
680
 
    if (!server) {
 
818
    if (!mc.server) {
681
819
        fprintf(stderr, "Failed to create server: %s\n",
682
820
                avahi_strerror(error));
683
821
        returncode = EXIT_FAILURE;
685
823
    }
686
824
    
687
825
    /* Create the service browser */
688
 
    sb = avahi_s_service_browser_new(server,
689
 
                                     (AvahiIfIndex)
690
 
                                     if_nametoindex(interface),
 
826
    sb = avahi_s_service_browser_new(mc.server, if_index,
691
827
                                     AVAHI_PROTO_INET6,
692
828
                                     "_mandos._tcp", NULL, 0,
693
 
                                     browse_callback, server);
 
829
                                     browse_callback, &mc);
694
830
    if (!sb) {
695
831
        fprintf(stderr, "Failed to create service browser: %s\n",
696
 
                avahi_strerror(avahi_server_errno(server)));
 
832
                avahi_strerror(avahi_server_errno(mc.server)));
697
833
        returncode = EXIT_FAILURE;
698
834
        goto exit;
699
835
    }
704
840
      fprintf(stderr, "Starting avahi loop search\n");
705
841
    }
706
842
    
707
 
    avahi_simple_poll_loop(simple_poll);
 
843
    avahi_simple_poll_loop(mc.simple_poll);
708
844
    
709
845
 exit:
710
846
 
716
852
    if (sb)
717
853
        avahi_s_service_browser_free(sb);
718
854
    
719
 
    if (server)
720
 
        avahi_server_free(server);
721
 
 
722
 
    if (simple_poll)
723
 
        avahi_simple_poll_free(simple_poll);
724
 
 
 
855
    if (mc.server)
 
856
        avahi_server_free(mc.server);
 
857
 
 
858
    if (mc.simple_poll)
 
859
        avahi_simple_poll_free(mc.simple_poll);
 
860
    free(pubkeyfile);
 
861
    free(seckeyfile);
 
862
    
725
863
    return returncode;
726
864
}