/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-07-20 23:34:51 UTC
  • mto: This revision was merged to the branch mainline in revision 17.
  • Revision ID: belorn@braxen-20080720233451-720put8qyxqvk2ld
Added debug options from passprompt as --debug and --debug=passprompt

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8 -*- */
2
 
/*
3
 
 * Mandos client - get and decrypt data from a Mandos server
4
 
 *
5
 
 * This program is partly derived from an example program for an Avahi
6
 
 * service browser, downloaded from
7
 
 * <http://avahi.org/browser/examples/core-browse-services.c>.  This
8
 
 * includes the following functions: "resolve_callback",
9
 
 * "browse_callback", and parts of "main".
10
 
 * 
11
 
 * Everything else is
12
 
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
13
 
 * 
14
 
 * This program is free software: you can redistribute it and/or
15
 
 * modify it under the terms of the GNU General Public License as
16
 
 * published by the Free Software Foundation, either version 3 of the
17
 
 * License, or (at your option) any later version.
18
 
 * 
19
 
 * This program is distributed in the hope that it will be useful, but
20
 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
21
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22
 
 * General Public License for more details.
23
 
 * 
24
 
 * You should have received a copy of the GNU General Public License
25
 
 * along with this program.  If not, see
26
 
 * <http://www.gnu.org/licenses/>.
27
 
 * 
28
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
29
 
 */
 
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
***/
30
19
 
31
 
/* Needed by GPGME, specifically gpgme_data_seek() */
32
20
#define _LARGEFILE_SOURCE
33
21
#define _FILE_OFFSET_BITS 64
34
22
 
35
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
36
 
 
37
23
#include <stdio.h>
38
24
#include <assert.h>
39
25
#include <stdlib.h>
40
26
#include <time.h>
41
27
#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
44
28
 
45
29
#include <avahi-core/core.h>
46
30
#include <avahi-core/lookup.h>
50
34
#include <avahi-common/error.h>
51
35
 
52
36
//mandos client part
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 */
 
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 */
58
41
 
59
42
#include <unistd.h>             /* close() */
60
43
#include <netinet/in.h>
67
50
#include <errno.h>              /* perror() */
68
51
#include <gpgme.h>
69
52
 
70
 
// getopt long
71
 
#include <getopt.h>
72
53
 
 
54
#ifndef CERT_ROOT
 
55
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
 
56
#endif
 
57
#define CERTFILE CERT_ROOT "openpgp-client.txt"
 
58
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
73
59
#define BUFFER_SIZE 256
74
60
#define DH_BITS 1024
75
61
 
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
 
 
80
62
bool debug = false;
81
63
 
82
 
const char mandos_protocol_version[] = "1";
83
 
 
84
64
typedef struct {
85
 
  AvahiSimplePoll *simple_poll;
86
 
  AvahiServer *server;
 
65
  gnutls_session_t session;
87
66
  gnutls_certificate_credentials_t cred;
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){
 
67
  gnutls_dh_params_t dh_params;
 
68
} encrypted_session;
 
69
 
 
70
 
 
71
ssize_t gpg_packet_decrypt (char *packet, size_t packet_size, char **new_packet, char *homedir){
107
72
  gpgme_data_t dh_crypto, dh_plain;
108
73
  gpgme_ctx_t ctx;
109
74
  gpgme_error_t rc;
110
75
  ssize_t ret;
111
76
  size_t new_packet_capacity = 0;
112
 
  ssize_t new_packet_length = 0;
 
77
  size_t new_packet_length = 0;
113
78
  gpgme_engine_info_t engine_info;
114
79
 
115
80
  if (debug){
116
 
    fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
 
81
    fprintf(stderr, "Attempting to decrypt password from gpg packet\n");
117
82
  }
118
83
  
119
84
  /* Init GPGME */
120
85
  gpgme_check_version(NULL);
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
 
  }
 
86
  gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
127
87
  
128
88
  /* Set GPGME home directory */
129
89
  rc = gpgme_get_engine_info (&engine_info);
169
129
    return -1;
170
130
  }
171
131
  
172
 
  /* Decrypt data from the FILE pointer to the plaintext data
173
 
     buffer */
 
132
  /* Decrypt data from the FILE pointer to the plaintext data buffer */
174
133
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
175
134
  if (rc != GPG_ERR_NO_ERROR){
176
135
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
179
138
  }
180
139
 
