/mandos/trunk

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

« back to all changes in this revision

Viewing changes to plugins.d/mandosclient.c

merge commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
39
39
#include <stdlib.h>
40
40
#include <time.h>
41
41
#include <net/if.h>             /* if_nametoindex */
42
 
#include <sys/ioctl.h>          /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
43
 
                                   SIOCSIFFLAGS */
44
 
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
45
 
                                   SIOCSIFFLAGS */
 
42
#include <sys/ioctl.h>          // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
 
43
#include <net/if.h>             // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
46
44
 
47
45
#include <avahi-core/core.h>
48
46
#include <avahi-core/lookup.h>
51
49
#include <avahi-common/malloc.h>
52
50
#include <avahi-common/error.h>
53
51
 
54
 
/* Mandos client part */
 
52
//mandos client part
55
53
#include <sys/types.h>          /* socket(), inet_pton() */
56
54
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
57
55
                                   struct in6_addr, inet_pton() */
64
62
#include <string.h>             /* memset */
65
63
#include <arpa/inet.h>          /* inet_pton() */
66
64
#include <iso646.h>             /* not */
67
 
#include <net/if.h>             /* IF_NAMESIZE */
68
 
#include <argp.h>               /* struct argp_option,
69
 
                                   struct argp_state, struct argp,
70
 
                                   argp_parse() */
71
 
/* GPGME */
 
65
 
 
66
// gpgme
72
67
#include <errno.h>              /* perror() */
73
68
#include <gpgme.h>
74
69
 
 
70
// getopt long
 
71
#include <getopt.h>
 
72
 
75
73
#define BUFFER_SIZE 256
 
74
#define DH_BITS 1024
 
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";
76
79
 
77
80
bool debug = false;
78
 
const char *keydir = "/conf/conf.d/mandos";
79
 
const char *argp_program_version = "mandosclient 0.9";
80
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
81
 
81
82
const char mandos_protocol_version[] = "1";
82
83
 
83
 
/* Used for passing in values through all the callback functions */
84
84
typedef struct {
85
85
  AvahiSimplePoll *simple_poll;
86
86
  AvahiServer *server;
87
87
  gnutls_certificate_credentials_t cred;
88
88
  unsigned int dh_bits;
89
 
  gnutls_dh_params_t dh_params;
90
89
  const char *priority;
91
90
} mandos_context;
92
91
 
93
 
size_t adjustbuffer(char **buffer, size_t buffer_length,
 
92
size_t adjustbuffer(char *buffer, size_t buffer_length,
94
93
                  size_t buffer_capacity){
95
94
  if (buffer_length + BUFFER_SIZE > buffer_capacity){
96
 
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
95
    buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
97
96
    if (buffer == NULL){
98
97
      return 0;
99
98
    }
102
101
  return buffer_capacity;
103
102
}
104
103
 
105
 
/* 
106
 
 * Decrypt OpenPGP data using keyrings in HOMEDIR.
107
 
 * Returns -1 on error
108
 
 */
109
 
static ssize_t pgp_packet_decrypt (const char *cryptotext,
110
 
                                   size_t crypto_size,
111
 
                                   char **plaintext,
 
104
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
 
105
                                   char **new_packet,
112
106
                                   const char *homedir){
113
107
  gpgme_data_t dh_crypto, dh_plain;
114
108
  gpgme_ctx_t ctx;
115
109
  gpgme_error_t rc;
116
110
  ssize_t ret;
117
 
  size_t plaintext_capacity = 0;
118
 
  ssize_t plaintext_length = 0;
 
111
  size_t new_packet_capacity = 0;
 
112
  ssize_t new_packet_length = 0;
119
113
  gpgme_engine_info_t engine_info;
120
 
  
 
114
 
121
115
  if (debug){
122
 
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
 
116
    fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
123
117
  }
124
118
  
125
119
  /* Init GPGME */
131
125
    return -1;
132
126
  }
133
127
  
134
 
  /* Set GPGME home directory for the OpenPGP engine only */
 
128
  /* Set GPGME home directory */
135
129
  rc = gpgme_get_engine_info (&engine_info);
136
130
  if (rc != GPG_ERR_NO_ERROR){
137
131
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
147
141
    engine_info = engine_info->next;
148
142
  }
149
143
  if(engine_info == NULL){
150
 
    fprintf(stderr, "Could not set GPGME home dir to %s\n", homedir);
 
144
    fprintf(stderr, "Could not set home dir to %s\n", homedir);
151
145
    return -1;
152
146
  }
153
147
  
154
 
  /* Create new GPGME data buffer from memory cryptotext */
155
 
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
156
 
                               0);
 
148
  /* Create new GPGME data buffer from packet buffer */
 
149
  rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
157
150
  if (rc != GPG_ERR_NO_ERROR){
158
151
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
159
152
            gpgme_strsource(rc), gpgme_strerror(rc));
165
158
  if (rc != GPG_ERR_NO_ERROR){
166
159
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
167
160
            gpgme_strsource(rc), gpgme_strerror(rc));
168
 
    gpgme_data_release(dh_crypto);
169
161
    return -1;
170
162
  }
