/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 03:33:56 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080803033356-6aemgj0g0hoz91ow
* plugins.d/mandosclient.c (pgp_packet_decrypt): Renamed variables.
                                                 On debug, show
                                                 decrypted plaintext
                                                 in hexadecimal.  Free
                                                 the GPGME data
                                                 buffers even on
                                                 errors.

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,
 
41
                                   SIOCSIFFLAGS */
 
42
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
 
43
                                   SIOCSIFFLAGS */
42
44
 
43
45
#include <avahi-core/core.h>
44
46
#include <avahi-core/lookup.h>
65
67
#include <errno.h>              /* perror() */
66
68
#include <gpgme.h>
67
69
 
68
 
// getopt long
 
70
// getopt_long
69
71
#include <getopt.h>
70
72
 
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
73
#define BUFFER_SIZE 256
77
 
#define DH_BITS 1024
 
74
 
 
75
static const char *keydir = "/conf/conf.d/mandos";
 
76
static const char *pubkeyfile = "pubkey.txt";
 
77
static const char *seckeyfile = "seckey.txt";
78
78
 
79
79
bool debug = false;
80
80
 
 
81
/* Used for passing in values through all the callback functions */
81
82
typedef struct {
82
 
  gnutls_session_t session;
 
83
  AvahiSimplePoll *simple_poll;
 
84
  AvahiServer *server;
83
85
  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){
 
86
  unsigned int dh_bits;
 
87
  const char *priority;
 
88
} mandos_context;
 
89
 
 
90
/* 
 
91
 * Decrypt OpenPGP data using keyrings in HOMEDIR.
 
92
 * Returns -1 on error
 
93
 */
 
94
static ssize_t pgp_packet_decrypt (const char *cryptotext,
 
95
                                   size_t crypto_size,
 
96
                                   char **plaintext,
 
97
                                   const char *homedir){
90
98
  gpgme_data_t dh_crypto, dh_plain;
91
99
  gpgme_ctx_t ctx;
92
100
  gpgme_error_t rc;
93
101
  ssize_t ret;
94
 
  ssize_t new_packet_capacity = 0;
95
 
  ssize_t new_packet_length = 0;
 
102
  ssize_t plaintext_capacity = 0;
 
103
  ssize_t plaintext_length = 0;
96
104
  gpgme_engine_info_t engine_info;
97
 
 
 
105
  
98
106
  if (debug){
99
 
    fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
 
107
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
100
108
  }
101
109
  
102
110
  /* Init GPGME */
103
111
  gpgme_check_version(NULL);
104
 
  gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
112
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
113
  if (rc != GPG_ERR_NO_ERROR){
 
114
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
 
115
            gpgme_strsource(rc), gpgme_strerror(rc));
 
116
    return -1;
 
117
  }
105
118
  
106
 
  /* Set GPGME home directory */
 
119
  /* Set GPGME home directory for the OpenPGP engine only */
107
120
  rc = gpgme_get_engine_info (&engine_info);
108
121
  if (rc != GPG_ERR_NO_ERROR){
109
122
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
119
132
    engine_info = engine_info->next;
120
133
  }
121
134
  if(engine_info == NULL){
122
 
    fprintf(stderr, "Could not set home dir to %s\n", homedir);
 
135
    fprintf(stderr, "Could not set GPGME home dir to %s\n", homedir);
123
136
    return -1;
124
137
  }
125
138
  
126
 
  /* Create new GPGME data buffer from packet buffer */
127
 
  rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
 
139
  /* Create new GPGME data buffer from memory cryptotext */
 
140
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
 
141
                               0);
128
142
  if (rc != GPG_ERR_NO_ERROR){
129
143
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
130
144
            gpgme_strsource(rc), gpgme_strerror(rc));
136
150
  if (rc != GPG_ERR_NO_ERROR){
137
151
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
138
152
            gpgme_strsource(rc), gpgme_strerror(rc));
 
