/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:
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
11
 * Everything else is
12
 
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
 
12
 * Copyright © 2008 Teddy Hogeborn
 
13
 * Copyright © 2008 Björn Påhlsson
13
14
 * 
14
15
 * This program is free software: you can redistribute it and/or
15
16
 * modify it under the terms of the GNU General Public License as
32
33
#define _LARGEFILE_SOURCE
33
34
#define _FILE_OFFSET_BITS 64
34
35
 
35
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
 
36
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
36
37
 
37
 
#include <stdio.h>              /* fprintf(), stderr, fwrite(), stdout,
38
 
                                   ferror() */
 
38
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
 
39
                                   stdout, ferror() */
39
40
#include <stdint.h>             /* uint16_t, uint32_t */
40
41
#include <stddef.h>             /* NULL, size_t, ssize_t */
41
42
#include <stdlib.h>             /* free(), EXIT_SUCCESS, EXIT_FAILURE,
42
43
                                   srand() */
43
44
#include <stdbool.h>            /* bool, true */
44
45
#include <string.h>             /* memset(), strcmp(), strlen(),
45
 
                                   strerror(), memcpy(), strcpy() */
 
46
                                   strerror(), asprintf(), strcpy() */
46
47
#include <sys/ioctl.h>          /* ioctl */
47
 
#include <net/if.h>             /* ifreq, SIOCGIFFLAGS, SIOCSIFFLAGS,
48
 
                                   IFF_UP */
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 */
 
51
                                   uid_t, gid_t, open(), opendir(), DIR */
 
52
#include <sys/stat.h>           /* open() */
53
53
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
54
54
                                   struct in6_addr, inet_pton(),
55
55
                                   connect() */
 
56
#include <fcntl.h>              /* open() */
 
57
#include <dirent.h>             /* opendir(), struct dirent, readdir() */
 
58
#include <inttypes.h>           /* PRIu16 */
56
59
#include <assert.h>             /* assert() */
57
60
#include <errno.h>              /* perror(), errno */
58
61
#include <time.h>               /* time() */
59
62
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
60
63
                                   SIOCSIFFLAGS, if_indextoname(),
61
64
                                   if_nametoindex(), IF_NAMESIZE */
 
65
#include <netinet/in.h>
62
66
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
63
67
                                   getuid(), getgid(), setuid(),
64
68
                                   setgid() */
65
 
#include <netinet/in.h>
66
69
#include <arpa/inet.h>          /* inet_pton(), htons */
67
70
#include <iso646.h>             /* not, and */
68
71
#include <argp.h>               /* struct argp_option, error_t, struct
82
85
#include <avahi-common/error.h>
83
86
 
84
87
/* GnuTLS */
85
 
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and functions
 
88
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
 
89
                                   functions:
86
90
                                   gnutls_*
87
91
                                   init_gnutls_session(),
88
92
                                   GNUTLS_* */
90
94
                                   GNUTLS_OPENPGP_FMT_BASE64 */
91
95
 
92
96
/* GPGME */
93
 
#include <gpgme.h>              /* All GPGME types, constants and functions
 
97
#include <gpgme.h>              /* All GPGME types, constants and
 
98
                                   functions:
94
99
                                   gpgme_*
95
100
                                   GPGME_PROTOCOL_OpenPGP,
96
101
                                   GPG_ERR_NO_* */
97
102
 
98
103
#define BUFFER_SIZE 256
99
104
 
 
105
#define PATHDIR "/conf/conf.d/mandos"
 
106
#define SECKEY "seckey.txt"
 
107
#define PUBKEY "pubkey.txt"
 
108
 
100
109
bool debug = false;
101
 
static const char *keydir = "/conf/conf.d/mandos";
102
110
static const char mandos_protocol_version[] = "1";
103
 
const char *argp_program_version = "mandosclient 0.9";
 
111
const char *argp_program_version = "mandos-client " VERSION;
104
112
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
105
113
 
106
114
/* Used for passing in values through the Avahi callback functions */
111
119
  unsigned int dh_bits;
112
120
  gnutls_dh_params_t dh_params;
113
121
  const char *priority;
 
122
  gpgme_ctx_t ctx;
114
123
} mandos_context;
115
124
 
116
125
/*
131
140
}
132
141
 
133
142
/* 
134
 
 * Decrypt OpenPGP data using keyrings in HOMEDIR.
135
 
 * Returns -1 on error
 
143
 * Initialize GPGME.
136
144
 */