171
163
  
174
166
  if (rc != GPG_ERR_NO_ERROR){
175
167
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
176
168
            gpgme_strsource(rc), gpgme_strerror(rc));
177
 
    plaintext_length = -1;
178
 
    goto decrypt_end;
 
169
    return -1;
179
170
  }
180
171
  
181
 
  /* Decrypt data from the cryptotext data buffer to the plaintext
182
 
     data buffer */
 
172
  /* Decrypt data from the FILE pointer to the plaintext data
 
173
     buffer */
183
174
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
184
175
  if (rc != GPG_ERR_NO_ERROR){
185
176
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
186
177
            gpgme_strsource(rc), gpgme_strerror(rc));
187
 
    plaintext_length = -1;
188
 
    goto decrypt_end;
 
178
    return -1;
189
179
  }
190
 
  
 
180
 
191
181
  if(debug){
192
 
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
 
182
    fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
193
183
  }
194
 
  
 
184
 
195
185
  if (debug){
196
186
    gpgme_decrypt_result_t result;
197
187
    result = gpgme_op_decrypt_result(ctx);
221
211
    }
222
212
  }
223
213
  
 
214
  /* Delete the GPGME FILE pointer cryptotext data buffer */
 
215
  gpgme_data_release(dh_crypto);
 
216
  
224
217
  /* Seek back to the beginning of the GPGME plaintext data buffer */
225
218
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
226
219
    perror("pgpme_data_seek");
227
 
    plaintext_length = -1;
228
 
    goto decrypt_end;
229
220
  }
230
221
  
231
 
  *plaintext = NULL;
 
222
  *new_packet = 0;
232
223
  while(true){
233
 
    plaintext_capacity = adjustbuffer(plaintext, (size_t)plaintext_length,
234
 
                                      plaintext_capacity);
235
 
    if (plaintext_capacity == 0){
 
224
    new_packet_capacity = adjustbuffer(*new_packet, new_packet_length,
 
225
                                       new_packet_capacity);
 
226
    if (new_packet_capacity == 0){
236
227
        perror("adjustbuffer");
237
 
        plaintext_length = -1;
238
 
        goto decrypt_end;
 
228
        return -1;
 
229
      }
 
230
      new_packet_capacity += BUFFER_SIZE;
239
231
    }
240
232
    
241
 
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
 
233
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
242
234
                          BUFFER_SIZE);
243
235
    /* Print the data, if any */
244
236
    if (ret == 0){
245
 
      /* EOF */
246
237
      break;
247
238
    }
248
239
    if(ret < 0){
249
240
      perror("gpgme_data_read");
250
 
      plaintext_length = -1;
251
 
      goto decrypt_end;
 
241
      return -1;
252
242
    }
253
 
    plaintext_length += ret;
 
243
    new_packet_length += ret;
254
244
  }
255
245
 
256
 
  if(debug){
257
 
    fprintf(stderr, "Decrypted password is: ");
258
 
    for(ssize_t i = 0; i < plaintext_length; i++){
259
 
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
260
 
    }
261
 
    fprintf(stderr, "\n");
262
 
  }
263
 
  
264
 
 decrypt_end:
265
 
  
266
 
  /* Delete the GPGME cryptotext data buffer */
267
 
  gpgme_data_release(dh_crypto);
 
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
  /*   } */
268
253
  
269
254
  /* Delete the GPGME plaintext data buffer */
270
255
  gpgme_data_release(dh_plain);
271
 
  return plaintext_length;
 
256
  return new_packet_length;
272
257
}
273
258
 
274
259
static const char * safer_gnutls_strerror (int value) {
278
263
  return ret;
279
264
}
280
265
 
281
 
/* GnuTLS log function callback */
282
266
static void debuggnutls(__attribute__((unused)) int level,
283
267
                        const char* string){
284
 
  fprintf(stderr, "GnuTLS: %s", string);
 
268
  fprintf(stderr, "%s", string);
285
269
}
286
270
 
287
 