153
    gpgme_data_release(dh_crypto);
139
154
    return -1;
140
155
  }
141
156
  
144
159
  if (rc != GPG_ERR_NO_ERROR){
145
160
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
146
161
            gpgme_strsource(rc), gpgme_strerror(rc));
147
 
    return -1;
 
162
    plaintext_length = -1;
 
163
    goto decrypt_end;
148
164
  }
149
165
  
150
 
  /* Decrypt data from the FILE pointer to the plaintext data
151
 
     buffer */
 
166
  /* Decrypt data from the cryptotext data buffer to the plaintext
 
167
     data buffer */
152
168
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
153
169
  if (rc != GPG_ERR_NO_ERROR){
154
170
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
155
171
            gpgme_strsource(rc), gpgme_strerror(rc));
156
 
    return -1;
 
172
    plaintext_length = -1;
 
173
    goto decrypt_end;
157
174
  }
158
 
 
 
175
  
159
176
  if(debug){
160
 
    fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
 
177
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
161
178
  }
162
 
 
 
179
  
163
180
  if (debug){
164
181
    gpgme_decrypt_result_t result;
165
182
    result = gpgme_op_decrypt_result(ctx);
189
206
    }
190
207
  }
191
208
  
192
 
  /* Delete the GPGME FILE pointer cryptotext data buffer */
193
 
  gpgme_data_release(dh_crypto);
194
 
  
195
209
  /* Seek back to the beginning of the GPGME plaintext data buffer */
196
 
  gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET);
197
 
 
198
 
  *new_packet = 0;
 
210
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
 
211
    perror("pgpme_data_seek");
 
212
    plaintext_length = -1;
 
213
    goto decrypt_end;
 
214
  }
 
215
  
 
216
  *plaintext = NULL;
199
217
  while(true){
200
 
    if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
201
 
      *new_packet = realloc(*new_packet,
202
 
                            (unsigned int)new_packet_capacity
 
218
    if (plaintext_length + BUFFER_SIZE > plaintext_capacity){
 
219
      *plaintext = realloc(*plaintext,
 
220
                            (unsigned int)plaintext_capacity
203
221
                            + BUFFER_SIZE);
204
 
      if (*new_packet == NULL){
 
222
      if (*plaintext == NULL){
205
223
        perror("realloc");
206
 
        return -1;
 
224
        plaintext_length = -1;
 
225
        goto decrypt_end;
207
226
      }
208
 
      new_packet_capacity += BUFFER_SIZE;
 
227
      plaintext_capacity += BUFFER_SIZE;
209
228
    }
210
229
    
211
 
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
 
230
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
212
231
                          BUFFER_SIZE);
213
232
    /* Print the data, if any */
214
233
    if (ret == 0){
 
234
      /* EOF */
215
235
      break;
216
236
    }
217
237
    if(ret < 0){
218
238
      perror("gpgme_data_read");
219
 
      return -1;
 
239
      plaintext_length = -1;
 
240
      goto decrypt_end;
220
241
    }
221
 
    new_packet_length += ret;
 
242
    plaintext_length += ret;
222
243
  }
223
244
 
224
 
  /* FIXME: check characters before printing to screen so to not print
225
 
     terminal control characters */
226
 
  /*   if(debug){ */
227
 
  /*     fprintf(stderr, "decrypted password is: "); */
228
 
  /*     fwrite(*new_packet, 1, new_packet_length, stderr); */
229
 
  /*     fprintf(stderr, "\n"); */
230
 
  /*   } */
 
245
  if(debug){
 
246
    fprintf(stderr, "Decrypted password is: ");
 
247
    for(size_t i = 0; i < plaintext_length; i++){
 
248
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
 
249
    }
 
250
    fprintf(stderr, "\n");
 
251
  }
 
252
  
 
253
 decrypt_end:
 
254
  
 
255
  /* Delete the GPGME cryptotext data buffer */
 
256
  gpgme_data_release(dh_crypto);
231
257
  
232
258
  /* Delete the GPGME plaintext data buffer */
233
259
  gpgme_data_release(dh_plain);
234
 
  return new_packet_length;
 
260
  return plaintext_length;
235
261
}
236
262
 