137
 
static ssize_t pgp_packet_decrypt (const char *cryptotext,
138
 
                                   size_t crypto_size,
139
 
                                   char **plaintext,
140
 
                                   const char *homedir){
141
 
  gpgme_data_t dh_crypto, dh_plain;
142
 
  gpgme_ctx_t ctx;
 
145
static bool init_gpgme(mandos_context *mc, const char *seckey,
 
146
                       const char *pubkey, const char *tempdir){
 
147
  int ret;
143
148
  gpgme_error_t rc;
144
 
  ssize_t ret;
145
 
  size_t plaintext_capacity = 0;
146
 
  ssize_t plaintext_length = 0;
147
149
  gpgme_engine_info_t engine_info;
148
150
  
 
151
  
 
152
  /*
 
153
   * Helper function to insert pub and seckey to the enigne keyring.
 
154
   */
 
155
  bool import_key(const char *filename){
 
156
    int fd;
 
157
    gpgme_data_t pgp_data;
 
158
    
 
159
    fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
 
160
    if(fd == -1){
 
161
      perror("open");
 
162
      return false;
 
163
    }
 
164
    
 
165
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
 
166
    if (rc != GPG_ERR_NO_ERROR){
 
167
      fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
 
168
              gpgme_strsource(rc), gpgme_strerror(rc));
 
169
      return false;
 
170
    }
 
171
    
 
172
    rc = gpgme_op_import(mc->ctx, pgp_data);
 
173
    if (rc != GPG_ERR_NO_ERROR){
 
174
      fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
 
175
              gpgme_strsource(rc), gpgme_strerror(rc));
 
176
      return false;
 
177
    }
 
178
    
 
179
    ret = TEMP_FAILURE_RETRY(close(fd));
 
180
    if(ret == -1){
 
181
      perror("close");
 
182
    }
 
183
    gpgme_data_release(pgp_data);
 
184
    return true;
 
185
  }
 
186
  
149
187
  if (debug){
150
 
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
 
188
    fprintf(stderr, "Initialize gpgme\n");
151
189
  }
152
190
  
153
191
  /* Init GPGME */
156
194
  if (rc != GPG_ERR_NO_ERROR){
157
195
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
158
196
            gpgme_strsource(rc), gpgme_strerror(rc));
159
 
    return -1;
 
197
    return false;
160
198
  }
161
199
  
162
 
  /* Set GPGME home directory for the OpenPGP engine only */
 
200
    /* Set GPGME home directory for the OpenPGP engine only */
163
201
  rc = gpgme_get_engine_info (&engine_info);
164
202
  if (rc != GPG_ERR_NO_ERROR){
165
203
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
166
204
            gpgme_strsource(rc), gpgme_strerror(rc));
167
 
    return -1;
 
205
    return false;
168
206
  }
169
207
  while(engine_info != NULL){
170
208
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
171
209
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
172
 
                            engine_info->file_name, homedir);
 
210
                            engine_info->file_name, tempdir);
173
211
      break;
174
212
    }
175
213
    engine_info = engine_info->next;
176
214
  }
177
215
  if(engine_info == NULL){
178
 
    fprintf(stderr, "Could not set GPGME home dir to %s\n", homedir);
179
 
    return -1;
 
216
    fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
 
217
    return false;
 
218
  }
 
219
  
 
220
  /* Create new GPGME "context" */
 
221
  rc = gpgme_new(&(mc->ctx));
 
222
  if (rc != GPG_ERR_NO_ERROR){
 
223
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
 
224
            gpgme_strsource(rc), gpgme_strerror(rc));
 
225
    return false;
 
226
  }
 
227
  
 
228
  if (not import_key(pubkey) or not import_key(seckey)){
 
229
    return false;
 
230
  }
 
231
  
 
232
  return true; 
 
233
}
 
234
 
 
235
/* 
 
236
 * Decrypt OpenPGP data.
 
237
 * Returns -1 on error
 
238
 */
 
239
static ssize_t pgp_packet_decrypt (const mandos_context *mc,
 
240
                                   const char *cryptotext,
 
241
                                   size_t crypto_size,
 
242
                                   char **plaintext){
 
243
  gpgme_data_t dh_crypto, dh_plain;
 
244
  gpgme_error_t rc;
 
245
  ssize_t ret;
 
246
  size_t plaintext_capacity = 0;
 
247
  ssize_t plaintext_length = 0;
 
248
  
 
249
  if (debug){
 
250
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
180
251
  }
181
252
  
182
253
  /* Create new GPGME data buffer from memory cryptotext */
197
268
    return -1;
198
269
  }