static int init_gnutls_global(mandos_context *mc,
288
 
                              const char *pubkeyfile,
289
 
                              const char *seckeyfile){
 
271
static int initgnutls(mandos_context *mc){
 
272
  const char *err;
290
273
  int ret;
291
274
  
292
275
  if(debug){
295
278
 
296
279
  if ((ret = gnutls_global_init ())
297
280
      != GNUTLS_E_SUCCESS) {
298
 
    fprintf (stderr, "GnuTLS global_init: %s\n",
299
 
             safer_gnutls_strerror(ret));
 
281
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
300
282
    return -1;
301
283
  }
302
 
  
 
284
 
303
285
  if (debug){
304
 
    /* "Use a log level over 10 to enable all debugging options."
305
 
     * - GnuTLS manual
306
 
     */
307
286
    gnutls_global_set_log_level(11);
308
287
    gnutls_global_set_log_function(debuggnutls);
309
288
  }
310
289
  
311
 
  /* OpenPGP credentials */
312
 
  if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
 
290
  /* openpgp credentials */
 
291
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
313
292
      != GNUTLS_E_SUCCESS) {
314
 
    fprintf (stderr, "GnuTLS memory error: %s\n",
 
293
    fprintf (stderr, "memory error: %s\n",
315
294
             safer_gnutls_strerror(ret));
316
295
    return -1;
317
296
  }
318
297
  
319
298
  if(debug){
320
299
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
321
 
            " and keyfile %s as GnuTLS credentials\n", pubkeyfile,
322
 
            seckeyfile);
 
300
            " and keyfile %s as GnuTLS credentials\n", certfile,
 
301
            certkey);
323
302
  }
324
303
  
325
304
  ret = gnutls_certificate_set_openpgp_key_file
326
 
    (mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
 
305
    (es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
327
306
  if (ret != GNUTLS_E_SUCCESS) {
328
 
    fprintf(stderr,
329
 
            "Error[%d] while reading the OpenPGP key pair ('%s',"
330
 
            " '%s')\n", ret, pubkeyfile, seckeyfile);
331
 
    fprintf(stdout, "The GnuTLS error is: %s\n",
 
307
    fprintf
 
308
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
 
309
       " '%s')\n",
 
310
       ret, certfile, certkey);
 
311
    fprintf(stdout, "The Error is: %s\n",
332
312
            safer_gnutls_strerror(ret));
333
313
    return -1;
334
314
  }
335
315
  
336
 
  /* GnuTLS server initialization */
337
 
  ret = gnutls_dh_params_init(&mc->dh_params);
338
 
  if (ret != GNUTLS_E_SUCCESS) {
339
 
    fprintf (stderr, "Error in GnuTLS DH parameter initialization:"
340
 
             " %s\n", safer_gnutls_strerror(ret));
341
 
    return -1;
342
 
  }
343
 
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
344
 
  if (ret != GNUTLS_E_SUCCESS) {
345
 
    fprintf (stderr, "Error in GnuTLS prime generation: %s\n",
346
 
             safer_gnutls_strerror(ret));
347
 
    return -1;
348
 
  }
349
 
  
350
 
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
351
 
 
352
 
  return 0;
353
 
}
354
 
 
355
 
static int init_gnutls_session(mandos_context *mc, gnutls_session_t *session){
356
 
  int ret;
357
 
  /* GnuTLS session creation */
358
 
  ret = gnutls_init(session, GNUTLS_SERVER);
359
 
  if (ret != GNUTLS_E_SUCCESS){
 
316
  //GnuTLS server initialization
 
317
  if ((ret = gnutls_dh_params_init (&es->dh_params))
 
318
      != GNUTLS_E_SUCCESS) {
 
319
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
 
320
             safer_gnutls_strerror(ret));
 
321
    return -1;
 
322
  }
 
323
  
 
324
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
 
325
      != GNUTLS_E_SUCCESS) {
 
326
    fprintf (stderr, "Error in prime generation: %s\n",
 
327
             safer_gnutls_strerror(ret));
 
328
    return -1;
 
329
  }
 
330
  
 
331
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
 
332
  
 
333
  // GnuTLS session creation
 
334
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
 
335
      != GNUTLS_E_SUCCESS){
360
336
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
361
337
            safer_gnutls_strerror(ret));
362
338
  }
363
339
  
364
 
  {
365
 
    const char *err;
366
 
    ret = gnutls_priority_set_direct(*session, mc->priority, &err);
367
 
    if (ret != GNUTLS_E_SUCCESS) {
368
 
      fprintf(stderr, "Syntax error at: %s\n", err);
369
 
      fprintf(stderr, "GnuTLS error: %s\n",
370
 
              safer_gnutls_strerror(ret));
371
 
      return -1;
372
 
    }
 
340
  if ((ret = gnutls_priority_set_direct (es->session, mc->priority, &err))
 
341
      != GNUTLS_E_SUCCESS) {
 
342
    fprintf(stderr, "Syntax error at: %s\n", err);
 
343
    fprintf(stderr, "GnuTLS error: %s\n",
 
344
            safer_gnutls_strerror(ret));
 
345
    return -1;
373
346
  }
374
347
  
375
 
  ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
376
 
                               mc->cred);
