/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: Björn Påhlsson
  • Date: 2008-08-03 16:08:20 UTC
  • mto: (237.7.1 mandos) (24.1.154 mandos)
  • mto: This revision was merged to the branch mainline in revision 41.
  • Revision ID: belorn@braxen-20080803160820-0tw2n3ufebh51e3i
Added support for protocol version handling

Show diffs side-by-side

added added

removed removed

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