199
270
  
200
 
  /* Create new GPGME "context" */
201
 
  rc = gpgme_new(&ctx);
202
 
  if (rc != GPG_ERR_NO_ERROR){
203
 
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
204
 
            gpgme_strsource(rc), gpgme_strerror(rc));
205
 
    plaintext_length = -1;
206
 
    goto decrypt_end;
207
 
  }
208
 
  
209
271
  /* Decrypt data from the cryptotext data buffer to the plaintext
210
272
     data buffer */
211
 
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
 
273
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
212
274
  if (rc != GPG_ERR_NO_ERROR){
213
275
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
214
276
            gpgme_strsource(rc), gpgme_strerror(rc));
215
277
    plaintext_length = -1;
 
278
    if (debug){
 
279
      gpgme_decrypt_result_t result;
 
280
      result = gpgme_op_decrypt_result(mc->ctx);
 
281
      if (result == NULL){
 
282
        fprintf(stderr, "gpgme_op_decrypt_result failed\n");
 
283
      } else {
 
284
        fprintf(stderr, "Unsupported algorithm: %s\n",
 
285
                result->unsupported_algorithm);
 
286
        fprintf(stderr, "Wrong key usage: %u\n",
 
287
                result->wrong_key_usage);
 
288
        if(result->file_name != NULL){
 
289
          fprintf(stderr, "File name: %s\n", result->file_name);
 
290
        }
 
291
        gpgme_recipient_t recipient;
 
292
        recipient = result->recipients;
 
293
        if(recipient){
 
294
          while(recipient != NULL){
 
295
            fprintf(stderr, "Public key algorithm: %s\n",
 
296
                    gpgme_pubkey_algo_name(recipient->pubkey_algo));
 
297
            fprintf(stderr, "Key ID: %s\n", recipient->keyid);
 
298
            fprintf(stderr, "Secret key available: %s\n",
 
299
                    recipient->status == GPG_ERR_NO_SECKEY
 
300
                    ? "No" : "Yes");
 
301
            recipient = recipient->next;
 
302
          }
 
303
        }
 
304
      }
 
305
    }
216
306
    goto decrypt_end;
217
307
  }
218
308
  
220
310
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
221
311
  }
222
312
  
223
 
  if (debug){
224
 
    gpgme_decrypt_result_t result;
225
 
    result = gpgme_op_decrypt_result(ctx);
226
 
    if (result == NULL){
227
 
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
228
 
    } else {
229
 
      fprintf(stderr, "Unsupported algorithm: %s\n",
230
 
              result->unsupported_algorithm);
231
 
      fprintf(stderr, "Wrong key usage: %d\n",
232
 
              result->wrong_key_usage);
233
 
      if(result->file_name != NULL){
234
 
        fprintf(stderr, "File name: %s\n", result->file_name);
235
 
      }
236
 
      gpgme_recipient_t recipient;
237
 
      recipient = result->recipients;
238
 
      if(recipient){
239
 
        while(recipient != NULL){
240
 
          fprintf(stderr, "Public key algorithm: %s\n",
241
 
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
242
 
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
243
 
          fprintf(stderr, "Secret key available: %s\n",
244
 
                  recipient->status == GPG_ERR_NO_SECKEY
245
 
                  ? "No" : "Yes");
246
 
          recipient = recipient->next;
247
 
        }
248
 
      }
249
 
    }
250
 
  }
251
 
  
252
313
  /* Seek back to the beginning of the GPGME plaintext data buffer */
253
314
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
254
 
    perror("pgpme_data_seek");
 
315
    perror("gpgme_data_seek");
255
316
    plaintext_length = -1;
256
317
    goto decrypt_end;
257
318
  }
281
342
    }
282
343
    plaintext_length += ret;
283
344
  }
284
 
 
 
345
  