377
 
  if (ret != GNUTLS_E_SUCCESS) {
378
 
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
 
348
  if ((ret = gnutls_credentials_set
 
349
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
 
350
      != GNUTLS_E_SUCCESS) {
 
351
    fprintf(stderr, "Error setting a credentials set: %s\n",
379
352
            safer_gnutls_strerror(ret));
380
353
    return -1;
381
354
  }
382
355
  
383
356
  /* ignore client certificate if any. */
384
 
  gnutls_certificate_server_set_request (*session,
 
357
  gnutls_certificate_server_set_request (es->session,
385
358
                                         GNUTLS_CERT_IGNORE);
386
359
  
387
 
  gnutls_dh_set_prime_bits (*session, mc->dh_bits);
 
360
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
388
361
  
389
362
  return 0;
390
363
}
391
364
 
392
 
/* Avahi log function callback */
393
365
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
394
366
                      __attribute__((unused)) const char *txt){}
395
367
 
396
 
/* Called when a Mandos server is found */
397
368
static int start_mandos_communication(const char *ip, uint16_t port,
398
369
                                      AvahiIfIndex if_index,
399
370
                                      mandos_context *mc){
400
371
  int ret, tcp_sd;
401
372
  struct sockaddr_in6 to;
 
373
  encrypted_session es;
402
374
  char *buffer = NULL;
403
375
  char *decrypted_buffer;
404
376
  size_t buffer_length = 0;
407
379
  size_t written;
408
380
  int retval = 0;
409
381
  char interface[IF_NAMESIZE];
410
 
  gnutls_session_t session;
411
 
  gnutls_dh_params_t dh_params;
412
 
  
413
 
  ret = init_gnutls_session (mc, &session);
414
 
  if (ret != 0){
415
 
    return -1;
416
 
  }
417
382
  
418
383
  if(debug){
419
384
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
428
393
 
429
394
  if(debug){
430
395
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
431
 
      perror("if_indextoname");
 
396
      if(debug){
 
397
        perror("if_indextoname");
 
398
      }
432
399
      return -1;
433
400
    }
 
401
    
434
402
    fprintf(stderr, "Binding to interface %s\n", interface);
435
403
  }
436
404
  
437
405
  memset(&to,0,sizeof(to));     /* Spurious warning */
438
406
  to.sin6_family = AF_INET6;
439
 
  /* It would be nice to have a way to detect if we were passed an
440
 
     IPv4 address here.   Now we assume an IPv6 address. */
441
407
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
442
408
  if (ret < 0 ){
443
409
    perror("inet_pton");
444
410
    return -1;
445
 
  }
 
411
  }  
446
412
  if(ret == 0){
447
413
    fprintf(stderr, "Bad address: %s\n", ip);
448
414
    return -1;
453
419
  
454
420
  if(debug){
455
421
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
456
 
    char addrstr[INET6_ADDRSTRLEN] = "";
457
 
    if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr,
458
 
                 sizeof(addrstr)) == NULL){
459
 
      perror("inet_ntop");
460
 
    } else {
461
 
      if(strcmp(addrstr, ip) != 0){
462
 
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
463
 
      }
464
 
    }
 
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
/*     } */
465
430
  }
466
431
  
467
432
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
470
435
    return -1;
471
436
  }
472
437
 
473
 
  const char *out = mandos_protocol_version;
 
438
  char *out = mandos_protocol_version;
474
439
  written = 0;
475
440
  while (true){
476
441
    size_t out_size = strlen(out);
479
444
    if (ret == -1){
480
445
      perror("write");
481
446
      retval = -1;
482
 
      goto mandos_end;
 
447
      goto end;
483
448
    }
484
 
    written += (size_t)ret;
 
449
    written += ret;
485
450
    if(written < out_size){
486
451
      continue;
487
452
    } else {
494
459
    }
495
460
  }
496
461
 
 
462
  ret = initgnutls (&es);
 
463
  if (ret != 0){
 
464
    retval = -1;
 
465
    return -1;
 
466
  }
 
467
  
 
468
  gnutls_transport_set_ptr (es.session,
 
469
                            (gnutls_transport_ptr_t) tcp_sd);
 
470
  
497
471
  if(debug){
498
472
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
499
473
  }
500
474
  
501
 
  gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
502
 
  
503
 
  ret = gnutls_handshake (session);
 
475
  ret = gnutls_handshake (es.session);
504
476
  
505
477
  if (ret != GNUTLS_E_SUCCESS){
506
478
    if(debug){
507
 
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
 
479
      fprintf(stderr, "\n*** Handshake failed ***\n");
508
480
      gnutls_perror (ret);
509
481
    }
510
482
    retval = -1;
511
 
    goto mandos_end;
 
483
    goto exit;
512
484
  }
513
485
  
514
 
  /* Read OpenPGP packet that contains the wanted password */
 
486
  //Retrieve OpenPGP packet that contains the wanted password
515
487
  
516
488
  if(debug){
517
489
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
519
491
  }
520
492
 