237
263
static const char * safer_gnutls_strerror (int value) {
241
267
  return ret;
242
268
}
243
269
 
244
 
void debuggnutls(__attribute__((unused)) int level,
245
 
                 const char* string){
 
270
static void debuggnutls(__attribute__((unused)) int level,
 
271
                        const char* string){
246
272
  fprintf(stderr, "%s", string);
247
273
}
248
274
 
249
 
int initgnutls(encrypted_session *es){
 
275
static int initgnutls(mandos_context *mc, gnutls_session_t *session,
 
276
                      gnutls_dh_params_t *dh_params){
250
277
  const char *err;
251
278
  int ret;
252
279
  
253
280
  if(debug){
254
281
    fprintf(stderr, "Initializing GnuTLS\n");
255
282
  }
256
 
  
 
283
 
257
284
  if ((ret = gnutls_global_init ())
258
285
      != GNUTLS_E_SUCCESS) {
259
286
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
260
287
    return -1;
261
288
  }
262
 
 
 
289
  
263
290
  if (debug){
264
291
    gnutls_global_set_log_level(11);
265
292
    gnutls_global_set_log_function(debuggnutls);
266
293
  }
267
294
  
268
295
  /* openpgp credentials */
269
 
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
 
296
  if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
270
297
      != GNUTLS_E_SUCCESS) {
271
298
    fprintf (stderr, "memory error: %s\n",
272
299
             safer_gnutls_strerror(ret));
275
302
  
276
303
  if(debug){
277
304
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
278
 
            " and keyfile %s as GnuTLS credentials\n", CERTFILE,
279
 
            KEYFILE);
 
305
            " and keyfile %s as GnuTLS credentials\n", pubkeyfile,
 
306
            seckeyfile);
280
307
  }
281
308
  
282
309
  ret = gnutls_certificate_set_openpgp_key_file
283
 
    (es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
 
310
    (mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
284
311
  if (ret != GNUTLS_E_SUCCESS) {
285
312
    fprintf
286
313
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
287
314
       " '%s')\n",
288
 
       ret, CERTFILE, KEYFILE);
 
315
       ret, pubkeyfile, seckeyfile);
289
316
    fprintf(stdout, "The Error is: %s\n",
290
317
            safer_gnutls_strerror(ret));
291
318
    return -1;
292
319
  }
293
320
  
294
321
  //GnuTLS server initialization
295
 
  if ((ret = gnutls_dh_params_init (&es->dh_params))
 
322
  if ((ret = gnutls_dh_params_init(dh_params))
296
323
      != GNUTLS_E_SUCCESS) {
297
324
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
298
325
             safer_gnutls_strerror(ret));
299
326
    return -1;
300
327
  }
301
328
  
302
 
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
 
329
  if ((ret = gnutls_dh_params_generate2(*dh_params, mc->dh_bits))
303
330
      != GNUTLS_E_SUCCESS) {
304
331
    fprintf (stderr, "Error in prime generation: %s\n",
305
332
             safer_gnutls_strerror(ret));
306
333
    return -1;
307
334
  }
308
335
  
309
 
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
 
336
  gnutls_certificate_set_dh_params(mc->cred, *dh_params);
310
337
  
311
338
  // GnuTLS session creation
312
 
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
 
339
  if ((ret = gnutls_init(session, GNUTLS_SERVER))
313
340
      != GNUTLS_E_SUCCESS){
314
341
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
315
342
            safer_gnutls_strerror(ret));
316
343
  }
317
344
  
318
 
  if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
 