285
346
  if(debug){
286
347
    fprintf(stderr, "Decrypted password is: ");
287
348
    for(ssize_t i = 0; i < plaintext_length; i++){
301
362
}
302
363
 
303
364
static const char * safer_gnutls_strerror (int value) {
304
 
  const char *ret = gnutls_strerror (value);
 
365
  const char *ret = gnutls_strerror (value); /* Spurious warning */
305
366
  if (ret == NULL)
306
367
    ret = "(unknown)";
307
368
  return ret;
314
375
}
315
376
 
316
377
static int init_gnutls_global(mandos_context *mc,
317
 
                              const char *pubkeyfile,
318
 
                              const char *seckeyfile){
 
378
                              const char *pubkeyfilename,
 
379
                              const char *seckeyfilename){
319
380
  int ret;
320
381
  
321
382
  if(debug){
340
401
  /* OpenPGP credentials */
341
402
  gnutls_certificate_allocate_credentials(&mc->cred);
342
403
  if (ret != GNUTLS_E_SUCCESS){
343
 
    fprintf (stderr, "GnuTLS memory error: %s\n",
 
404
    fprintf (stderr, "GnuTLS memory error: %s\n", /* Spurious
 
405
                                                     warning */
344
406
             safer_gnutls_strerror(ret));
345
407
    gnutls_global_deinit ();
346
408
    return -1;
347
409
  }
348
410
  
349
411
  if(debug){
350
 
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
351
 
            " and keyfile %s as GnuTLS credentials\n", pubkeyfile,
352
 
            seckeyfile);
 
412
    fprintf(stderr, "Attempting to use OpenPGP public key %s and"
 
413
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
 
414
            seckeyfilename);
353
415
  }
354
416
  
355
417
  ret = gnutls_certificate_set_openpgp_key_file
356
 
    (mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
 
418
    (mc->cred, pubkeyfilename, seckeyfilename,
 
419
     GNUTLS_OPENPGP_FMT_BASE64);
357
420
  if (ret != GNUTLS_E_SUCCESS) {
358
421
    fprintf(stderr,
359
422
            "Error[%d] while reading the OpenPGP key pair ('%s',"
360
 
            " '%s')\n", ret, pubkeyfile, seckeyfile);
361
 
    fprintf(stdout, "The GnuTLS error is: %s\n",
 
423
            " '%s')\n", ret, pubkeyfilename, seckeyfilename);
 
424
    fprintf(stderr, "The GnuTLS error is: %s\n",
362
425
            safer_gnutls_strerror(ret));
363
426
    goto globalfail;
364
427
  }
378
441
  }
379
442
  
380
443
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
381
 
 
 
444
  
382
445
  return 0;
383
 
 
 
446
  
384
447
 globalfail:
385
 
 
 
448
  
386
449
  gnutls_certificate_free_credentials(mc->cred);
387
450
  gnutls_global_deinit();
 
451
  gnutls_dh_params_deinit(mc->dh_params);
388
452
  return -1;
389
 
 
390
453
}
391
454
 
392
455
static int init_gnutls_session(mandos_context *mc,
455
518
  }
456
519
  
457
520
  if(debug){
458
 
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
459
 
            ip, port);
 
521
    fprintf(stderr, "Setting up a tcp connection to %s, port %" PRIu16
 
522
            "\n", ip, port);
460
523
  }
461
524
  
462
525
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
464
527
    perror("socket");
465
528
    return -1;
466
529
  }
467
 
 
 
530
  
468
531
  if(debug){
469
532
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
470
533
      perror("if_indextoname");
473
536
    fprintf(stderr, "Binding to interface %s\n", interface);
474
537
  }
475
538
  
476
 
  memset(&to,0,sizeof(to));     /* Spurious warning */
 
539
  memset(&to, 0, sizeof(to));
477
540
  to.in6.sin6_family = AF_INET6;
478
541
  /* It would be nice to have a way to detect if we were passed an
479
542
     IPv4 address here.   Now we assume an IPv6 address. */
486
549
    fprintf(stderr, "Bad address: %s\n", ip);
487
550
    return -1;
488
551
  }
489
 
  to.in6.sin6_port = htons(port);       /* Spurious warning */
 
552
  to.in6.sin6_port = htons(port); /* Spurious warning */
490
553
  
491
554
  to.in6.sin6_scope_id = (uint32_t)if_index;
492
555
  
493
556
  if(debug){
494
 
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
 
557
    fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
 
558
            port);
495
559
    char addrstr[INET6_ADDRSTRLEN] = "";