181
140
  if(debug){
182
 
    fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
 
141
    fprintf(stderr, "decryption of gpg packet succeeded\n");
183
142
  }
184
143
 
185
144
  if (debug){
188
147
    if (result == NULL){
189
148
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
190
149
    } else {
191
 
      fprintf(stderr, "Unsupported algorithm: %s\n",
192
 
              result->unsupported_algorithm);
193
 
      fprintf(stderr, "Wrong key usage: %d\n",
194
 
              result->wrong_key_usage);
 
150
      fprintf(stderr, "Unsupported algorithm: %s\n", result->unsupported_algorithm);
 
151
      fprintf(stderr, "Wrong key usage: %d\n", result->wrong_key_usage);
195
152
      if(result->file_name != NULL){
196
153
        fprintf(stderr, "File name: %s\n", result->file_name);
197
154
      }
203
160
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
204
161
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
205
162
          fprintf(stderr, "Secret key available: %s\n",
206
 
                  recipient->status == GPG_ERR_NO_SECKEY
207
 
                  ? "No" : "Yes");
 
163
                  recipient->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
208
164
          recipient = recipient->next;
209
165
        }
210
166
      }
215
171
  gpgme_data_release(dh_crypto);
216
172
  
217
173
  /* Seek back to the beginning of the GPGME plaintext data buffer */
218
 
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
219
 
    perror("pgpme_data_seek");
220
 
  }
221
 
  
 
174
  gpgme_data_seek(dh_plain, 0, SEEK_SET);
 
175
 
222
176
  *new_packet = 0;
223
177
  while(true){
224
 
    new_packet_capacity = adjustbuffer(*new_packet, new_packet_length,
225
 
                                       new_packet_capacity);
226
 
    if (new_packet_capacity == 0){
227
 
        perror("adjustbuffer");
 
178
    if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
 
179
      *new_packet = realloc(*new_packet, new_packet_capacity + BUFFER_SIZE);
 
180
      if (*new_packet == NULL){
 
181
        perror("realloc");
228
182
        return -1;
229
183
      }
230
184
      new_packet_capacity += BUFFER_SIZE;
231
185
    }
232
186
    
233
 
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
234
 
                          BUFFER_SIZE);
 
187
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length, BUFFER_SIZE);
235
188
    /* Print the data, if any */
236
189
    if (ret == 0){
 
190
      /* If password is empty, then a incorrect error will be printed */
237
191
      break;
238
192
    }
239
193
    if(ret < 0){
243
197
    new_packet_length += ret;
244
198
  }
245
199
 
246
 
  /* FIXME: check characters before printing to screen so to not print
247
 
     terminal control characters */
248
 
  /*   if(debug){ */
249
 
  /*     fprintf(stderr, "decrypted password is: "); */
250
 
  /*     fwrite(*new_packet, 1, new_packet_length, stderr); */
251
 
  /*     fprintf(stderr, "\n"); */
252
 
  /*   } */
253
 
  
254
 
  /* Delete the GPGME plaintext data buffer */
 
200
  if(debug){
 
201
    fprintf(stderr, "decrypted password is: %s\n", *new_packet);
 
202
  }
 
203
 
 
204
   /* Delete the GPGME plaintext data buffer */
255
205
  gpgme_data_release(dh_plain);
256
206
  return new_packet_length;
257
207
}
263
213
  return ret;
264
214
}
265
215
 
266
 