345
  if ((ret = gnutls_priority_set_direct(*session, mc->priority, &err))
319
346
      != GNUTLS_E_SUCCESS) {
320
347
    fprintf(stderr, "Syntax error at: %s\n", err);
321
348
    fprintf(stderr, "GnuTLS error: %s\n",
323
350
    return -1;
324
351
  }
325
352
  
326
 
  if ((ret = gnutls_credentials_set
327
 
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
 
353
  if ((ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
354
                                    mc->cred))
328
355
      != GNUTLS_E_SUCCESS) {
329
356
    fprintf(stderr, "Error setting a credentials set: %s\n",
330
357
            safer_gnutls_strerror(ret));
332
359
  }
333
360
  
334
361
  /* ignore client certificate if any. */
335
 
  gnutls_certificate_server_set_request (es->session,
 
362
  gnutls_certificate_server_set_request (*session,
336
363
                                         GNUTLS_CERT_IGNORE);
337
364
  
338
 
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
 
365
  gnutls_dh_set_prime_bits (*session, mc->dh_bits);
339
366
  
340
367
  return 0;
341
368
}
342
369
 
343
 
void empty_log(__attribute__((unused)) AvahiLogLevel level,
344
 
               __attribute__((unused)) const char *txt){}
 
370
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
371
                      __attribute__((unused)) const char *txt){}
345
372
 
346
 
int start_mandos_communication(const char *ip, uint16_t port,
347
 
                               unsigned int if_index){
 
373
static int start_mandos_communication(const char *ip, uint16_t port,
 
374
                                      AvahiIfIndex if_index,
 
375
                                      mandos_context *mc){
348
376
  int ret, tcp_sd;
349
377
  struct sockaddr_in6 to;
350
 
  encrypted_session es;
351
378
  char *buffer = NULL;
352
379
  char *decrypted_buffer;
353
380
  size_t buffer_length = 0;
356
383
  size_t written = 0;
357
384
  int retval = 0;
358
385
  char interface[IF_NAMESIZE];
 
386
  gnutls_session_t session;
 
387
  gnutls_dh_params_t dh_params;
359
388
  
360
389
  if(debug){
361
 
    fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
 
390
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
 
391
            ip, port);
362
392
  }
363
393
  
364
394
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
366
396
    perror("socket");
367
397
    return -1;
368
398
  }
369
 
  
370
 
  if(if_indextoname(if_index, interface) == NULL){
371
 
    if(debug){
 
399
 
 
400
  if(debug){
 
401
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
372
402
      perror("if_indextoname");
 
403
      return -1;
373
404
    }
374
 
    return -1;
375
 
  }
376
 
  
377
 
  if(debug){
378
405
    fprintf(stderr, "Binding to interface %s\n", interface);
379
406
  }
380
407
  
384
411
  if (ret < 0 ){
385
412
    perror("inet_pton");
386
413
    return -1;
387
 
  }  
 
414
  }
388
415
  if(ret == 0){
389
416
    fprintf(stderr, "Bad address: %s\n", ip);
390
417
    return -1;
394
421
  to.sin6_scope_id = (uint32_t)if_index;
395
422
  
396
423
  if(debug){
397
 
    fprintf(stderr, "Connection to: %s\n", ip);
 
424
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
 
425
    char addrstr[INET6_ADDRSTRLEN] = "";
 
426
    if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr,
 
427
                 sizeof(addrstr)) == NULL){
 
428
      perror("inet_ntop");
 
429
    } else {
 
430
      if(strcmp(addrstr, ip) != 0){
 
431
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
 
432
      }
 
433
    }
398
434
  }
399
435
  
400
436
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
403
439
    return -1;
404
440
  }
405
441
  
406
 
  ret = initgnutls (&es);
 
442
  ret = initgnutls (mc, &session, &dh_params);
407
443
  if (ret != 0){
408
444
    retval = -1;
409
445
    return -1;
410
446
  }
411
447
  
412
 
  gnutls_transport_set_ptr (es.session,
413
 
                            (gnutls_transport_ptr_t) tcp_sd);
 