496
560
    if(inet_ntop(to.in6.sin6_family, &(to.in6.sin6_addr), addrstr,
497
561
                 sizeof(addrstr)) == NULL){
508
572
    perror("connect");
509
573
    return -1;
510
574
  }
511
 
 
 
575
  
512
576
  const char *out = mandos_protocol_version;
513
577
  written = 0;
514
578
  while (true){
532
596
      }
533
597
    }
534
598
  }
535
 
 
 
599
  
536
600
  if(debug){
537
601
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
538
602
  }
539
603
  
540
604
  gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
541
 
 
 
605
  
542
606
  do{
543
607
    ret = gnutls_handshake (session);
544
608
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
558
622
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
559
623
            ip);
560
624
  }
561
 
 
 
625
  
562
626
  while(true){
563
627
    buffer_capacity = adjustbuffer(&buffer, buffer_length,
564
628
                                   buffer_capacity);
608
672
  gnutls_bye (session, GNUTLS_SHUT_RDWR);
609
673
  
610
674
  if (buffer_length > 0){
611
 
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
 
675
    decrypted_buffer_size = pgp_packet_decrypt(mc, buffer,
612
676
                                               buffer_length,
613
 
                                               &decrypted_buffer,
614
 
                                               keydir);
 
677
                                               &decrypted_buffer);
615
678
    if (decrypted_buffer_size >= 0){
616
679
      written = 0;
617
680
      while(written < (size_t) decrypted_buffer_size){
632
695
    } else {
633
696
      retval = -1;
634
697
    }
 
698
  } else {
 
699
    retval = -1;
635
700
  }
636
701
  
637
702
  /* Shutdown procedure */
638
703
  
639
704
 mandos_end:
640
705
  free(buffer);
641
 
  close(tcp_sd);
 
706
  ret = TEMP_FAILURE_RETRY(close(tcp_sd));
 
707
  if(ret == -1){
 
708
    perror("close");
 
709
  }
642
710
  gnutls_deinit (session);
643
711
  return retval;
644
712
}
658
726
                             flags,
659
727
                             void* userdata) {
660
728
  mandos_context *mc = userdata;
661
 
  assert(r);                    /* Spurious warning */
 
729
  assert(r);
662
730
  
663
731
  /* Called whenever a service has been resolved successfully or
664
732
     timed out */
676
744
      char ip[AVAHI_ADDRESS_STR_MAX];
677
745
      avahi_address_snprint(ip, sizeof(ip), address);
678
746
      if(debug){
679
 
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %d) on"
680
 
                " port %d\n", name, host_name, ip, interface, port);
 
747
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
748
                PRIu16 ") on port %d\n", name, host_name, ip,
 
749
                interface, port);
681
750
      }
682
751
      int ret = start_mandos_communication(ip, port, interface, mc);
683
752
      if (ret == 0){
684
 
        exit(EXIT_SUCCESS);
 
753
        avahi_simple_poll_quit(mc->simple_poll);
685
754
      }
686
755
    }
687
756
  }
699
768
                             flags,
700
769
                             void* userdata) {
701
770
  mandos_context *mc = userdata;
702
 
  assert(b);                    /* Spurious warning */
 
771
  assert(b);
703
772
  
704
773
  /* Called whenever a new services becomes available on the LAN or
705
774
     is removed from the LAN */
739
808
  }
740
809
}
741
810
 
742
 
/* Combines file name and path and returns the malloced new
743
 
   string. some sane checks could/should be added */
744
 
static const char *combinepath(const char *first, const char *second){
745
 
  size_t f_len = strlen(first);
746
 
  size_t s_len = strlen(second);
747
 
  char *tmp = malloc(f_len + s_len + 2);
748
 
  if (tmp == NULL){
749
 
    return NULL;
750
 
  }
751
 
  if(f_len > 0){
752
 
    memcpy(tmp, first, f_len);  /* Spurious warning */
753
 
  }
754
 
  tmp[f_len] = '/';
755
 
  if(s_len > 0){
756
 
    memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
757
 
  }
758
 
  tmp[f_len + 1 + s_len] = '\0';
759
 
  return tmp;
760
 
}
761
 
 
762
 
 
763
811
int main(int argc, char *argv[]){
764
812
    AvahiSServiceBrowser *sb = NULL;
765
813
    int error;
771
819
    uid_t uid;
772
820
    gid_t gid;
773
821
    char *connect_to = NULL;
 
822
    char tempdir[] = "/tmp/mandosXXXXXX";
774
823
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
775
 
    const char *pubkeyfile = "pubkey.txt";
776
 
    const char *seckeyfile = "seckey.txt";
 
824
    const char *seckey = PATHDIR "/" SECKEY;
 
825
    const char *pubkey = PATHDIR "/" PUBKEY;
 
826
    
777
827
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
778
 
                          .dh_bits = 1024, .priority = "SECURE256"};
 