static void debuggnutls(__attribute__((unused)) int level,
267
 
                        const char* string){
 
216
void debuggnutls(int level, const char* string){
268
217
  fprintf(stderr, "%s", string);
269
218
}
270
219
 
271
 
static int initgnutls(mandos_context *mc){
 
220
int initgnutls(encrypted_session *es){
272
221
  const char *err;
273
222
  int ret;
274
 
  
 
223
 
275
224
  if(debug){
276
 
    fprintf(stderr, "Initializing GnuTLS\n");
 
225
    fprintf(stderr, "Initializing gnutls\n");
277
226
  }
278
227
 
 
228
  
279
229
  if ((ret = gnutls_global_init ())
280
230
      != GNUTLS_E_SUCCESS) {
281
231
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
287
237
    gnutls_global_set_log_function(debuggnutls);
288
238
  }
289
239
  
 
240
 
290
241
  /* openpgp credentials */
291
242
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
292
243
      != GNUTLS_E_SUCCESS) {
293
 
    fprintf (stderr, "memory error: %s\n",
294
 
             safer_gnutls_strerror(ret));
 
244
    fprintf (stderr, "memory error: %s\n", safer_gnutls_strerror(ret));
295
245
    return -1;
296
246
  }
297
 
  
 
247
 
298
248
  if(debug){
299
 
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
300
 
            " and keyfile %s as GnuTLS credentials\n", certfile,
301
 
            certkey);
 
249
    fprintf(stderr, "Attempting to use openpgp certificate %s"
 
250
            " and keyfile %s as gnutls credentials\n", CERTFILE, KEYFILE);
302
251
  }
303
 
  
 
252
 
304
253
  ret = gnutls_certificate_set_openpgp_key_file
305
 
    (es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
 
254
    (es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
306
255
  if (ret != GNUTLS_E_SUCCESS) {
307
256
    fprintf
308
 
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
309
 
       " '%s')\n",
310
 
       ret, certfile, certkey);
 
257
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s', '%s')\n",
 
258
       ret, CERTFILE, KEYFILE);
311
259
    fprintf(stdout, "The Error is: %s\n",
312
260
            safer_gnutls_strerror(ret));
313
261
    return -1;
314
262
  }
315
 
  
316
 
  //GnuTLS server initialization
 
263
 
 
264
  //Gnutls server initialization
317
265
  if ((ret = gnutls_dh_params_init (&es->dh_params))
318
266
      != GNUTLS_E_SUCCESS) {
319
267
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
320
268
             safer_gnutls_strerror(ret));
321
269
    return -1;
322
270
  }
323
 
  
 
271
 
324
272
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
325
273
      != GNUTLS_E_SUCCESS) {
326
274
    fprintf (stderr, "Error in prime generation: %s\n",
327
275
             safer_gnutls_strerror(ret));
328
276
    return -1;
329
277
  }
330
 
  
 
278
 
331
279
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
332
 
  
333
 
  // GnuTLS session creation
 
280
 
 
281
  // Gnutls session creation
334
282
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
335
283
      != GNUTLS_E_SUCCESS){
336
 
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
 
284
    fprintf(stderr, "Error in gnutls session initialization: %s\n",
337
285
            safer_gnutls_strerror(ret));
338
286
  }
339
 
  
340
 
  if ((ret = gnutls_priority_set_direct (es->session, mc->priority, &err))
 
287
 
 
288
  if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
341
289
      != GNUTLS_E_SUCCESS) {
342
290
    fprintf(stderr, "Syntax error at: %s\n", err);
343
 
    fprintf(stderr, "GnuTLS error: %s\n",
 
291
    fprintf(stderr, "Gnutls error: %s\n",
344
292
            safer_gnutls_strerror(ret));
345
293
    return -1;
346
294
  }
347
 
  
 
295
 
348
296
  if ((ret = gnutls_credentials_set
349
297
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
350
298
      != GNUTLS_E_SUCCESS) {
352
300
            safer_gnutls_strerror(ret));
353
301
    return -1;
354
302
  }
355
 
  
 
303
 
356
304
  /* ignore client certificate if any. */
357
 
  gnutls_certificate_server_set_request (es->session,
358
 
                                         GNUTLS_CERT_IGNORE);
 
305
  gnutls_certificate_server_set_request (es->session, GNUTLS_CERT_IGNORE);
359
306
  
360
307
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
361
308
  
362
309
  return 0;
363
310
}
364
311
 
365
 
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
366
 
                      __attribute__((unused)) const char *txt){}
 
312
void empty_log(AvahiLogLevel level, const char *txt){}
367
313
 
368
 
static int start_mandos_communication(const char *ip, uint16_t port,
369
 
                                      AvahiIfIndex if_index,
370
 
                                      mandos_context *mc){
 
314
int start_mandos_communcation(char *ip, uint16_t port){
371
315
  int ret, tcp_sd;
372
316
  struct sockaddr_in6 to;
 
317
  struct in6_addr ip_addr;
373
318
  encrypted_session es;
374
319
  char *buffer = NULL;
375
320
  char *decrypted_buffer;
376
321
  size_t buffer_length = 0;
377
322
  size_t buffer_capacity = 0;
378
323
  ssize_t decrypted_buffer_size;
379
 
  size_t written;
380
324
  int retval = 0;
381
 
  char interface[IF_NAMESIZE];
382
 
  
 
325
  const char interface[] = "eth0";
 
326
 
383
327
  if(debug){
384
 
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
385
 
            ip, port);
 
328
    fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
386
329
  }
387
330
  
388
331
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
392
335
  }