448
  gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
414
449
  
415
450
  if(debug){
416
451
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
417
452
  }
418
453
  
419
 
  ret = gnutls_handshake (es.session);
 
454
  ret = gnutls_handshake (session);
420
455
  
421
456
  if (ret != GNUTLS_E_SUCCESS){
422
457
    if(debug){
444
479
      buffer_capacity += BUFFER_SIZE;
445
480
    }
446
481
    
447
 
    ret = gnutls_record_recv
448
 
      (es.session, buffer+buffer_length, BUFFER_SIZE);
 
482
    ret = gnutls_record_recv(session, buffer+buffer_length,
 
483
                             BUFFER_SIZE);
449
484
    if (ret == 0){
450
485
      break;
451
486
    }
455
490
      case GNUTLS_E_AGAIN:
456
491
        break;
457
492
      case GNUTLS_E_REHANDSHAKE:
458
 
        ret = gnutls_handshake (es.session);
 
493
        ret = gnutls_handshake (session);
459
494
        if (ret < 0){
460
495
          fprintf(stderr, "\n*** Handshake failed ***\n");
461
496
          gnutls_perror (ret);
467
502
        fprintf(stderr, "Unknown error while reading data from"
468
503
                " encrypted session with mandos server\n");
469
504
        retval = -1;
470
 
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
 
505
        gnutls_bye (session, GNUTLS_SHUT_RDWR);
471
506
        goto exit;
472
507
      }
473
508
    } else {
479
514
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
480
515
                                               buffer_length,
481
516
                                               &decrypted_buffer,
482
 
                                               CERT_ROOT);
 
517
                                               keydir);