828
                          .dh_bits = 1024, .priority = "SECURE256"
 
829
                          ":!CTYPE-X.509:+CTYPE-OPENPGP" };
779
830
    bool gnutls_initalized = false;
 
831
    bool gpgme_initalized = false;
780
832
    
781
833
    {
782
834
      struct argp_option options[] = {
783
835
        { .name = "debug", .key = 128,
784
836
          .doc = "Debug mode", .group = 3 },
785
837
        { .name = "connect", .key = 'c',
786
 
          .arg = "IP",
787
 
          .doc = "Connect directly to a sepcified mandos server",
 
838
          .arg = "ADDRESS:PORT",
 
839
          .doc = "Connect directly to a specific Mandos server",
788
840
          .group = 1 },
789
841
        { .name = "interface", .key = 'i',
790
 
          .arg = "INTERFACE",
791
 
          .doc = "Interface that Avahi will conntect through",
792
 
          .group = 1 },
793
 
        { .name = "keydir", .key = 'd',
794
 
          .arg = "KEYDIR",
795
 
          .doc = "Directory where the openpgp keyring is",
 
842
          .arg = "NAME",
 
843
          .doc = "Interface that will be used to search for Mandos"
 
844
          " servers",
796
845
          .group = 1 },
797
846
        { .name = "seckey", .key = 's',
798
 
          .arg = "SECKEY",
799
 
          .doc = "Secret openpgp key for gnutls authentication",
 
847
          .arg = "FILE",
 
848
          .doc = "OpenPGP secret key file base name",
800
849
          .group = 1 },
801
850
        { .name = "pubkey", .key = 'p',
802
 
          .arg = "PUBKEY",
803
 
          .doc = "Public openpgp key for gnutls authentication",
 
851
          .arg = "FILE",
 
852
          .doc = "OpenPGP public key file base name",
804
853
          .group = 2 },
805
854
        { .name = "dh-bits", .key = 129,
806
855
          .arg = "BITS",
807
 
          .doc = "dh-bits to use in gnutls communication",
 
856
          .doc = "Bit length of the prime number used in the"
 
857
          " Diffie-Hellman key exchange",
808
858
          .group = 2 },
809
859
        { .name = "priority", .key = 130,
810
 
          .arg = "PRIORITY",
811
 
          .doc = "GNUTLS priority", .group = 1 },
 
860
          .arg = "STRING",
 
861
          .doc = "GnuTLS priority string for the TLS handshake",
 
862
          .group = 1 },
812
863
        { .name = NULL }
813
864
      };
814
 
 
815
865
      
816
866
      error_t parse_opt (int key, char *arg,
817
867
                         struct argp_state *state) {
818
868
        /* Get the INPUT argument from `argp_parse', which we know is
819
869
           a pointer to our plugin list pointer. */
820
870
        switch (key) {
821
 
        case 128:
 
871
        case 128:               /* --debug */
822
872
          debug = true;
823
873
          break;
824
 
        case 'c':
 
874
        case 'c':               /* --connect */
825
875
          connect_to = arg;
826
876
          break;
827
 
        case 'i':
 
877
        case 'i':               /* --interface */
828
878
          interface = arg;
829
879
          break;
830
 
        case 'd':
831
 
          keydir = arg;
832
 
          break;
833
 
        case 's':
834
 
          seckeyfile = arg;
835
 
          break;
836
 
        case 'p':
837
 
          pubkeyfile = arg;
838
 
          break;
839
 
        case 129:
 
880
        case 's':               /* --seckey */
 
881
          seckey = arg;
 
882
          break;
 
883
        case 'p':               /* --pubkey */
 
884
          pubkey = arg;
 
885
          break;
 
886
        case 129:               /* --dh-bits */
840
887
          errno = 0;
841
888
          mc.dh_bits = (unsigned int) strtol(arg, NULL, 10);
842
889
          if (errno){
844
891
            exit(EXIT_FAILURE);
845
892
          }
846
893
          break;
847
 
        case 130:
 
894
        case 130:               /* --priority */
848
895
          mc.priority = arg;
849
896
          break;
850
897
        case ARGP_KEY_ARG:
851
898
          argp_usage (state);
 
899
        case ARGP_KEY_END:
852
900
          break;
853
 
          case ARGP_KEY_END:
854
 
            break;
855
901
        default:
856
902
          return ARGP_ERR_UNKNOWN;
857
903
        }
858
904
        return 0;
859
905
      }