393
336
 
394
337
  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
 
    
402
338
    fprintf(stderr, "Binding to interface %s\n", interface);
403
339
  }
 
340
 
 
341
  ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, interface, 5);
 
342
  if(tcp_sd < 0) {
 
343
    perror("setsockopt bindtodevice");
 
344
    return -1;
 
345
  }
404
346
  
405
 
  memset(&to,0,sizeof(to));     /* Spurious warning */
 
347
  memset(&to,0,sizeof(to));
406
348
  to.sin6_family = AF_INET6;
407
 
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
 
349
  ret = inet_pton(AF_INET6, ip, &ip_addr);
408
350
  if (ret < 0 ){
409
351
    perror("inet_pton");
410
352
    return -1;
413
355
    fprintf(stderr, "Bad address: %s\n", ip);
414
356
    return -1;
415
357
  }
416
 
  to.sin6_port = htons(port);   /* Spurious warning */
417
 
  
418
 
  to.sin6_scope_id = (uint32_t)if_index;
419
 
  
 
358
  to.sin6_port = htons(port);
 
359
  to.sin6_scope_id = if_nametoindex(interface);
 
360
 
420
361
  if(debug){
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
 
/*     } */
 
362
    fprintf(stderr, "Connection to: %s\n", ip);
430
363
  }
431
364
  
432
365
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
434
367
    perror("connect");
435
368
    return -1;
436
369
  }
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
 
 
 
370
  
462
371
  ret = initgnutls (&es);
463
372
  if (ret != 0){
464
373
    retval = -1;
465
374
    return -1;
466
375
  }
467
 
  
468
 
  gnutls_transport_set_ptr (es.session,
469
 
                            (gnutls_transport_ptr_t) tcp_sd);
470
 
  
 
376
    
 
377
  
 
378
  gnutls_transport_set_ptr (es.session, (gnutls_transport_ptr_t) tcp_sd);
 
379
 
471
380
  if(debug){
472
 
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
 
381
    fprintf(stderr, "Establishing tls session with %s\n", ip);
473
382
  }
 
383
 
474
384
  
475
385
  ret = gnutls_handshake (es.session);
476
386
  
477
387
  if (ret != GNUTLS_E_SUCCESS){
478
 
    if(debug){
479
 
      fprintf(stderr, "\n*** Handshake failed ***\n");
480
 
      gnutls_perror (ret);
481
 
    }
 
388
    fprintf(stderr, "\n*** Handshake failed ***\n");
 
389
    gnutls_perror (ret);
482
390
    retval = -1;
483
391
    goto exit;
484
392
  }
485
 
  
486
 
  //Retrieve OpenPGP packet that contains the wanted password
487
 
  
 
393
 
 
394
  //Retrieve gpg packet that contains the wanted password
 
395
 
488
396
  if(debug){
489
 
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
490
 
            ip);
 
397
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n", ip);
491
398
  }
492
399
 
493
400
  while(true){
494
 
    buffer_capacity = adjustbuffer(buffer, buffer_length, buffer_capacity);
495
 
    if (buffer_capacity == 0){
496
 
      perror("adjustbuffer");
497
 
      retval = -1;
498
 
      goto exit;
 
401
    if (buffer_length + BUFFER_SIZE > buffer_capacity){
 
402
      buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
 
403
      if (buffer == NULL){
 
404
        perror("realloc");
 
405
        goto exit;
 
406
      }
 
407
      buffer_capacity += BUFFER_SIZE;
499
408
    }
500
409
    
501
410
    ret = gnutls_record_recv
518
427
        }
519
428
        break;
520
429
      default:
521
 
        fprintf(stderr, "Unknown error while reading data from"
522
 
                " encrypted session with mandos server\n");
 
430
        fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
523
431
        retval = -1;
524
432
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
525
433
        goto exit;
526
434
      }
527
435
    } else {
528
 
      buffer_length += (size_t) ret;
 
436
      buffer_length += ret;
529
437
    }
530
438
  }
531
439
  
532
440
  if (buffer_length > 0){
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
 
      }
 