521
493
  while(true){
522
 
    buffer_capacity = adjustbuffer(&buffer, buffer_length, buffer_capacity);
 
494
    buffer_capacity = adjustbuffer(buffer, buffer_length, buffer_capacity);
523
495
    if (buffer_capacity == 0){
524
496
      perror("adjustbuffer");
525
497
      retval = -1;
526
 
      goto mandos_end;
 
498
      goto exit;
527
499
    }
528
500
    
529
 
    ret = gnutls_record_recv(session, buffer+buffer_length,
530
 
                             BUFFER_SIZE);
 
501
    ret = gnutls_record_recv
 
502
      (es.session, buffer+buffer_length, BUFFER_SIZE);
531
503
    if (ret == 0){
532
504
      break;
533
505
    }
537
509
      case GNUTLS_E_AGAIN:
538
510
        break;
539
511
      case GNUTLS_E_REHANDSHAKE:
540
 
        ret = gnutls_handshake (session);
 
512
        ret = gnutls_handshake (es.session);
541
513
        if (ret < 0){
542
 
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
 
514
          fprintf(stderr, "\n*** Handshake failed ***\n");
543
515
          gnutls_perror (ret);
544
516
          retval = -1;
545
 
          goto mandos_end;
 
517
          goto exit;
546
518
        }
547
519
        break;
548
520
      default:
549
521
        fprintf(stderr, "Unknown error while reading data from"
550
 
                " encrypted session with Mandos server\n");
 
522
                " encrypted session with mandos server\n");
551
523
        retval = -1;
552
 
        gnutls_bye (session, GNUTLS_SHUT_RDWR);
553
 
        goto mandos_end;
 
524
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
 
525
        goto exit;
554
526
      }
555
527
    } else {
556
528
      buffer_length += (size_t) ret;
557
529
    }
558
530
  }
559
531
  
560
 
  if(debug){
561
 
    fprintf(stderr, "Closing TLS session\n");
562
 
  }
563
 
  
564
 
  gnutls_bye (session, GNUTLS_SHUT_RDWR);
565
 
  
566
532
  if (buffer_length > 0){
567
533
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
568
534
                                               buffer_length,
569
535
                                               &decrypted_buffer,
570
 
                                               keydir);
 
536
                                               certdir);
571
537
    if (decrypted_buffer_size >= 0){
572
538
      written = 0;
573
539
      while(written < (size_t) decrypted_buffer_size){
589
555
      retval = -1;
590
556
    }
591
557
  }
592
 
  
593
 
  /* Shutdown procedure */
594
 
  
595
 
 mandos_end:
 
558
 
 
559
  //shutdown procedure
 
560
 
 
561
  if(debug){
 
562
    fprintf(stderr, "Closing TLS session\n");
 
563
  }
 
564
 
596
565
  free(buffer);
 
566
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
 
567
 exit:
597
568
  close(tcp_sd);
598
 
  gnutls_deinit (session);
599
 
  gnutls_certificate_free_credentials (mc->cred);
 
569
  gnutls_deinit (es.session);
 
570
  gnutls_certificate_free_credentials (es.cred);
600
571
  gnutls_global_deinit ();
601
572
  return retval;
602
573
}
603
574
 
604
 
static void resolve_callback(AvahiSServiceResolver *r,
605
 
                             AvahiIfIndex interface,
606
 
                             AVAHI_GCC_UNUSED AvahiProtocol protocol,
607
 
                             AvahiResolverEvent event,
608
 
                             const char *name,
609
 
                             const char *type,
610
 
                             const char *domain,
611
 
                             const char *host_name,
612
 
                             const AvahiAddress *address,
613
 
                             uint16_t port,
614
 
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
615
 
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
616
 
                             flags,
617
 
                             void* userdata) {
 
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) {
618
588
  mandos_context *mc = userdata;
619
589
  assert(r);                    /* Spurious warning */
620
590
  
624
594
  switch (event) {
625
595
  default:
626
596
  case AVAHI_RESOLVER_FAILURE:
627
 
    fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
628
 
            " of type '%s' in domain '%s': %s\n", name, type, domain,
 
597
    fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
 
598
            " type '%s' in domain '%s': %s\n", name, type, domain,
629
599
            avahi_strerror(avahi_server_errno(mc->server)));
630
600
    break;
631
601
    
634
604
      char ip[AVAHI_ADDRESS_STR_MAX];
635
605
      avahi_address_snprint(ip, sizeof(ip), address);
636
606
      if(debug){
637
 
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %d) on"
638
 
                " port %d\n", name, host_name, ip, interface, port);
 
607
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
 
608
                " port %d\n", name, host_name, ip, port);
639
609
      }
640
610
      int ret = start_mandos_communication(ip, port, interface, mc);