483
518
    if (decrypted_buffer_size >= 0){
484
 
      while(written < decrypted_buffer_size){
 
519
      while(written < (size_t) decrypted_buffer_size){
485
520
        ret = (int)fwrite (decrypted_buffer + written, 1,
486
521
                           (size_t)decrypted_buffer_size - written,
487
522
                           stdout);
508
543
  }
509
544
 
510
545
  free(buffer);
511
 
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
 
546
  gnutls_bye (session, GNUTLS_SHUT_RDWR);
512
547
 exit:
513
548
  close(tcp_sd);
514
 
  gnutls_deinit (es.session);
515
 
  gnutls_certificate_free_credentials (es.cred);
 
549
  gnutls_deinit (session);
 
550
  gnutls_certificate_free_credentials (mc->cred);
516
551
  gnutls_global_deinit ();
517
552
  return retval;
518
553
}
519
554
 
520
 
static AvahiSimplePoll *simple_poll = NULL;
521
 
static AvahiServer *server = NULL;
522
 
 
523
 
static void resolve_callback(
524
 
    AvahiSServiceResolver *r,
525
 
    AvahiIfIndex interface,
526
 
    AVAHI_GCC_UNUSED AvahiProtocol protocol,
527
 
    AvahiResolverEvent event,
528
 
    const char *name,
529
 
    const char *type,
530
 
    const char *domain,
531
 
    const char *host_name,
532
 
    const AvahiAddress *address,
533
 
    uint16_t port,
534
 
    AVAHI_GCC_UNUSED AvahiStringList *txt,
535
 
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
536
 
    AVAHI_GCC_UNUSED void* userdata) {
537
 
    
 
555
static void resolve_callback(AvahiSServiceResolver *r,
 
556
                             AvahiIfIndex interface,
 
557
                             AVAHI_GCC_UNUSED AvahiProtocol protocol,
 
558
                             AvahiResolverEvent event,
 
559
                             const char *name,
 
560
                             const char *type,
 
561
                             const char *domain,
 
562
                             const char *host_name,
 
563
                             const AvahiAddress *address,
 
564
                             uint16_t port,
 
565
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
 
566
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
567
                             flags,
 
568
                             void* userdata) {
 
569
  mandos_context *mc = userdata;
538
570
  assert(r);                    /* Spurious warning */
539
571
  
540
572
  /* Called whenever a service has been resolved successfully or
545
577
  case AVAHI_RESOLVER_FAILURE:
546
578
    fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
547
579
            " type '%s' in domain '%s': %s\n", name, type, domain,
548
 
            avahi_strerror(avahi_server_errno(server)));
 
580
            avahi_strerror(avahi_server_errno(mc->server)));
549
581
    break;
550
582
    
551
583
  case AVAHI_RESOLVER_FOUND:
556
588
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
557
589
                " port %d\n", name, host_name, ip, port);
558
590
      }
559
 
      int ret = start_mandos_communication(ip, port,
560
 
                                           (unsigned int) interface);
 
591
      int ret = start_mandos_communication(ip, port, interface, mc);
561
592
      if (ret == 0){
562
593
        exit(EXIT_SUCCESS);
563
594
      }
566
597
  avahi_s_service_resolver_free(r);
567
598
}
568
599
 
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
 
}
 
600
static void browse_callback( AvahiSServiceBrowser *b,
 
601
                             AvahiIfIndex interface,
 
602
                             AvahiProtocol protocol,
 
603
                             AvahiBrowserEvent event,
 
604
                             const char *name,
 
605
                             const char *type,
 
606
                             const char *domain,
 
607
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
608
                             flags,
 
609
                             void* userdata) {
 
610
  mandos_context *mc = userdata;
 
611
  assert(b);                    /* Spurious warning */
 
612
  
 
613
  /* Called whenever a new services becomes available on the LAN or
 
614
     is removed from the LAN */
 
615
  
 
616
  switch (event) {
 
617
  default:
 
618
  case AVAHI_BROWSER_FAILURE:
 
619
    
 
620
    fprintf(stderr, "(Browser) %s\n",
 
621
            avahi_strerror(avahi_server_errno(mc->server)));
 
622
    avahi_simple_poll_quit(mc->simple_poll);
 
623
    return;
 
624
    
 
625
  case AVAHI_BROWSER_NEW:
 
626
    /* We ignore the returned resolver object. In the callback
 
627
       function we free it. If the server is terminated before
 
628
       the callback function is called the server will free
 
629
       the resolver for us. */
 
630
    
 
631
    if (!(avahi_s_service_resolver_new(mc->server, interface,
 
632
                                       protocol, name, type, domain,
 
633
                                       AVAHI_PROTO_INET6, 0,
 
634
                                       resolve_callback, mc)))
 
635
      fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
 
636
              avahi_strerror(avahi_server_errno(mc->server)));
 
637
    break;
 
638
    
 
639
  case AVAHI_BROWSER_REMOVE:
 
640
    break;
 
641
    
 
642
  case AVAHI_BROWSER_ALL_FOR_NOW:
 
643
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
644
    break;
 
645
  }
 
646
}
 
647
 
 
648
/* Combines file name and path and returns the malloced new
 
649
   string. some sane checks could/should be added */
 
650
static const char *combinepath(const char *first, const char *second){
 
651
  size_t f_len = strlen(first);
 
652
  size_t s_len = strlen(second);
 
653
  char *tmp = malloc(f_len + s_len + 2);
 
654
  if (tmp == NULL){
 
655
    return NULL;
 
656
  }
 
657
  if(f_len > 0){
 
658
    memcpy(tmp, first, f_len);  /* Spurious warning */
 
659
  }
 
660
  tmp[f_len] = '/';
 
661
  if(s_len > 0){
 
662
    memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
 
663
  }
 
664
  tmp[f_len + 1 + s_len] = '\0';
 
665
  return tmp;
 
666
}
 
667
 
617
668
 
618
669
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
619
670
    AvahiServerConfig config;
620
671
    AvahiSServiceBrowser *sb = NULL;
621
672
    int error;
622
673
    int ret;
 
674
    int debug_int;
623
675
    int returncode = EXIT_SUCCESS;
624
676
    const char *interface = "eth0";
 
677
    struct ifreq network;
 
678
    int sd;
 
679
    char *connect_to = NULL;
 
680
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
681
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
 
682
                          .dh_bits = 1024, .priority = "SECURE256"};
