/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/mandos-client.c

  • Committer: Teddy Hogeborn
  • Date: 2008-12-10 01:26:02 UTC
  • mfrom: (237.1.2 mandos)
  • Revision ID: teddy@fukt.bsnet.se-20081210012602-vhz3h75xkj24t340
First version of a somewhat complete D-Bus server interface.  Also
change user/group name to "_mandos".

* debian/mandos.postinst: Rename old "mandos" user and group to
                          "_mandos"; create "_mandos" user and group
                          if none exist.
* debian/mandos-client.postinst: - '' -

* initramfs-tools-hook: Try "_mandos" before "mandos" as user and
                        group name.

* mandos (_datetime_to_dbus_struct): New; was previously local.
  (Client.started): Renamed to "last_started".  All users changed.
  (Client.started): New; boolean.
  (Client.dbus_object_path): New.
  (Client.check_command): Renamed to "checker_command".  All users
                          changed.
  (Client.__init__): Set and use "self.dbus_object_path".  Set
                     "self.started".
  (Client.start): Update "self.started".  Emit "self.PropertyChanged"
                  signals for both "started" and "last_started".
  (Client.stop): Update "self.started".  Emit "self.PropertyChanged"
                 signal for "started".
  (Client.checker_callback): Take additional "command" argument.  All
                             callers changed. Emit
                             "self.PropertyChanged" signal.
  (Client.bump_timeout): Emit "self.PropertyChanged" signal for
                         "last_checked_ok".
  (Client.start_checker): Emit "self.PropertyChanged" signal for
                          "checker_running".
  (Client.stop_checker): Emit "self.PropertyChanged" signal for
                         "checker_running".
  (Client.still_valid): Bug fix: use "getattr(self, started, False)"
                        instead of "self.started" in case this client
                        object is so new that the "started" attribute
                        has not been created yet.
  (Client.IntervalChanged, Client.CheckerIsRunning, Client.GetChecker,
  Client.GetCreated, Client.GetFingerprint, Client.GetHost,
  Client.GetInterval, Client.GetName, Client.GetStarted,
  Client.GetTimeout, Client.StateChanged, Client.TimeoutChanged):
  Removed; all callers changed.
  (Client.CheckerCompleted): Add "condition" and "command" arguments.
                             All callers changed.
  (Client.GetAllProperties, Client.PropertyChanged): New.
  (Client.StillValid): Renamed to "IsStillValid".
  (Client.StartChecker): Changed to its own function to avoid the
                         return value from "Client.start_checker()".
  (Client.Stop): Changed to its own function to avoid the return value
                 from "Client.stop()".
  (main): Try "_mandos" before "mandos" as user and group name.
          Removed inner function "remove_from_clients".  New inner
          class "MandosServer".

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*  -*- coding: utf-8 -*- */
2
2
/*
3
 
 * Mandos-client - get and decrypt data from a Mandos server
 
3
 * Mandos client - get and decrypt data from a Mandos server
4
4
 *
5
5
 * This program is partly derived from an example program for an Avahi
6
6
 * service browser, downloaded from
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
11
 * Everything else is
12
 
 * Copyright © 2008,2009 Teddy Hogeborn
13
 
 * Copyright © 2008,2009 Björn Påhlsson
 
12
 * Copyright © 2008 Teddy Hogeborn
 
13
 * Copyright © 2008 Björn Påhlsson
14
14
 * 
15
15
 * This program is free software: you can redistribute it and/or
16
16
 * modify it under the terms of the GNU General Public License as
36
36
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
37
37
 
38
38
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
39
 
                                   stdout, ferror(), sscanf(),
40
 
                                   remove() */
 
39
                                   stdout, ferror() */
41
40
#include <stdint.h>             /* uint16_t, uint32_t */
42
41
#include <stddef.h>             /* NULL, size_t, ssize_t */
43
42
#include <stdlib.h>             /* free(), EXIT_SUCCESS, EXIT_FAILURE,
49
48
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
50
49
                                   sockaddr_in6, PF_INET6,
51
50
                                   SOCK_STREAM, INET6_ADDRSTRLEN,
52
 
                                   uid_t, gid_t, open(), opendir(),
53
 
                                   DIR */
 
51
                                   uid_t, gid_t, open(), opendir(), DIR */
54
52
#include <sys/stat.h>           /* open() */
55
53
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
56
54
                                   struct in6_addr, inet_pton(),
57
55
                                   connect() */
58
56
#include <fcntl.h>              /* open() */
59
 
#include <dirent.h>             /* opendir(), struct dirent, readdir()
60
 
                                 */
61
 
#include <inttypes.h>           /* PRIu16, intmax_t, SCNdMAX */
 
57
#include <dirent.h>             /* opendir(), struct dirent, readdir() */
 
58
#include <inttypes.h>           /* PRIu16 */
62
59
#include <assert.h>             /* assert() */
63
60
#include <errno.h>              /* perror(), errno */
64
 
#include <time.h>               /* nanosleep(), time() */
 
61
#include <time.h>               /* time() */
65
62
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
66
63
                                   SIOCSIFFLAGS, if_indextoname(),
67
64
                                   if_nametoindex(), IF_NAMESIZE */
70
67
                                   getuid(), getgid(), setuid(),