641
611
      if (ret == 0){
653
623
                             const char *name,
654
624
                             const char *type,
655
625
                             const char *domain,
656
 
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
657
 
                             flags,
 
626
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
658
627
                             void* userdata) {
659
628
  mandos_context *mc = userdata;
660
629
  assert(b);                    /* Spurious warning */
665
634
  switch (event) {
666
635
  default:
667
636
  case AVAHI_BROWSER_FAILURE:
668
 
    
669
 
    fprintf(stderr, "(Avahi browser) %s\n",
 
637
      
 
638
    fprintf(stderr, "(Browser) %s\n",
670
639
            avahi_strerror(avahi_server_errno(mc->server)));
671
640
    avahi_simple_poll_quit(mc->simple_poll);
672
641
    return;
673
 
    
 
642
      
674
643
  case AVAHI_BROWSER_NEW:
675
 
    /* We ignore the returned Avahi resolver object. In the callback
676
 
       function we free it. If the Avahi server is terminated before
677
 
       the callback function is called the Avahi server will free the
678
 
       resolver for us. */
679
 
    
680
 
    if (!(avahi_s_service_resolver_new(mc->server, interface,
681
 
                                       protocol, name, type, domain,
 
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,
682
651
                                       AVAHI_PROTO_INET6, 0,
683
652
                                       resolve_callback, mc)))
684
 
      fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
685
 
              name, avahi_strerror(avahi_server_errno(mc->server)));
 
653
      fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
 
654
              avahi_strerror(avahi_server_errno(s)));
686
655
    break;
687
 
    
 
656
      
688
657
  case AVAHI_BROWSER_REMOVE:
689
658
    break;
690
 
    
 
659
      
691
660
  case AVAHI_BROWSER_ALL_FOR_NOW:
692
661
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
693
 
    if(debug){
694
 
      fprintf(stderr, "No Mandos server found, still searching...\n");
695
 
    }
696
662
    break;
697
663
  }
698
664
}
707
673
    return NULL;
708
674
  }
709
675
  if(f_len > 0){
710
 
    memcpy(tmp, first, f_len);  /* Spurious warning */
 
676
    memcpy(tmp, first, f_len);
711
677
  }
712
678
  tmp[f_len] = '/';
713
679
  if(s_len > 0){
714
 
    memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
 
680
    memcpy(tmp + f_len + 1, second, s_len);
715
681
  }
716
682
  tmp[f_len + 1 + s_len] = '\0';
717
683
  return tmp;
718
684
}
719
685
 
720
686
 
721
 
int main(int argc, char *argv[]){
 
687
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
 
688
    AvahiServerConfig config;
722
689
    AvahiSServiceBrowser *sb = NULL;
723
690
    int error;
724
691
    int ret;
725
 
    int exitcode = EXIT_SUCCESS;
 
692
    int returncode = EXIT_SUCCESS;
726
693
    const char *interface = "eth0";
727
694
    struct ifreq network;
728
695
    int sd;
729
 
    uid_t uid;
730
 
    gid_t gid;
731
696
    char *connect_to = NULL;
732
697
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
733
 
    const char *pubkeyfile = "pubkey.txt";
734
 
    const char *seckeyfile = "seckey.txt";
735
698
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
736
 
                          .dh_bits = 1024, .priority = "SECURE256"};
 
699
                          .dh_bits = 2048, .priority = "SECURE256"};
737
700
    