625
683
    
 
684
    debug_int = debug ? 1 : 0;
626
685
    while (true){
627
 
      static struct option long_options[] = {
628
 
        {"debug", no_argument, (int *)&debug, 1},
629
 
        {"interface", required_argument, 0, 'i'},
 
686
      struct option long_options[] = {
 
687
        {"debug", no_argument, &debug_int, 1},
 
688
        {"connect", required_argument, NULL, 'c'},
 
689
        {"interface", required_argument, NULL, 'i'},
 
690
        {"keydir", required_argument, NULL, 'd'},
 
691
        {"seckey", required_argument, NULL, 's'},
 
692
        {"pubkey", required_argument, NULL, 'p'},
 
693
        {"dh-bits", required_argument, NULL, 'D'},
 
694
        {"priority", required_argument, NULL, 'P'},
630
695
        {0, 0, 0, 0} };
631
696
      
632
697
      int option_index = 0;
643
708
      case 'i':
644
709
        interface = optarg;
645
710
        break;
 
711
      case 'c':
 
712
        connect_to = optarg;
 
713
        break;
 
714
      case 'd':
 
715
        keydir = optarg;
 
716
        break;
 
717
      case 'p':
 
718
        pubkeyfile = optarg;
 
719
        break;
 
720
      case 's':
 
721
        seckeyfile = optarg;
 
722
        break;
 
723
      case 'D':
 
724
        errno = 0;
 
725
        mc.dh_bits = (unsigned int) strtol(optarg, NULL, 10);
 
726
        if (errno){
 
727
          perror("strtol");
 
728
          exit(EXIT_FAILURE);
 
729
        }
 
730
        break;
 
731
      case 'P':
 
732
        mc.priority = optarg;
 
733
        break;
 
734
      case '?':
646
735
      default:
647
736
        exit(EXIT_FAILURE);
648
737
      }
649
738
    }
 
739
    debug = debug_int ? true : false;
 
740
    
 
741
    pubkeyfile = combinepath(keydir, pubkeyfile);
 
742
    if (pubkeyfile == NULL){
 
743
      perror("combinepath");
 
744
      returncode = EXIT_FAILURE;
 
745
      goto exit;
 
746
    }
 
747
    
 
748
    seckeyfile = combinepath(keydir, seckeyfile);
 
749
    if (seckeyfile == NULL){
 
750
      perror("combinepath");
 
751
      goto exit;
 
752
    }
 
753
    
 
754
    if_index = (AvahiIfIndex) if_nametoindex(interface);
 
755
    if(if_index == 0){
 
756
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
 
757
      exit(EXIT_FAILURE);
 
758
    }
 
759
    
 
760
    if(connect_to != NULL){
 
761
      /* Connect directly, do not use Zeroconf */
 
762
      /* (Mainly meant for debugging) */
 
763
      char *address = strrchr(connect_to, ':');
 
764
      if(address == NULL){
 
765
        fprintf(stderr, "No colon in address\n");
 
766
        exit(EXIT_FAILURE);
 
767
      }
 
768
      errno = 0;
 
769
      uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
 
770
      if(errno){
 
771
        perror("Bad port number");
 
772
        exit(EXIT_FAILURE);
 
773
      }
 
774
      *address = '\0';
 
775
      address = connect_to;
 
776
      ret = start_mandos_communication(address, port, if_index, &mc);
 
777
      if(ret < 0){
 
778
        exit(EXIT_FAILURE);
 
779
      } else {
 
780
        exit(EXIT_SUCCESS);
 
781
      }
 
782
    }
 
783
    
 
784
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
785
    if(sd < 0) {
 
786
      perror("socket");
 
787
      returncode = EXIT_FAILURE;
 
788
      goto exit;
 
789
    }
 