71
68
                                   setgid() */
72
69
#include <arpa/inet.h>          /* inet_pton(), htons */
73
 
#include <iso646.h>             /* not, and, or */
 
70
#include <iso646.h>             /* not, and */
74
71
#include <argp.h>               /* struct argp_option, error_t, struct
75
72
                                   argp_state, struct argp,
76
73
                                   argp_parse(), ARGP_KEY_ARG,
77
74
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
78
 
#include <sys/klog.h>           /* klogctl() */
79
75
 
80
76
/* Avahi */
81
77
/* All Avahi types, constants and functions
94
90
                                   gnutls_*
95
91
                                   init_gnutls_session(),
96
92
                                   GNUTLS_* */
97
 
#include <gnutls/openpgp.h>
98
 
                          /* gnutls_certificate_set_openpgp_key_file(),
 
93
#include <gnutls/openpgp.h>     /* gnutls_certificate_set_openpgp_key_file(),
99
94
                                   GNUTLS_OPENPGP_FMT_BASE64 */
100
95
 
101
96
/* GPGME */
134
129
 */
135
130
size_t adjustbuffer(char **buffer, size_t buffer_length,
136
131
                  size_t buffer_capacity){
137
 
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
 
132
  if (buffer_length + BUFFER_SIZE > buffer_capacity){
138
133
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
139
 
    if(buffer == NULL){
 
134
    if (buffer == NULL){
140
135
      return 0;
141
136
    }
142
137
    buffer_capacity += BUFFER_SIZE;
155
150
  
156
151
  
157
152
  /*
158
 
   * Helper function to insert pub and seckey to the engine keyring.
 
153
   * Helper function to insert pub and seckey to the enigne keyring.
159
154
   */
160
155
  bool import_key(const char *filename){
161
156
    int fd;
162
157
    gpgme_data_t pgp_data;
163
158
    
164
 
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
 
159
    fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
165
160
    if(fd == -1){
166
161
      perror("open");
167
162
      return false;
168
163
    }
169
164
    
170
165
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
171
 
    if(rc != GPG_ERR_NO_ERROR){
 
166
    if (rc != GPG_ERR_NO_ERROR){
172
167
      fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
173
168
              gpgme_strsource(rc), gpgme_strerror(rc));
174
169
      return false;
175
170
    }
176
171
    
177
172
    rc = gpgme_op_import(mc->ctx, pgp_data);
178
 
    if(rc != GPG_ERR_NO_ERROR){
 
173
    if (rc != GPG_ERR_NO_ERROR){
179
174
      fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
180
175
              gpgme_strsource(rc), gpgme_strerror(rc));
181
176
      return false;
182
177
    }
183
178
    
184
 
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
179
    ret = TEMP_FAILURE_RETRY(close(fd));
185
180
    if(ret == -1){
186
181
      perror("close");
187
182
    }
189
184
    return true;
190
185
  }
191
186
  
192
 
  if(debug){
 
187
  if (debug){
193
188
    fprintf(stderr, "Initialize gpgme\n");
194
189
  }
195
190
  
196
191
  /* Init GPGME */
197
192
  gpgme_check_version(NULL);
198
193
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
199
 
  if(rc != GPG_ERR_NO_ERROR){
 
194
  if (rc != GPG_ERR_NO_ERROR){
200
195
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
201
196
            gpgme_strsource(rc), gpgme_strerror(rc));
202
197
    return false;
203
198
  }
204
199
  
205
200
    /* Set GPGME home directory for the OpenPGP engine only */
206
 
  rc = gpgme_get_engine_info(&engine_info);
207
 
  if(rc != GPG_ERR_NO_ERROR){
 
201
  rc = gpgme_get_engine_info (&engine_info);
 
202
  if (rc != GPG_ERR_NO_ERROR){
208
203
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
209
204
            gpgme_strsource(rc), gpgme_strerror(rc));
210
205
    return false;
224
219
  
225
220
  /* Create new GPGME "context" */
226
221
  rc = gpgme_new(&(mc->ctx));
227
 
  if(rc != GPG_ERR_NO_ERROR){
 
222
  if (rc != GPG_ERR_NO_ERROR){
228
223
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
229
224
            gpgme_strsource(rc), gpgme_strerror(rc));
230
225
    return false;
231
226
  }
232
227
  
233
 
  if(not import_key(pubkey) or not import_key(seckey)){
 
228
  if (not import_key(pubkey) or not import_key(seckey)){
234
229
    return false;
235
230
  }
236
231
  
241
236
 * Decrypt OpenPGP data.
242
237
 * Returns -1 on error
243
238
 */
244
 
static ssize_t pgp_packet_decrypt(const mandos_context *mc,
245
 
                                  const char *cryptotext,
246
 
                                  size_t crypto_size,
247
 
                                  char **plaintext){
 
239
static ssize_t pgp_packet_decrypt (const mandos_context *mc,
 
240
                                   const char *cryptotext,
 
241
                                   size_t crypto_size,
 
242
                                   char **plaintext){
248
243
  gpgme_data_t dh_crypto, dh_plain;
249
244
  gpgme_error_t rc;
250
245
  ssize_t ret;
251
246
  size_t plaintext_capacity = 0;
252
247
  ssize_t plaintext_length = 0;
253
248
  
254
 
  if(debug){
 
249
  if (debug){
255
250
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
256
251
  }
257
252
  
258
253
  /* Create new GPGME data buffer from memory cryptotext */
259
254
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
260
255
                               0);
261
 
  if(rc != GPG_ERR_NO_ERROR){
 
256
  if (rc != GPG_ERR_NO_ERROR){
262
257
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
263
258
            gpgme_strsource(rc), gpgme_strerror(rc));
264
259
    return -1;
266
261
  
267
262
  /* Create new empty GPGME data buffer for the plaintext */
268
263
  rc = gpgme_data_new(&dh_plain);
269
 
  if(rc != GPG_ERR_NO_ERROR){
 
264
  if (rc != GPG_ERR_NO_ERROR){
270
265
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
271
266
            gpgme_strsource(rc), gpgme_strerror(rc));
272
267
    gpgme_data_release(dh_crypto);
276
271
  /* Decrypt data from the cryptotext data buffer to the plaintext
277
272
     data buffer */
278
273
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
279
 
  if(rc != GPG_ERR_NO_ERROR){
 
274
  if (rc != GPG_ERR_NO_ERROR){
280
275
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
281
276
            gpgme_strsource(rc), gpgme_strerror(rc));
282
277
    plaintext_length = -1;
283
 
    if(debug){
 
278
    if (debug){
284
279
      gpgme_decrypt_result_t result;
285
280
      result = gpgme_op_decrypt_result(mc->ctx);
286
 
      if(result == NULL){
 
281
      if (result == NULL){
287
282
        fprintf(stderr, "gpgme_op_decrypt_result failed\n");
288
283
      } else {
289
284
        fprintf(stderr, "Unsupported algorithm: %s\n",
316
311
  }
317
312
  
318
313
  /* Seek back to the beginning of the GPGME plaintext data buffer */
319
 
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
 
314
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
320
315
    perror("gpgme_data_seek");
321
316
    plaintext_length = -1;
322
317
    goto decrypt_end;
327
322
    plaintext_capacity = adjustbuffer(plaintext,
328
323
                                      (size_t)plaintext_length,
329
324
                                      plaintext_capacity);
330
 
    if(plaintext_capacity == 0){
 
325
    if (plaintext_capacity == 0){
331
326
        perror("adjustbuffer");
332
327
        plaintext_length = -1;
333
328
        goto decrypt_end;
336
331
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
337
332
                          BUFFER_SIZE);
338
333
    /* Print the data, if any */
339
 
    if(ret == 0){
 
334
    if (ret == 0){
340
335
      /* EOF */
341
336
      break;
342
337
    }
366
361
  return plaintext_length;
367
362
}
368
363
 
369
 
static const char * safer_gnutls_strerror(int value) {
370
 
  const char *ret = gnutls_strerror(value); /* Spurious warning from
371
 
                                               -Wunreachable-code */
372
 
  if(ret == NULL)
 
364
static const char * safer_gnutls_strerror (int value) {
 
365
  const char *ret = gnutls_strerror (value); /* Spurious warning */
 
366
  if (ret == NULL)
373
367
    ret = "(unknown)";
374
368
  return ret;
375
369
}
390
384
  }
391
385
  
392
386
  ret = gnutls_global_init();
393
 
  if(ret != GNUTLS_E_SUCCESS) {
394
 
    fprintf(stderr, "GnuTLS global_init: %s\n",
395
 
            safer_gnutls_strerror(ret));
 
387
  if (ret != GNUTLS_E_SUCCESS) {
 
388
    fprintf (stderr, "GnuTLS global_init: %s\n",
 
389
             safer_gnutls_strerror(ret));
396
390
    return -1;
397
391
  }
398
392
  
399
 
  if(debug){
 
393
  if (debug){
400
394
    /* "Use a log level over 10 to enable all debugging options."
401
395
     * - GnuTLS manual
402
396
     */
406
400
  
407
401
  /* OpenPGP credentials */
408
402
  gnutls_certificate_allocate_credentials(&mc->cred);
409
 
  if(ret != GNUTLS_E_SUCCESS){
410
 
    fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
411
 
                                                  * from
412
 
                                                  * -Wunreachable-code
413
 
                                                  */
414
 
            safer_gnutls_strerror(ret));
415
 
    gnutls_global_deinit();
 
403
  if (ret != GNUTLS_E_SUCCESS){
 
404
    fprintf (stderr, "GnuTLS memory error: %s\n", /* Spurious
 
405
                                                     warning */
 
406
             safer_gnutls_strerror(ret));
 
407
    gnutls_global_deinit ();
416
408
    return -1;
417
409
  }
418
410
  
425
417
  ret = gnutls_certificate_set_openpgp_key_file
426
418
    (mc->cred, pubkeyfilename, seckeyfilename,
427
419
     GNUTLS_OPENPGP_FMT_BASE64);
428
 
  if(ret != GNUTLS_E_SUCCESS) {
 
420
  if (ret != GNUTLS_E_SUCCESS) {
429
421
    fprintf(stderr,
430
422
            "Error[%d] while reading the OpenPGP key pair ('%s',"
431
423
            " '%s')\n", ret, pubkeyfilename, seckeyfilename);
436
428
  
437
429
  /* GnuTLS server initialization */
438
430
  ret = gnutls_dh_params_init(&mc->dh_params);
439
 
  if(ret != GNUTLS_E_SUCCESS) {
440
 
    fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
441
 
            " %s\n", safer_gnutls_strerror(ret));
 
431
  if (ret != GNUTLS_E_SUCCESS) {
 
432
    fprintf (stderr, "Error in GnuTLS DH parameter initialization:"
 
433
             " %s\n", safer_gnutls_strerror(ret));
442
434
    goto globalfail;
443
435
  }
444
436
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
445
 
  if(ret != GNUTLS_E_SUCCESS) {
446
 
    fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
447
 
            safer_gnutls_strerror(ret));
 
437
  if (ret != GNUTLS_E_SUCCESS) {
 
438
    fprintf (stderr, "Error in GnuTLS prime generation: %s\n",
 
439
             safer_gnutls_strerror(ret));
448
440
    goto globalfail;
449
441
  }
450
442
  
465
457
  int ret;
466
458
  /* GnuTLS session creation */
467
459
  ret = gnutls_init(session, GNUTLS_SERVER);
468
 
  if(ret != GNUTLS_E_SUCCESS){
 
460
  if (ret != GNUTLS_E_SUCCESS){
469
461
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
470
462
            safer_gnutls_strerror(ret));
471
463
  }
473
465
  {
474
466
    const char *err;
475
467
    ret = gnutls_priority_set_direct(*session, mc->priority, &err);
476
 
    if(ret != GNUTLS_E_SUCCESS) {
 
468
    if (ret != GNUTLS_E_SUCCESS) {
477
469
      fprintf(stderr, "Syntax error at: %s\n", err);
478
470
      fprintf(stderr, "GnuTLS error: %s\n",
479
471
              safer_gnutls_strerror(ret));
480
 
      gnutls_deinit(*session);
 
472
      gnutls_deinit (*session);
481
473
      return -1;
482
474
    }
483
475
  }
484
476
  
485
477
  ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
486
478
                               mc->cred);
487
 
  if(ret != GNUTLS_E_SUCCESS) {
 
479
  if (ret != GNUTLS_E_SUCCESS) {
488
480
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
489
481
            safer_gnutls_strerror(ret));
490
 
    gnutls_deinit(*session);
 
482
    gnutls_deinit (*session);
491
483
    return -1;
492
484
  }
493
485
  
494
486
  /* ignore client certificate if any. */
495
 
  gnutls_certificate_server_set_request(*session,
496
 
                                        GNUTLS_CERT_IGNORE);
 
487
  gnutls_certificate_server_set_request (*session,
 
488
                                         GNUTLS_CERT_IGNORE);
497
489
  
498
 
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
 
490
  gnutls_dh_set_prime_bits (*session, mc->dh_bits);
499
491
  
500
492
  return 0;
501
493
}
509
501
                                      AvahiIfIndex if_index,
510
502
                                      mandos_context *mc){
511
503
  int ret, tcp_sd;
512
 
  ssize_t sret;
513
504
  union { struct sockaddr in; struct sockaddr_in6 in6; } to;
514
505
  char *buffer = NULL;
515
506
  char *decrypted_buffer;
521
512
  char interface[IF_NAMESIZE];
522
513
  gnutls_session_t session;
523
514
  
524
 
  ret = init_gnutls_session(mc, &session);
525
 
  if(ret != 0){
 
515
  ret = init_gnutls_session (mc, &session);
 
516
  if (ret != 0){
526
517
    return -1;
527
518
  }
528
519
  
550
541
  /* It would be nice to have a way to detect if we were passed an
551
542
     IPv4 address here.   Now we assume an IPv6 address. */
552
543
  ret = inet_pton(AF_INET6, ip, &to.in6.sin6_addr);
553
 
  if(ret < 0 ){
 
544
  if (ret < 0 ){
554
545
    perror("inet_pton");
555
546
    return -1;
556
547
  }
558
549
    fprintf(stderr, "Bad address: %s\n", ip);
559
550
    return -1;
560
551
  }
561
 
  to.in6.sin6_port = htons(port); /* Spurious warnings from
562
 
                                     -Wconversion and
563
 
                                     -Wunreachable-code */
 
552
  to.in6.sin6_port = htons(port); /* Spurious warning */
564
553
  
565
554
  to.in6.sin6_scope_id = (uint32_t)if_index;
566
555
  
579
568
  }
580
569
  
581
570
  ret = connect(tcp_sd, &to.in, sizeof(to));
582
 
  if(ret < 0){
 
571
  if (ret < 0){
583
572
    perror("connect");
584
573
    return -1;
585
574
  }
586
575
  
587
576
  const char *out = mandos_protocol_version;
588
577
  written = 0;
589
 
  while(true){
 
578
  while (true){
590
579
    size_t out_size = strlen(out);
591
 
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
580
    ret = TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
592
581
                                   out_size - written));
593
 
    if(ret == -1){
 
582
    if (ret == -1){
594
583
      perror("write");
595
584
      retval = -1;
596
585
      goto mandos_end;
599
588
    if(written < out_size){
600
589
      continue;
601
590
    } else {
602
 
      if(out == mandos_protocol_version){
 
591
      if (out == mandos_protocol_version){
603
592
        written = 0;
604
593
        out = "\r\n";
605
594
      } else {
612
601
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
613
602
  }
614
603
  
615
 
  gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
 
604
  gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
616
605
  
617
606
  do{
618
 
    ret = gnutls_handshake(session);
 
607
    ret = gnutls_handshake (session);
619
608
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
620
609
  
621
 
  if(ret != GNUTLS_E_SUCCESS){
 
610
  if (ret != GNUTLS_E_SUCCESS){
622
611
    if(debug){
623
612
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
624
 
      gnutls_perror(ret);
 
613
      gnutls_perror (ret);
625
614
    }
626
615
    retval = -1;
627
616
    goto mandos_end;
637
626
  while(true){
638
627
    buffer_capacity = adjustbuffer(&buffer, buffer_length,
639
628
                                   buffer_capacity);
640
 
    if(buffer_capacity == 0){
 
629
    if (buffer_capacity == 0){
641
630
      perror("adjustbuffer");
642
631
      retval = -1;
643
632
      goto mandos_end;
644
633
    }
645
634
    
646
 
    sret = gnutls_record_recv(session, buffer+buffer_length,
647
 
                              BUFFER_SIZE);
648
 
    if(sret == 0){
 
635
    ret = gnutls_record_recv(session, buffer+buffer_length,
 
636
                             BUFFER_SIZE);
 
637
    if (ret == 0){
649
638
      break;
650
639
    }
651
 
    if(sret < 0){
652
 
      switch(sret){
 
640
    if (ret < 0){
 
641
      switch(ret){
653
642
      case GNUTLS_E_INTERRUPTED:
654
643
      case GNUTLS_E_AGAIN:
655
644
        break;
656
645
      case GNUTLS_E_REHANDSHAKE:
657
646
        do{
658
 
          ret = gnutls_handshake(session);
 
647
          ret = gnutls_handshake (session);
659
648
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
660
 
        if(ret < 0){
 
649
        if (ret < 0){
661
650
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
662
 
          gnutls_perror(ret);
 
651
          gnutls_perror (ret);
663
652
          retval = -1;
664
653
          goto mandos_end;
665
654
        }
668
657
        fprintf(stderr, "Unknown error while reading data from"
669
658
                " encrypted session with Mandos server\n");
670
659
        retval = -1;
671
 
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
660
        gnutls_bye (session, GNUTLS_SHUT_RDWR);
672
661
        goto mandos_end;
673
662
      }
674
663
    } else {
675
 
      buffer_length += (size_t) sret;
 
664
      buffer_length += (size_t) ret;
676
665
    }
677
666
  }
678
667
  
680
669
    fprintf(stderr, "Closing TLS session\n");
681
670
  }
682
671
  
683
 
  gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
672
  gnutls_bye (session, GNUTLS_SHUT_RDWR);
684
673
  
685
 
  if(buffer_length > 0){
 
674
  if (buffer_length > 0){
686
675
    decrypted_buffer_size = pgp_packet_decrypt(mc, buffer,
687
676
                                               buffer_length,
688
677
                                               &decrypted_buffer);
689
 
    if(decrypted_buffer_size >= 0){
 
678
    if (decrypted_buffer_size >= 0){
690
679
      written = 0;
691
680
      while(written < (size_t) decrypted_buffer_size){
692
 
        ret = (int)fwrite(decrypted_buffer + written, 1,
693
 
                          (size_t)decrypted_buffer_size - written,
694
 
                          stdout);
 
681
        ret = (int)fwrite (decrypted_buffer + written, 1,
 
682
                           (size_t)decrypted_buffer_size - written,
 
683
                           stdout);
695
684
        if(ret == 0 and ferror(stdout)){
696
685
          if(debug){
697
686
            fprintf(stderr, "Error writing encrypted data: %s\n",
714
703
  
715
704
 mandos_end:
716
705
  free(buffer);
717
 
  ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
706
  ret = TEMP_FAILURE_RETRY(close(tcp_sd));
718
707
  if(ret == -1){
719
708
    perror("close");
720
709
  }
721
 
  gnutls_deinit(session);
 
710
  gnutls_deinit (session);
722
711
  return retval;
723
712
}
724
713
 
742
731
  /* Called whenever a service has been resolved successfully or
743
732
     timed out */
744
733
  
745
 
  switch(event) {
 
734
  switch (event) {
746
735
  default:
747
736
  case AVAHI_RESOLVER_FAILURE:
748
737
    fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
756
745
      avahi_address_snprint(ip, sizeof(ip), address);
757
746
      if(debug){
758
747
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
759
 
                PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
760
 
                ip, (intmax_t)interface, port);
 
748
                PRIu16 ") on port %d\n", name, host_name, ip,
 
749
                interface, port);
761
750
      }
762
751
      int ret = start_mandos_communication(ip, port, interface, mc);
763
 
      if(ret == 0){
 
752
      if (ret == 0){
764
753
        avahi_simple_poll_quit(mc->simple_poll);
765
754
      }
766
755
    }
784
773
  /* Called whenever a new services becomes available on the LAN or
785
774
     is removed from the LAN */
786
775
  
787
 
  switch(event) {
 
776
  switch (event) {
788
777
  default:
789
778
  case AVAHI_BROWSER_FAILURE:
790
779
    
799
788
       the callback function is called the Avahi server will free the
800
789
       resolver for us. */
801
790
    
802
 
    if(!(avahi_s_service_resolver_new(mc->server, interface,
 
791
    if (!(avahi_s_service_resolver_new(mc->server, interface,
803
792
                                       protocol, name, type, domain,
804
793
                                       AVAHI_PROTO_INET6, 0,
805
794
                                       resolve_callback, mc)))
823
812
    AvahiSServiceBrowser *sb = NULL;
824
813
    int error;
825
814
    int ret;
826
 
    intmax_t tmpmax;
827
 
    int numchars;
828
815
    int exitcode = EXIT_SUCCESS;
829
816
    const char *interface = "eth0";
830
817
    struct ifreq network;
833
820
    gid_t gid;
834
821
    char *connect_to = NULL;
835
822
    char tempdir[] = "/tmp/mandosXXXXXX";
836
 
    bool tempdir_created = false;
837
823
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
838
824
    const char *seckey = PATHDIR "/" SECKEY;
839
825
    const char *pubkey = PATHDIR "/" PUBKEY;
841
827
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
842
828
                          .dh_bits = 1024, .priority = "SECURE256"
843
829
                          ":!CTYPE-X.509:+CTYPE-OPENPGP" };
844
 
    bool gnutls_initialized = false;
845
 
    bool gpgme_initialized = false;
846
 
    double delay = 2.5;
 
830
    bool gnutls_initalized = false;
 
831
    bool gpgme_initalized = false;
847
832
    
848
833
    {
849
834
      struct argp_option options[] = {
875
860
          .arg = "STRING",
876
861
          .doc = "GnuTLS priority string for the TLS handshake",
877
862
          .group = 1 },
878
 
        { .name = "delay", .key = 131,
879
 
          .arg = "SECONDS",
880
 
          .doc = "Maximum delay to wait for interface startup",
881
 
          .group = 2 },
882
863
        { .name = NULL }
883
864
      };
884
865
      
885
 
      error_t parse_opt(int key, char *arg,
886
 
                        struct argp_state *state) {
887
 
        switch(key) {
 
866
      error_t parse_opt (int key, char *arg,
 
867
                         struct argp_state *state) {
 
868
        /* Get the INPUT argument from `argp_parse', which we know is
 
869
           a pointer to our plugin list pointer. */
 
870
        switch (key) {
888
871
        case 128:               /* --debug */
889
872
          debug = true;
890
873
          break;
901
884
          pubkey = arg;
902
885
          break;
903
886
        case 129:               /* --dh-bits */
904
 
          ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars);
905
 
          if(ret < 1 or tmpmax != (typeof(mc.dh_bits))tmpmax
906
 
             or arg[numchars] != '\0'){
907
 
            fprintf(stderr, "Bad number of DH bits\n");
 
887
          errno = 0;
 
888
          mc.dh_bits = (unsigned int) strtol(arg, NULL, 10);
 
889
          if (errno){
 
890
            perror("strtol");
908
891
            exit(EXIT_FAILURE);
909
892
          }
910
 
          mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
911
893
          break;
912
894
        case 130:               /* --priority */
913
895
          mc.priority = arg;
914
896
          break;
915
 
        case 131:               /* --delay */
916
 
          ret = sscanf(arg, "%lf%n", &delay, &numchars);
917
 
          if(ret < 1 or arg[numchars] != '\0'){
918
 
            fprintf(stderr, "Bad delay\n");
919
 
            exit(EXIT_FAILURE);
920
 
          }
921
 
          break;
922
897
        case ARGP_KEY_ARG:
923
 
          argp_usage(state);
 
898
          argp_usage (state);
924
899
        case ARGP_KEY_END:
925
900
          break;
926
901
        default:
933
908
                           .args_doc = "",
934
909
                           .doc = "Mandos client -- Get and decrypt"
935
910
                           " passwords from a Mandos server" };
936
 
      ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
937
 
      if(ret == ARGP_ERR_UNKNOWN){
 
911
      ret = argp_parse (&argp, argc, argv, 0, 0, NULL);
 
912
      if (ret == ARGP_ERR_UNKNOWN){
938
913
        fprintf(stderr, "Unknown error while parsing arguments\n");
939
914
        exitcode = EXIT_FAILURE;
940
915
        goto end;
943
918
    
944
919
    /* If the interface is down, bring it up */
945
920
    {
946
 
      /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
947
 
         messages to mess up the prompt */
948
 
      ret = klogctl(8, NULL, 5);
949
 
      if(ret == -1){
950
 
        perror("klogctl");
951
 
      }
952
 
      
953
921
      sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
954
922
      if(sd < 0) {
955
923
        perror("socket");
956
924
        exitcode = EXIT_FAILURE;
957
 
        ret = klogctl(7, NULL, 0);
958
 
        if(ret == -1){
959
 
          perror("klogctl");
960
 
        }
961
925
        goto end;
962
926
      }
963
927
      strcpy(network.ifr_name, interface);
964
928
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
965
929
      if(ret == -1){
966
930
        perror("ioctl SIOCGIFFLAGS");
967
 
        ret = klogctl(7, NULL, 0);
968
 
        if(ret == -1){
969
 
          perror("klogctl");
970
 
        }
971
931
        exitcode = EXIT_FAILURE;
972
932
        goto end;
973
933
      }
977
937
        if(ret == -1){
978
938
          perror("ioctl SIOCSIFFLAGS");
979
939
          exitcode = EXIT_FAILURE;
980
 
          ret = klogctl(7, NULL, 0);
981
 
          if(ret == -1){
982
 
            perror("klogctl");
983
 
          }
984
940
          goto end;
985
941
        }
986
942
      }
987
 
      /* sleep checking until interface is running */
988
 
      for(int i=0; i < delay * 4; i++){
989
 
        ret = ioctl(sd, SIOCGIFFLAGS, &network);
990
 
        if(ret == -1){
991
 
          perror("ioctl SIOCGIFFLAGS");
992
 
        } else if(network.ifr_flags & IFF_RUNNING){
993
 
          break;
994
 
        }
995
 
        struct timespec sleeptime = { .tv_nsec = 250000000 };
996
 
        nanosleep(&sleeptime, NULL);
997
 
      }
998
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
943
      ret = TEMP_FAILURE_RETRY(close(sd));
999
944
      if(ret == -1){
1000
945
        perror("close");
1001
946
      }
1002
 
      /* Restores kernel loglevel to default */
1003
 
      ret = klogctl(7, NULL, 0);
1004
 
      if(ret == -1){
1005
 
        perror("klogctl");
1006
 
      }
1007
947
    }
1008
948
    
1009
949
    uid = getuid();
1010
950
    gid = getgid();
1011
951
    
 
952
    ret = setuid(uid);
 
953
    if (ret == -1){
 
954
      perror("setuid");
 
955
    }
 
956
    
1012
957
    setgid(gid);
1013
 
    if(ret == -1){
 
958
    if (ret == -1){
1014
959
      perror("setgid");
1015
960
    }
1016
961
    
1017
 
    ret = setuid(uid);
1018
 
    if(ret == -1){
1019
 
      perror("setuid");
1020
 
    }
1021
 
    
1022
962
    ret = init_gnutls_global(&mc, pubkey, seckey);
1023
 
    if(ret == -1){
 
963
    if (ret == -1){
1024
964
      fprintf(stderr, "init_gnutls_global failed\n");
1025
965
      exitcode = EXIT_FAILURE;
1026
966
      goto end;
1027
967
    } else {
1028
 
      gnutls_initialized = true;
 
968
      gnutls_initalized = true;
1029
969
    }
1030
970
    
1031
971
    if(mkdtemp(tempdir) == NULL){
1032
972
      perror("mkdtemp");
 
973
      tempdir[0] = '\0';
1033
974
      goto end;
1034
975
    }
1035
 
    tempdir_created = true;
1036
976
    
1037
977
    if(not init_gpgme(&mc, pubkey, seckey, tempdir)){
1038
 
      fprintf(stderr, "init_gpgme failed\n");
 
978
      fprintf(stderr, "gpgme_initalized failed\n");
1039
979
      exitcode = EXIT_FAILURE;
1040
980
      goto end;
1041
981
    } else {
1042
 
      gpgme_initialized = true;
 
982
      gpgme_initalized = true;
1043
983
    }
1044
984
    
1045
985
    if_index = (AvahiIfIndex) if_nametoindex(interface);
1046
986
    if(if_index == 0){
1047
987
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
1048
 
      exitcode = EXIT_FAILURE;
1049
 
      goto end;
 
988
      exit(EXIT_FAILURE);
1050
989
    }
1051
990
    
1052
991
    if(connect_to != NULL){
1058
997
        exitcode = EXIT_FAILURE;
1059
998
        goto end;
1060
999
      }
1061
 
      uint16_t port;
1062
 
      ret = sscanf(address+1, "%" SCNdMAX "%n", &tmpmax, &numchars);
1063
 
      if(ret < 1 or tmpmax != (uint16_t)tmpmax
1064
 
         or address[numchars+1] != '\0'){
1065
 
        fprintf(stderr, "Bad port number\n");
 
1000
      errno = 0;
 
1001
      uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
 
1002
      if(errno){
 
1003
        perror("Bad port number");
1066
1004
        exitcode = EXIT_FAILURE;
1067
1005
        goto end;
1068
1006
      }
1069
 
      port = (uint16_t)tmpmax;
1070
1007
      *address = '\0';
1071
1008
      address = connect_to;
1072
1009
      ret = start_mandos_communication(address, port, if_index, &mc);
1078
1015
      goto end;
1079
1016
    }
1080
1017
    
1081
 
    if(not debug){
 
1018
    if (not debug){
1082
1019
      avahi_set_log_function(empty_log);
1083
1020
    }
1084
1021
    
1087
1024
    
1088
1025
    /* Allocate main Avahi loop object */
1089
1026
    mc.simple_poll = avahi_simple_poll_new();
1090
 
    if(mc.simple_poll == NULL) {
 
1027
    if (mc.simple_poll == NULL) {
1091
1028
        fprintf(stderr, "Avahi: Failed to create simple poll"
1092
1029
                " object.\n");
1093
1030
        exitcode = EXIT_FAILURE;
1113
1050
    }
1114
1051
    
1115
1052
    /* Check if creating the Avahi server object succeeded */
1116
 
    if(mc.server == NULL) {
 
1053
    if (mc.server == NULL) {
1117
1054
        fprintf(stderr, "Failed to create Avahi server: %s\n",
1118
1055
                avahi_strerror(error));
1119
1056
        exitcode = EXIT_FAILURE;
1125
1062
                                     AVAHI_PROTO_INET6,
1126
1063
                                     "_mandos._tcp", NULL, 0,
1127
1064
                                     browse_callback, &mc);
1128
 
    if(sb == NULL) {
 
1065
    if (sb == NULL) {
1129
1066
        fprintf(stderr, "Failed to create service browser: %s\n",
1130
1067
                avahi_strerror(avahi_server_errno(mc.server)));
1131
1068
        exitcode = EXIT_FAILURE;
1134
1071
    
1135
1072
    /* Run the main loop */
1136
1073
    
1137
 
    if(debug){
 
1074
    if (debug){
1138
1075
      fprintf(stderr, "Starting Avahi loop search\n");
1139
1076
    }
1140
 
 
 
1077
    
1141
1078
    avahi_simple_poll_loop(mc.simple_poll);
1142
1079
    
1143
1080
 end:
1144
1081
    
1145
 
    if(debug){
 
1082
    if (debug){
1146
1083
      fprintf(stderr, "%s exiting\n", argv[0]);
1147
1084
    }
1148
1085
    
1149
1086
    /* Cleanup things */
1150
 
    if(sb != NULL)
 
1087
    if (sb != NULL)
1151
1088
        avahi_s_service_browser_free(sb);
1152
1089
    
1153
 
    if(mc.server != NULL)
 
1090
    if (mc.server != NULL)
1154
1091
        avahi_server_free(mc.server);
1155
1092
    
1156
 
    if(mc.simple_poll != NULL)
 
1093
    if (mc.simple_poll != NULL)
1157
1094
        avahi_simple_poll_free(mc.simple_poll);
1158
1095
    
1159
 
    if(gnutls_initialized){
 
1096
    if (gnutls_initalized){
1160
1097
      gnutls_certificate_free_credentials(mc.cred);
1161
 
      gnutls_global_deinit();
 
1098
      gnutls_global_deinit ();
1162
1099
      gnutls_dh_params_deinit(mc.dh_params);
1163
1100
    }
1164
1101
    
1165
 
    if(gpgme_initialized){
 
1102
    if(gpgme_initalized){
1166
1103
      gpgme_release(mc.ctx);
1167
1104
    }
1168
1105
    
1169
1106
    /* Removes the temp directory used by GPGME */
1170
 
    if(tempdir_created){
 
1107
    if(tempdir[0] != '\0'){
1171
1108
      DIR *d;
1172
1109
      struct dirent *direntry;
1173
1110
      d = opendir(tempdir);
1174
1111
      if(d == NULL){
1175
 
        if(errno != ENOENT){
1176
 
          perror("opendir");
1177
 
        }
 
1112
        perror("opendir");
1178
1113
      } else {
1179
1114
        while(true){
1180
1115
          direntry = readdir(d);
1181
1116
          if(direntry == NULL){
1182
1117
            break;
1183
1118
          }
1184
 
          /* Skip "." and ".." */
1185
 
          if(direntry->d_name[0] == '.'
1186
 
             and (direntry->d_name[1] == '\0'
1187
 
                  or (direntry->d_name[1] == '.'
1188
 
                      and direntry->d_name[2] == '\0'))){
1189
 
            continue;
1190
 
          }
1191
 
          char *fullname = NULL;
1192
 
          ret = asprintf(&fullname, "%s/%s", tempdir,
1193
 
                         direntry->d_name);
1194
 
          if(ret < 0){
1195
 
            perror("asprintf");
1196
 
            continue;
1197
 
          }
1198
 
          ret = remove(fullname);
1199
 
          if(ret == -1){
1200
 
            fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1201
 
                    strerror(errno));
1202
 
          }
1203
 
          free(fullname);
 
1119
          if (direntry->d_type == DT_REG){
 
1120
            char *fullname = NULL;
 
1121
            ret = asprintf(&fullname, "%s/%s", tempdir,
 
1122
                           direntry->d_name);
 
1123
            if(ret < 0){
 
1124
              perror("asprintf");
 
1125
              continue;
 
1126
            }
 
1127
            ret = unlink(fullname);
 
1128
            if(ret == -1){
 
1129
              fprintf(stderr, "unlink(\"%s\"): %s",
 
1130
                      fullname, strerror(errno));
 
1131
            }
 
1132
            free(fullname);
 
1133
          }
1204
1134
        }
1205
1135
        closedir(d);
1206
1136
      }
1207
1137
      ret = rmdir(tempdir);
1208
 
      if(ret == -1 and errno != ENOENT){
 
1138
      if(ret == -1){
1209
1139
        perror("rmdir");
1210
1140
      }
1211
1141
    }
1212
 
    
 
1142
          
1213
1143
    return exitcode;
1214
1144
}