738
 
    {
739
 
      struct argp_option options[] = {
740
 
        { .name = "debug", .key = 128,
741
 
          .doc = "Debug mode", .group = 3 },
742
 
        { .name = "connect", .key = 'c',
743
 
          .arg = "IP",
744
 
          .doc = "Connect directly to a sepcified mandos server", .group = 1 },
745
 
        { .name = "interface", .key = 'i',
746
 
          .arg = "INTERFACE",
747
 
          .doc = "Interface that Avahi will conntect through", .group = 1 },
748
 
        { .name = "keydir", .key = 'd',
749
 
          .arg = "KEYDIR",
750
 
          .doc = "Directory where the openpgp keyring is", .group = 1 },
751
 
        { .name = "seckey", .key = 's',
752
 
          .arg = "SECKEY",
753
 
          .doc = "Secret openpgp key for gnutls authentication", .group = 1 },
754
 
        { .name = "pubkey", .key = 'p',
755
 
          .arg = "PUBKEY",
756
 
          .doc = "Public openpgp key for gnutls authentication", .group = 2 },
757
 
        { .name = "dh-bits", .key = 129,
758
 
          .arg = "BITS",
759
 
          .doc = "dh-bits to use in gnutls communication", .group = 2 },
760
 
        { .name = "priority", .key = 130,
761
 
          .arg = "PRIORITY",
762
 
          .doc = "GNUTLS priority", .group = 1 },
763
 
        { .name = NULL }
764
 
      };
765
 
 
766
 
      
767
 
      error_t parse_opt (int key, char *arg, struct argp_state *state) {
768
 
        /* Get the INPUT argument from `argp_parse', which we know is a
769
 
           pointer to our plugin list pointer. */
770
 
        switch (key) {
771
 
        case 128:
772
 
          debug = true;
773
 
          break;
774
 
        case 'c':
775
 
          connect_to = arg;
776
 
          break;
777
 
        case 'i':
778
 
          interface = arg;
779
 
          break;
780
 
        case 'd':
781
 
          keydir = arg;
782
 
          break;
783
 
        case 's':
784
 
          seckeyfile = arg;
785
 
          break;
786
 
        case 'p':
787
 
          pubkeyfile = arg;
788
 
          break;
789
 
        case 129:
 
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;
790
742
          errno = 0;
791
 
          mc.dh_bits = (unsigned int) strtol(arg, NULL, 10);
792
 
          if (errno){
 
743
          tmp = strtol(optarg, NULL, 10);
 
744
          if (errno == ERANGE){
793
745
            perror("strtol");
794
746
            exit(EXIT_FAILURE);
795
747
          }
796
 
          break;
797
 
        case 130:
798
 
          mc.priority = arg;
799
 
          break;
800
 
        case ARGP_KEY_ARG:
801
 
          argp_usage (state);
802
 
          break;
803
 
          case ARGP_KEY_END:
804
 
            break;
805
 
        default:
806
 
          return ARGP_ERR_UNKNOWN;
 
748
          mc.dh_bits = tmp;
807
749
        }
808
 
        return 0;
 
750
        break;
 
751
      case 'p':
 
752
        mc.priority = optarg;
 
753
        break;
 
754
      default:
 
755
        exit(EXIT_FAILURE);
809
756
      }
810
 
 
811
 
      struct argp argp = { .options = options, .parser = parse_opt,
812
 
                           .args_doc = "",
813
 
                           .doc = "Mandos client -- Get and decrypt passwords from mandos server" };
814
 
      argp_parse (&argp, argc, argv, 0, 0, NULL);
815
 
    }
816
 
      
817
 
    pubkeyfile = combinepath(keydir, pubkeyfile);
818
 
    if (pubkeyfile == NULL){
819
 
      perror("combinepath");
820
 
      exitcode = EXIT_FAILURE;
821
 
      goto end;
822
 
    }
823
 
    
824
 
    seckeyfile = combinepath(keydir, seckeyfile);
825
 
    if (seckeyfile == NULL){
826
 
      perror("combinepath");
827
 
      goto end;
828
 
    }
829
 
 
830
 
    ret = init_gnutls_global(&mc, pubkeyfile, seckeyfile);
831
 
    if (ret == -1){
832
 
      fprintf(stderr, "init_gnutls_global\n");
833
 
      goto end;
834
 
    }
835
 
 
836
 
    uid = getuid();
837
 
    gid = getgid();
838
 
 
839
 
    ret = setuid(uid);
840
 
    if (ret == -1){
841
 
      perror("setuid");
842
 
    }
843
 
    
844
 
    setgid(gid);
845
 
    if (ret == -1){
846
 
      perror("setgid");
 
757
    }
 
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;
847
771
    }
848
772
    
849
773
    if_index = (AvahiIfIndex) if_nametoindex(interface);
858
782
      char *address = strrchr(connect_to, ':');
859
783
      if(address == NULL){
860
784
        fprintf(stderr, "No colon in address\n");
861
 
        exitcode = EXIT_FAILURE;
862
 
        goto end;
 
785
        exit(EXIT_FAILURE);
863
786
      }
864
787
      errno = 0;
865
788
      uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
866
789
      if(errno){
867
790
        perror("Bad port number");
868
 
        exitcode = EXIT_FAILURE;
869
 
        goto end;
 
791
        exit(EXIT_FAILURE);
870
792
      }
871
793
      *address = '\0';
872
794
      address = connect_to;
873
 
      ret = start_mandos_communication(address, port, if_index, &mc);
 
795
      ret = start_mandos_communication(address, port, if_index);
874
796
      if(ret < 0){
875
 
        exitcode = EXIT_FAILURE;
 
797
        exit(EXIT_FAILURE);
876
798
      } else {
877
 
        exitcode = EXIT_SUCCESS;
 
799
        exit(EXIT_SUCCESS);
878
800
      }
879
 
      goto end;
880
801
    }
881
802
    
882
 
    /* If the interface is down, bring it up */
883
 
    {
884
 
      sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
885
 
      if(sd < 0) {
886
 
        perror("socket");
887
 
        exitcode = EXIT_FAILURE;
888
 
        goto end;
889
 
      }
890
 
      strcpy(network.ifr_name, interface); /* Spurious warning */
891
 
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
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);
892
820
      if(ret == -1){
893
 
        perror("ioctl SIOCGIFFLAGS");
894
 
        exitcode = EXIT_FAILURE;
895
 
        goto end;
896
 
      }
897
 
      if((network.ifr_flags & IFF_UP) == 0){
898
 
        network.ifr_flags |= IFF_UP;
899
 
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
900
 
        if(ret == -1){
901
 
          perror("ioctl SIOCSIFFLAGS");
902
 
          exitcode = EXIT_FAILURE;
903
 
          goto end;
904
 
        }
905
 
      }
906
 
      close(sd);
 
821
        perror("ioctl SIOCSIFFLAGS");
 
822
        returncode = EXIT_FAILURE;
 
823
        goto exit;
 
824
      }
907
825
    }
 
826
    close(sd);
908
827
    