790
    strcpy(network.ifr_name, interface); /* Spurious warning */
 
791
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
792
    if(ret == -1){
 
793
      
 
794
      perror("ioctl SIOCGIFFLAGS");
 
795
      returncode = EXIT_FAILURE;
 
796
      goto exit;
 
797
    }
 
798
    if((network.ifr_flags & IFF_UP) == 0){
 
799
      network.ifr_flags |= IFF_UP;
 
800
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
801
      if(ret == -1){
 
802
        perror("ioctl SIOCSIFFLAGS");
 
803
        returncode = EXIT_FAILURE;
 
804
        goto exit;
 
805
      }
 
806
    }
 
807
    close(sd);
650
808
    
651
809
    if (not debug){
652
810
      avahi_set_log_function(empty_log);
656
814
    srand((unsigned int) time(NULL));
657
815
 
658
816
    /* Allocate main loop object */
659
 
    if (!(simple_poll = avahi_simple_poll_new())) {
 
817
    if (!(mc.simple_poll = avahi_simple_poll_new())) {
660
818
        fprintf(stderr, "Failed to create simple poll object.\n");
661
 
        
 
819
        returncode = EXIT_FAILURE;
662
820
        goto exit;
663
821
    }
664
822
 
670
828
    config.publish_domain = 0;
671
829
 
672
830
    /* Allocate a new server */
673
 
    server = avahi_server_new(avahi_simple_poll_get(simple_poll),
674
 
                              &config, NULL, NULL, &error);
675
 
 
 
831
    mc.server=avahi_server_new(avahi_simple_poll_get(mc.simple_poll),
 
832
                               &config, NULL, NULL, &error);
 
833
    
676
834
    /* Free the configuration data */
677
835
    avahi_server_config_free(&config);
678
 
 
 
836
    
679
837
    /* Check if creating the server object succeeded */
680
 
    if (!server) {
 
838
    if (!mc.server) {
681
839
        fprintf(stderr, "Failed to create server: %s\n",
682
840
                avahi_strerror(error));
683
841
        returncode = EXIT_FAILURE;
685
843
    }
686
844
    
687
845
    /* Create the service browser */
688
 
    sb = avahi_s_service_browser_new(server,
689
 
                                     (AvahiIfIndex)
690
 
                                     if_nametoindex(interface),
 
846
    sb = avahi_s_service_browser_new(mc.server, if_index,
691
847
                                     AVAHI_PROTO_INET6,
692
848
                                     "_mandos._tcp", NULL, 0,
693
 
                                     browse_callback, server);
 
849
                                     browse_callback, &mc);
694
850
    if (!sb) {
695
851
        fprintf(stderr, "Failed to create service browser: %s\n",
696
 
                avahi_strerror(avahi_server_errno(server)));
 
852
                avahi_strerror(avahi_server_errno(mc.server)));
697
853
        returncode = EXIT_FAILURE;
698
854
        goto exit;
699
855
    }
704
860
      fprintf(stderr, "Starting avahi loop search\n");
705
861
    }
706
862
    
707
 
    avahi_simple_poll_loop(simple_poll);
 
863
    avahi_simple_poll_loop(mc.simple_poll);
708
864
    
709
865
 exit:
710
866
 
716
872
    if (sb)
717
873
        avahi_s_service_browser_free(sb);
718
874
    
719
 
    if (server)
720
 
        avahi_server_free(server);
721
 
 
722
 
    if (simple_poll)
723
 
        avahi_simple_poll_free(simple_poll);
724
 
 
 
875
    if (mc.server)
 
876
        avahi_server_free(mc.server);
 
877
 
 
878
    if (mc.simple_poll)
 
879
        avahi_simple_poll_free(mc.simple_poll);
 
880
    free(pubkeyfile);
 
881
    free(seckeyfile);
 
882
    
725
883
    return returncode;
726
884
}