860
 
 
 
906
      
861
907
      struct argp argp = { .options = options, .parser = parse_opt,
862
908
                           .args_doc = "",
863
909
                           .doc = "Mandos client -- Get and decrypt"
864
 
                           " passwords from mandos server" };
 
910
                           " passwords from a Mandos server" };
865
911
      ret = argp_parse (&argp, argc, argv, 0, 0, NULL);
866
912
      if (ret == ARGP_ERR_UNKNOWN){
867
 
        fprintf(stderr, "Unkown error while parsing arguments\n");
868
 
        exitcode = EXIT_FAILURE;
869
 
        goto end;
870
 
      }
871
 
    }
872
 
      
873
 
    pubkeyfile = combinepath(keydir, pubkeyfile);
874
 
    if (pubkeyfile == NULL){
875
 
      perror("combinepath");
876
 
      exitcode = EXIT_FAILURE;
877
 
      goto end;
878
 
    }
879
 
    
880
 
    seckeyfile = combinepath(keydir, seckeyfile);
881
 
    if (seckeyfile == NULL){
882
 
      perror("combinepath");
883
 
      goto end;
884
 
    }
885
 
 
886
 
    ret = init_gnutls_global(&mc, pubkeyfile, seckeyfile);
887
 
    if (ret == -1){
888
 
      fprintf(stderr, "init_gnutls_global\n");
889
 
      goto end;
890
 
    } else {
891
 
      gnutls_initalized = true;
892
 
    }
893
 
 
 
913
        fprintf(stderr, "Unknown error while parsing arguments\n");
 
914
        exitcode = EXIT_FAILURE;
 
915
        goto end;
 
916
      }
 
917
    }
 
918
    
 
919
    /* If the interface is down, bring it up */
 
920
    {
 
921
      sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
922
      if(sd < 0) {
 
923
        perror("socket");
 
924
        exitcode = EXIT_FAILURE;
 
925
        goto end;
 
926
      }
 
927
      strcpy(network.ifr_name, interface);
 
928
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
929
      if(ret == -1){
 
930
        perror("ioctl SIOCGIFFLAGS");
 
931
        exitcode = EXIT_FAILURE;
 
932
        goto end;
 
933
      }
 
934
      if((network.ifr_flags & IFF_UP) == 0){
 
935
        network.ifr_flags |= IFF_UP;
 
936
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
937
        if(ret == -1){
 
938
          perror("ioctl SIOCSIFFLAGS");
 
939
          exitcode = EXIT_FAILURE;
 
940
          goto end;
 
941
        }
 
942
      }
 
943
      ret = TEMP_FAILURE_RETRY(close(sd));
 
944
      if(ret == -1){
 
945
        perror("close");
 
946
      }
 
947
    }
 
948
    
894
949
    uid = getuid();
895
950
    gid = getgid();
896
 
 
 
951
    
897
952
    ret = setuid(uid);
898
953
    if (ret == -1){
899
954
      perror("setuid");
904
959
      perror("setgid");
905
960
    }
906
961
    
 
962
    ret = init_gnutls_global(&mc, pubkey, seckey);
 
963
    if (ret == -1){
 
964
      fprintf(stderr, "init_gnutls_global failed\n");
 
965
      exitcode = EXIT_FAILURE;
 
966
      goto end;
 
967
    } else {
 
968
      gnutls_initalized = true;
 
969
    }
 
970
    
 
971
    if(mkdtemp(tempdir) == NULL){
 
972
      perror("mkdtemp");
 
973
      tempdir[0] = '\0';
 
974
      goto end;
 
975
    }
 
976
    
 
977
    if(not init_gpgme(&mc, pubkey, seckey, tempdir)){
 
978
      fprintf(stderr, "gpgme_initalized failed\n");
 
979
      exitcode = EXIT_FAILURE;
 
980
      goto end;
 
981
    } else {
 
982
      gpgme_initalized = true;
 
983
    }
 