909
828
    if (not debug){
910
829
      avahi_set_log_function(empty_log);
911
830
    }
912
831
    
913
 
    /* Initialize the pseudo-RNG for Avahi */
 
832
    /* Initialize the psuedo-RNG */
914
833
    srand((unsigned int) time(NULL));
915
 
    
916
 
    /* Allocate main Avahi loop object */
917
 
    mc.simple_poll = avahi_simple_poll_new();
918
 
    if (mc.simple_poll == NULL) {
919
 
        fprintf(stderr, "Avahi: Failed to create simple poll"
920
 
                " object.\n");
921
 
        exitcode = EXIT_FAILURE;
922
 
        goto end;
923
 
    }
924
 
 
925
 
    {
926
 
      AvahiServerConfig config;
927
 
      /* Do not publish any local Zeroconf records */
928
 
      avahi_server_config_init(&config);
929
 
      config.publish_hinfo = 0;
930
 
      config.publish_addresses = 0;
931
 
      config.publish_workstation = 0;
932
 
      config.publish_domain = 0;
933
 
 
934
 
      /* Allocate a new server */
935
 
      mc.server = avahi_server_new(avahi_simple_poll_get
936
 
                                   (mc.simple_poll), &config, NULL,
937
 
                                   NULL, &error);
938
 
    
939
 
      /* Free the Avahi configuration data */
940
 
      avahi_server_config_free(&config);
941
 
    }
942
 
    
943
 
    /* Check if creating the Avahi server object succeeded */
944
 
    if (mc.server == NULL) {
945
 
        fprintf(stderr, "Failed to create Avahi server: %s\n",
 
834
 
 
835
    /* Allocate main loop object */
 
836
    if (!(mc.simple_poll = avahi_simple_poll_new())) {
 
837
        fprintf(stderr, "Failed to create simple poll object.\n");
 
838
        returncode = EXIT_FAILURE;      
 
839
        goto exit;
 
840
    }
 
841
 
 
842
    /* Do not publish any local records */
 
843
    avahi_server_config_init(&config);
 
844
    config.publish_hinfo = 0;
 
845
    config.publish_addresses = 0;
 
846
    config.publish_workstation = 0;
 
847
    config.publish_domain = 0;
 
848
 
 
849
    /* Allocate a new server */
 
850
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
 
851
                              &config, NULL, NULL, &error);
 
852
 
 
853
    /* Free the configuration data */
 
854
    avahi_server_config_free(&config);
 
855
 
 
856
    /* Check if creating the server object succeeded */
 
857
    if (!mc.server) {
 
858
        fprintf(stderr, "Failed to create server: %s\n",
946
859
                avahi_strerror(error));
947
 
        exitcode = EXIT_FAILURE;
948
 
        goto end;
 
860
        returncode = EXIT_FAILURE;
 
861
        goto exit;
949
862
    }
950
863
    
951
 
    /* Create the Avahi service browser */
 
864
    /* Create the service browser */
952
865
    sb = avahi_s_service_browser_new(mc.server, if_index,
953
866
                                     AVAHI_PROTO_INET6,
954
867
                                     "_mandos._tcp", NULL, 0,
955
868
                                     browse_callback, &mc);
956
 
    if (sb == NULL) {
 
869
    if (!sb) {
957
870
        fprintf(stderr, "Failed to create service browser: %s\n",
958
871
                avahi_strerror(avahi_server_errno(mc.server)));
959
 
        exitcode = EXIT_FAILURE;
960
 
        goto end;
 
872
        returncode = EXIT_FAILURE;
 
873
        goto exit;
961
874
    }
962
875
    
963
876
    /* Run the main loop */
964
877
 
965
878
    if (debug){
966
 
      fprintf(stderr, "Starting Avahi loop search\n");
 
879
      fprintf(stderr, "Starting avahi loop search\n");
967
880
    }
968
881
    
969
 
    avahi_simple_poll_loop(mc.simple_poll);
 
882
    avahi_simple_poll_loop(simple_poll);
970
883
    
971
 
 end:
 
884
 exit:
972
885
 
973
886
    if (debug){
974
887
      fprintf(stderr, "%s exiting\n", argv[0]);
975
888
    }
976
889
    
977
890
    /* Cleanup things */
978
 
    if (sb != NULL)
 
891
    if (sb)
979
892
        avahi_s_service_browser_free(sb);
980
893
    
981
 
    if (mc.server != NULL)
 
894
    if (mc.server)
982
895
        avahi_server_free(mc.server);
983
896
 
984
 
    if (mc.simple_poll != NULL)
985
 
        avahi_simple_poll_free(mc.simple_poll);
986
 
    free(pubkeyfile);
987
 
    free(seckeyfile);
 
897
    if (simple_poll)
 
898
        avahi_simple_poll_free(simple_poll);
 
899
    free(certfile);
 
900
    free(certkey);
988
901
    
989
 
    return exitcode;
 
902
    return returncode;
990
903
}