441
    if ((decrypted_buffer_size = gpg_packet_decrypt(buffer, buffer_length, &decrypted_buffer, CERT_ROOT)) >= 0){
 
442
      fwrite (decrypted_buffer, 1, decrypted_buffer_size, stdout);
553
443
      free(decrypted_buffer);
554
444
    } else {
555
445
      retval = -1;
559
449
  //shutdown procedure
560
450
 
561
451
  if(debug){
562
 
    fprintf(stderr, "Closing TLS session\n");
 
452
    fprintf(stderr, "Closing tls session\n");
563
453
  }
564
454
 
565
455
  free(buffer);
572
462
  return retval;
573
463
}
574
464
 
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
 
 
 
465
static AvahiSimplePoll *simple_poll = NULL;
 
466
static AvahiServer *server = NULL;
 
467
 
 
468
static void resolve_callback(
 
469
    AvahiSServiceResolver *r,
 
470
    AVAHI_GCC_UNUSED AvahiIfIndex interface,
 
471
    AVAHI_GCC_UNUSED AvahiProtocol protocol,
 
472
    AvahiResolverEvent event,
 
473
    const char *name,
 
474
    const char *type,
 
475
    const char *domain,
 
476
    const char *host_name,
 
477
    const AvahiAddress *address,
 
478
    uint16_t port,
 
479
    AvahiStringList *txt,
 
480
    AvahiLookupResultFlags flags,
 
481
    AVAHI_GCC_UNUSED void* userdata) {
 
482
    
 
483
    assert(r);
 
484
 
 
485
    /* Called whenever a service has been resolved successfully or timed out */
 
486
 
 
487
    switch (event) {
 
488
        case AVAHI_RESOLVER_FAILURE:
 
489
            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)));
 
490
            break;
 
491
 
 
492
        case AVAHI_RESOLVER_FOUND: {
 
493
          char ip[AVAHI_ADDRESS_STR_MAX];
 
494
            avahi_address_snprint(ip, sizeof(ip), address);
 
495
            if(debug){
 
496
              fprintf(stderr, "Mandos server found at %s on port %d\n", ip, port);
 
497
            }
 
498
            int ret = start_mandos_communcation(ip, port);
 
499
            if (ret == 0){
 
500
              exit(EXIT_SUCCESS);
 
501
            } else {
 
502
              exit(EXIT_FAILURE);
 
503
            }
 
504
        }
 
505
    }
 
506
    avahi_s_service_resolver_free(r);
 
507
}
 
508
 
 
509
static void browse_callback(
 
510
    AvahiSServiceBrowser *b,
 
511
    AvahiIfIndex interface,
 
512
    AvahiProtocol protocol,
 
513
    AvahiBrowserEvent event,
 
514
    const char *name,
 
515
    const char *type,
 
516
    const char *domain,
 
517
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
 
518
    void* userdata) {
 
519
    
 
520
    AvahiServer *s = userdata;
 
521
    assert(b);
 
522
 
 
523
    /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
 
524
 
 
525
    switch (event) {
 
526
 
 
527
        case AVAHI_BROWSER_FAILURE:
 
528
            
 
529
            fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(server)));
 
530
            avahi_simple_poll_quit(simple_poll);
 
531
            return;
 
532
 
 
533
        case AVAHI_BROWSER_NEW:
 
534
            /* We ignore the returned resolver object. In the callback
 
535
               function we free it. If the server is terminated before
 
536
               the callback function is called the server will free
 
537
               the resolver for us. */
 
538
            
 
539
            if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_INET6, 0, resolve_callback, s)))
 
540
                fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(s)));
 
541
            
 
542
            break;
 
543
 
 
544
        case AVAHI_BROWSER_REMOVE:
 
545
            break;
 
546
 
 
547
        case AVAHI_BROWSER_ALL_FOR_NOW:
 
548
        case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
549
            break;
 
550
    }
 
551
}
686
552
 
687
553
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
688
554
    AvahiServerConfig config;
689
555
    AvahiSServiceBrowser *sb = NULL;
 
556
    const char db[] = "--debug";
690
557
    int error;
691
 
    int ret;
 
558
    int ret = 1;