984
    
907
985
    if_index = (AvahiIfIndex) if_nametoindex(interface);
908
986
    if(if_index == 0){
909
987
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
937
1015
      goto end;
938
1016
    }
939
1017
    
940
 
    /* If the interface is down, bring it up */
941
 
    {
942
 
      sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
943
 
      if(sd < 0) {
944
 
        perror("socket");
945
 
        exitcode = EXIT_FAILURE;
946
 
        goto end;
947
 
      }
948
 
      strcpy(network.ifr_name, interface); /* Spurious warning */
949
 
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
950
 
      if(ret == -1){
951
 
        perror("ioctl SIOCGIFFLAGS");
952
 
        exitcode = EXIT_FAILURE;
953
 
        goto end;
954
 
      }
955
 
      if((network.ifr_flags & IFF_UP) == 0){
956
 
        network.ifr_flags |= IFF_UP;
957
 
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
958
 
        if(ret == -1){
959
 
          perror("ioctl SIOCSIFFLAGS");
960
 
          exitcode = EXIT_FAILURE;
961
 
          goto end;
962
 
        }
963
 
      }
964
 
      close(sd);
965
 
    }
966
 
    
967
1018
    if (not debug){
968
1019
      avahi_set_log_function(empty_log);
969
1020
    }
979
1030
        exitcode = EXIT_FAILURE;
980
1031
        goto end;
981
1032
    }
982
 
 
 
1033
    
983
1034
    {
984
1035
      AvahiServerConfig config;
985
1036
      /* Do not publish any local Zeroconf records */
988
1039
      config.publish_addresses = 0;
989
1040
      config.publish_workstation = 0;
990
1041
      config.publish_domain = 0;
991
 
 
 
1042
      
992
1043
      /* Allocate a new server */
993
1044
      mc.server = avahi_server_new(avahi_simple_poll_get
994
1045
                                   (mc.simple_poll), &config, NULL,
995
1046
                                   NULL, &error);
996
 
    
 
1047
      
997
1048
      /* Free the Avahi configuration data */
998
1049
      avahi_server_config_free(&config);
999
1050
    }
1019
1070
    }
1020
1071
    
1021
1072
    /* Run the main loop */
1022
 
 
 
1073
    
1023
1074
    if (debug){
1024
1075
      fprintf(stderr, "Starting Avahi loop search\n");
1025
1076
    }
1027
1078
    avahi_simple_poll_loop(mc.simple_poll);
1028
1079
    
1029
1080
 end:
1030
 
 
 
1081
    
1031
1082
    if (debug){
1032
1083
      fprintf(stderr, "%s exiting\n", argv[0]);
1033
1084
    }
1038
1089
    
1039
1090
    if (mc.server != NULL)
1040
1091
        avahi_server_free(mc.server);
1041
 
 
 
1092
    
1042
1093
    if (mc.simple_poll != NULL)
1043
1094
        avahi_simple_poll_free(mc.simple_poll);
1044
 
    free(pubkeyfile);
1045
 
    free(seckeyfile);
1046
 
 
 
1095
    
1047
1096
    if (gnutls_initalized){
1048
1097
      gnutls_certificate_free_credentials(mc.cred);
1049
1098
      gnutls_global_deinit ();
1050
 
    }
1051
 
    
 
1099
      gnutls_dh_params_deinit(mc.dh_params);
 
1100
    }
 
1101
    
 
1102
    if(gpgme_initalized){
 
1103
      gpgme_release(mc.ctx);
 
1104
    }
 
1105
    
 
1106
    /* Removes the temp directory used by GPGME */
 
1107
    if(tempdir[0] != '\0'){
 
1108
      DIR *d;
 
1109
      struct dirent *direntry;
 
1110
      d = opendir(tempdir);
 
1111
      if(d == NULL){
 
1112
        perror("opendir");
 
1113
      } else {
 
1114
        while(true){
 
1115
          direntry = readdir(d);
 
1116
          if(direntry == NULL){
 
1117
            break;
 
1118
          }
 
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
          }
 
1134
        }
 
1135
        closedir(d);
 
1136
      }
 
1137
      ret = rmdir(tempdir);
 
1138
      if(ret == -1){
 
1139
        perror("rmdir");
 
1140
      }
 
1141
    }
 
1142
          
1052
1143
    return exitcode;
1053
1144
}