/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

Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.

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