692
559
    int returncode = EXIT_SUCCESS;
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
 
    
701
 
    while (true){
702
 
      static struct option long_options[] = {
703
 
        {"debug", no_argument, (int *)&debug, 1},
704
 
        {"connect", required_argument, 0, 'C'},
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'},
711
 
        {0, 0, 0, 0} };
712
 
      
713
 
      int option_index = 0;
714
 
      ret = getopt_long (argc, argv, "i:", long_options,
715
 
                         &option_index);
716
 
      
717
 
      if (ret == -1){
718
 
        break;
719
 
      }
720
 
      
721
 
      switch(ret){
722
 
      case 0:
723
 
        break;
724
 
      case 'i':
725
 
        interface = optarg;
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);
 
560
    char *basename = rindex(argv[0], '/');
 
561
    if(basename == NULL){
 
562
      basename = argv[0];
 
563
    } else {
 
564
      basename++;
 
565
    }
 
566
    
 
567
    char *program_name = malloc(strlen(basename) + sizeof(db));
 
568
 
 
569
    if (program_name == NULL){
 
570
      perror("argv[0]");
 
571
      return EXIT_FAILURE;
 
572
    }
 
573
    
 
574
    program_name[0] = '\0';
 
575
    
 
576
    for (int i = 1; i < argc; i++){
 
577
      if (not strncmp(argv[i], db, 5)){
 
578
          strcat(strcat(strcat(program_name, db ), "="), basename);
 
579
          if(not strcmp(argv[i], db) or not strcmp(argv[i], program_name)){
 
580
            debug = true;
747
581
          }
748
 
          mc.dh_bits = tmp;
749
582
        }
750
 
        break;
751
 
      case 'p':
752
 
        mc.priority = optarg;
753
 
        break;
754
 
      default:
755
 
        exit(EXIT_FAILURE);
756
 
      }
757
 
    }
758
 
    
759
 
    certfile = combinepath(certdir, certfile);
760
 
    if (certfile == NULL){
761
 
      perror("combinepath");
762
 
      returncode = EXIT_FAILURE;
763
 
      goto exit;
764
 
    }
 
583
    }
 
584
    free(program_name);
765
585
 
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
 
    
828
586
    if (not debug){
829
587
      avahi_set_log_function(empty_log);
830
588
    }
831
589
    
832
590
    /* Initialize the psuedo-RNG */
833
 
    srand((unsigned int) time(NULL));
 
591
    srand(time(NULL));
834
592
 
835
593
    /* Allocate main loop object */
836
 
    if (!(mc.simple_poll = avahi_simple_poll_new())) {
 
594
    if (!(simple_poll = avahi_simple_poll_new())) {
837
595
        fprintf(stderr, "Failed to create simple poll object.\n");
838
 
        returncode = EXIT_FAILURE;      
 
596
        
839
597
        goto exit;
840
598
    }
841
599
 
847
605
    config.publish_domain = 0;
848
606
 
849
607
    /* Allocate a new server */
850
 
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
851
 
                              &config, NULL, NULL, &error);
 
608
    server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
852
609
 
853
610
    /* Free the configuration data */
854
611
    avahi_server_config_free(&config);
855
612
 
856
613
    /* Check if creating the server object succeeded */
857
 
    if (!mc.server) {
858
 
        fprintf(stderr, "Failed to create server: %s\n",
859
 
                avahi_strerror(error));
 
614
    if (!server) {
 
615
        fprintf(stderr, "Failed to create server: %s\n", avahi_strerror(error));
860
616
        returncode = EXIT_FAILURE;
861
617
        goto exit;
862
618
    }
863
619
    
864
620
    /* Create the service browser */
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)));
 
621
    if (!(sb = avahi_s_service_browser_new(server, if_nametoindex("eth0"), AVAHI_PROTO_INET6, "_mandos._tcp", NULL, 0, browse_callback, server))) {
 
622
        fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_server_errno(server)));
872
623
        returncode = EXIT_FAILURE;
873
624
        goto exit;
874
625
    }
881
632
    
882
633
    avahi_simple_poll_loop(simple_poll);
883
634
    
884
 
 exit:
 
635
exit:
885
636
 
886
637
    if (debug){
887
638
      fprintf(stderr, "%s exiting\n", argv[0]);
891
642
    if (sb)
892
643
        avahi_s_service_browser_free(sb);
893
644
    
894
 
    if (mc.server)
895
 
        avahi_server_free(mc.server);
 
645
    if (server)
 
646
        avahi_server_free(server);
896
647
 
897
648
    if (simple_poll)
898
649
        avahi_simple_poll_free(simple_poll);
899
 
    free(certfile);
900
 
    free(certkey);
901
 
    
902
 
    return returncode;
 
650
 
 
651
    return ret;
903
652
}