/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>
38
 
#include <assert.h>
39
 
#include <stdlib.h>
40
 
#include <time.h>
41
 
#include <net/if.h>             /* if_nametoindex */
42
 
#include <sys/ioctl.h>          /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
43
 
                                   SIOCSIFFLAGS */
 
38
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
 
39
                                   stdout, ferror() */
 
40
#include <stdint.h>             /* uint16_t, uint32_t */
 
41
#include <stddef.h>             /* NULL, size_t, ssize_t */
 
42
#include <stdlib.h>             /* free(), EXIT_SUCCESS, EXIT_FAILURE,
 
43
                                   srand() */
 
44
#include <stdbool.h>            /* bool, true */
 
45
#include <string.h>             /* memset(), strcmp(), strlen(),
 
46
                                   strerror(), asprintf(), strcpy() */
 
47
#include <sys/ioctl.h>          /* ioctl */
 
48
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
 
49
                                   sockaddr_in6, PF_INET6,
 
50
                                   SOCK_STREAM, INET6_ADDRSTRLEN,
 
51
                                   uid_t, gid_t, open(), opendir(), DIR */
 
52
#include <sys/stat.h>           /* open() */
 
53
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
 
54
                                   struct in6_addr, inet_pton(),
 
55
                                   connect() */
 
56
#include <fcntl.h>              /* open() */
 
57
#include <dirent.h>             /* opendir(), struct dirent, readdir() */
 
58
#include <inttypes.h>           /* PRIu16 */
 
59
#include <assert.h>             /* assert() */
 
60
#include <errno.h>              /* perror(), errno */
 
61
#include <time.h>               /* time() */
44
62
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
45
 
                                   SIOCSIFFLAGS */
 
63
                                   SIOCSIFFLAGS, if_indextoname(),
 
64
                                   if_nametoindex(), IF_NAMESIZE */
 
65
#include <netinet/in.h>
 
66
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
 
67
                                   getuid(), getgid(), setuid(),
 
68
                                   setgid() */
 
69
#include <arpa/inet.h>          /* inet_pton(), htons */
 
70
#include <iso646.h>             /* not, and */
 
71
#include <argp.h>               /* struct argp_option, error_t, struct
 
72
                                   argp_state, struct argp,
 
73
                                   argp_parse(), ARGP_KEY_ARG,
 
74
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
46
75
 
 
76
/* Avahi */
 
77
/* All Avahi types, constants and functions
 
78
 Avahi*, avahi_*,
 
79
 AVAHI_* */
47
80
#include <avahi-core/core.h>
48
81
#include <avahi-core/lookup.h>
49
82
#include <avahi-core/log.h>
51
84
#include <avahi-common/malloc.h>
52
85
#include <avahi-common/error.h>
53
86
 
54
 
/* Mandos client part */
55
 
#include <sys/types.h>          /* socket(), inet_pton() */
56
 
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
57
 
                                   struct in6_addr, inet_pton() */
58
 
#include <gnutls/gnutls.h>      /* All GnuTLS stuff */
59
 
#include <gnutls/openpgp.h>     /* GnuTLS with openpgp stuff */
 
87
/* GnuTLS */
 
88
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
 
89
                                   functions:
 
90
                                   gnutls_*
 
91
                                   init_gnutls_session(),
 
92
                                   GNUTLS_* */
 
93
#include <gnutls/openpgp.h>     /* gnutls_certificate_set_openpgp_key_file(),
 
94
                                   GNUTLS_OPENPGP_FMT_BASE64 */
60
95
 
61
 
#include <unistd.h>             /* close() */
62
 
#include <netinet/in.h>
63
 
#include <stdbool.h>            /* true */
64
 
#include <string.h>             /* memset */
65
 
#include <arpa/inet.h>          /* inet_pton() */
66
 
#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
96
/* GPGME */
72
 
#include <errno.h>              /* perror() */
73
 
#include <gpgme.h>
 
97
#include <gpgme.h>              /* All GPGME types, constants and
 
98
                                   functions:
 
99
                                   gpgme_*
 
100
                                   GPGME_PROTOCOL_OpenPGP,
 
101
                                   GPG_ERR_NO_* */
74
102
 
75
103
#define BUFFER_SIZE 256
76
104
 
 
105
#define PATHDIR "/conf/conf.d/mandos"
 
106
#define SECKEY "seckey.txt"
 
107
#define PUBKEY "pubkey.txt"
 
108
 
77
109
bool debug = false;
78
 
static const char *keydir = "/conf/conf.d/mandos";
79
110
static const char mandos_protocol_version[] = "1";
80
 
const char *argp_program_version = "mandosclient 0.9";
 
111
const char *argp_program_version = "mandos-client " VERSION;
81
112
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
82
113
 
83
114
/* Used for passing in values through the Avahi callback functions */
88
119
  unsigned int dh_bits;
89
120
  gnutls_dh_params_t dh_params;
90
121
  const char *priority;
 
122
  gpgme_ctx_t ctx;
91
123
} mandos_context;
92
124
 
93
125
/*
108
140
}
109
141
 
110
142
/* 
111
 
 * Decrypt OpenPGP data using keyrings in HOMEDIR.
112
 
 * Returns -1 on error
 
143
 * Initialize GPGME.
113
144
 */
114
 
static ssize_t pgp_packet_decrypt (const char *cryptotext,
115
 
                                   size_t crypto_size,
116
 
                                   char **plaintext,
117
 
                                   const char *homedir){
118
 
  gpgme_data_t dh_crypto, dh_plain;
119
 
  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;
120
148
  gpgme_error_t rc;
121
 
  ssize_t ret;
122
 
  size_t plaintext_capacity = 0;
123
 
  ssize_t plaintext_length = 0;
124
149
  gpgme_engine_info_t engine_info;
125
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
  
126
187
  if (debug){
127
 
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
 
188
    fprintf(stderr, "Initialize gpgme\n");
128
189
  }
129
190
  
130
191
  /* Init GPGME */
133
194
  if (rc != GPG_ERR_NO_ERROR){
134
195
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
135
196
            gpgme_strsource(rc), gpgme_strerror(rc));
136
 
    return -1;
 
197
    return false;
137
198
  }
138
199
  
139
 
  /* Set GPGME home directory for the OpenPGP engine only */
 
200
    /* Set GPGME home directory for the OpenPGP engine only */
140
201
  rc = gpgme_get_engine_info (&engine_info);
141
202
  if (rc != GPG_ERR_NO_ERROR){
142
203
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
143
204
            gpgme_strsource(rc), gpgme_strerror(rc));
144
 
    return -1;
 
205
    return false;
145
206
  }
146
207
  while(engine_info != NULL){
147
208
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
148
209
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
149
 
                            engine_info->file_name, homedir);
 
210
                            engine_info->file_name, tempdir);
150
211
      break;
151
212
    }
152
213
    engine_info = engine_info->next;
153
214
  }
154
215
  if(engine_info == NULL){
155
 
    fprintf(stderr, "Could not set GPGME home dir to %s\n", homedir);
156
 
    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");
157
251
  }
158
252
  
159
253
  /* Create new GPGME data buffer from memory cryptotext */
174
268
    return -1;
175
269
  }
176
270
  
177
 
  /* Create new GPGME "context" */
178
 
  rc = gpgme_new(&ctx);
179
 
  if (rc != GPG_ERR_NO_ERROR){
180
 
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
181
 
            gpgme_strsource(rc), gpgme_strerror(rc));
182
 
    plaintext_length = -1;
183
 
    goto decrypt_end;
184
 
  }
185
 
  
186
271
  /* Decrypt data from the cryptotext data buffer to the plaintext
187
272
     data buffer */
188
 
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
 
273
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
189
274
  if (rc != GPG_ERR_NO_ERROR){
190
275
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
191
276
            gpgme_strsource(rc), gpgme_strerror(rc));
192
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
    }
193
306
    goto decrypt_end;
194
307
  }
195
308
  
197
310
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
198
311
  }
199
312
  
200
 
  if (debug){
201
 
    gpgme_decrypt_result_t result;
202
 
    result = gpgme_op_decrypt_result(ctx);
203
 
    if (result == NULL){
204
 
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
205
 
    } else {
206
 
      fprintf(stderr, "Unsupported algorithm: %s\n",
207
 
              result->unsupported_algorithm);
208
 
      fprintf(stderr, "Wrong key usage: %d\n",
209
 
              result->wrong_key_usage);
210
 
      if(result->file_name != NULL){
211
 
        fprintf(stderr, "File name: %s\n", result->file_name);
212
 
      }
213
 
      gpgme_recipient_t recipient;
214
 
      recipient = result->recipients;
215
 
      if(recipient){
216
 
        while(recipient != NULL){
217
 
          fprintf(stderr, "Public key algorithm: %s\n",
218
 
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
219
 
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
220
 
          fprintf(stderr, "Secret key available: %s\n",
221
 
                  recipient->status == GPG_ERR_NO_SECKEY
222
 
                  ? "No" : "Yes");
223
 
          recipient = recipient->next;
224
 
        }
225
 
      }
226
 
    }
227
 
  }
228
 
  
229
313
  /* Seek back to the beginning of the GPGME plaintext data buffer */
230
314
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
231
 
    perror("pgpme_data_seek");
 
315
    perror("gpgme_data_seek");
232
316
    plaintext_length = -1;
233
317
    goto decrypt_end;
234
318
  }
258
342
    }
259
343
    plaintext_length += ret;
260
344
  }
261
 
 
 
345
  
262
346
  if(debug){
263
347
    fprintf(stderr, "Decrypted password is: ");
264
348
    for(ssize_t i = 0; i < plaintext_length; i++){
278
362
}
279
363
 
280
364
static const char * safer_gnutls_strerror (int value) {
281
 
  const char *ret = gnutls_strerror (value);
 
365
  const char *ret = gnutls_strerror (value); /* Spurious warning */
282
366
  if (ret == NULL)
283
367
    ret = "(unknown)";
284
368
  return ret;
291
375
}
292
376
 
293
377
static int init_gnutls_global(mandos_context *mc,
294
 
                              const char *pubkeyfile,
295
 
                              const char *seckeyfile){
 
378
                              const char *pubkeyfilename,
 
379
                              const char *seckeyfilename){
296
380
  int ret;
297
381
  
298
382
  if(debug){
299
383
    fprintf(stderr, "Initializing GnuTLS\n");
300
384
  }
301
 
 
302
 
  if ((ret = gnutls_global_init ())
303
 
      != GNUTLS_E_SUCCESS) {
 
385
  
 
386
  ret = gnutls_global_init();
 
387
  if (ret != GNUTLS_E_SUCCESS) {
304
388
    fprintf (stderr, "GnuTLS global_init: %s\n",
305
389
             safer_gnutls_strerror(ret));
306
390
    return -1;
315
399
  }
316
400
  
317
401
  /* OpenPGP credentials */
318
 
  if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
319
 
      != GNUTLS_E_SUCCESS) {
320
 
    fprintf (stderr, "GnuTLS memory error: %s\n",
 
402
  gnutls_certificate_allocate_credentials(&mc->cred);
 
403
  if (ret != GNUTLS_E_SUCCESS){
 
404
    fprintf (stderr, "GnuTLS memory error: %s\n", /* Spurious
 
405
                                                     warning */
321
406
             safer_gnutls_strerror(ret));
 
407
    gnutls_global_deinit ();
322
408
    return -1;
323
409
  }
324
410
  
325
411
  if(debug){
326
 
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
327
 
            " and keyfile %s as GnuTLS credentials\n", pubkeyfile,
328
 
            seckeyfile);
 
412
    fprintf(stderr, "Attempting to use OpenPGP public key %s and"
 
413
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
 
414
            seckeyfilename);
329
415
  }
330
416
  
331
417
  ret = gnutls_certificate_set_openpgp_key_file
332
 
    (mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
 
418
    (mc->cred, pubkeyfilename, seckeyfilename,
 
419
     GNUTLS_OPENPGP_FMT_BASE64);
333
420
  if (ret != GNUTLS_E_SUCCESS) {
334
421
    fprintf(stderr,
335
422
            "Error[%d] while reading the OpenPGP key pair ('%s',"
336
 
            " '%s')\n", ret, pubkeyfile, seckeyfile);
337
 
    fprintf(stdout, "The GnuTLS error is: %s\n",
 
423
            " '%s')\n", ret, pubkeyfilename, seckeyfilename);
 
424
    fprintf(stderr, "The GnuTLS error is: %s\n",
338
425
            safer_gnutls_strerror(ret));
339
 
    return -1;
 
426
    goto globalfail;
340
427
  }
341
428
  
342
429
  /* GnuTLS server initialization */
344
431
  if (ret != GNUTLS_E_SUCCESS) {
345
432
    fprintf (stderr, "Error in GnuTLS DH parameter initialization:"
346
433
             " %s\n", safer_gnutls_strerror(ret));
347
 
    return -1;
 
434
    goto globalfail;
348
435
  }
349
436
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
350
437
  if (ret != GNUTLS_E_SUCCESS) {
351
438
    fprintf (stderr, "Error in GnuTLS prime generation: %s\n",
352
439
             safer_gnutls_strerror(ret));
353
 
    return -1;
 
440
    goto globalfail;
354
441
  }
355
442
  
356
443
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
357
 
 
 
444
  
358
445
  return 0;
 
446
  
 
447
 globalfail:
 
448
  
 
449
  gnutls_certificate_free_credentials(mc->cred);
 
450
  gnutls_global_deinit();
 
451
  gnutls_dh_params_deinit(mc->dh_params);
 
452
  return -1;
359
453
}
360
454
 
361
455
static int init_gnutls_session(mandos_context *mc,
375
469
      fprintf(stderr, "Syntax error at: %s\n", err);
376
470
      fprintf(stderr, "GnuTLS error: %s\n",
377
471
              safer_gnutls_strerror(ret));
 
472
      gnutls_deinit (*session);
378
473
      return -1;
379
474
    }
380
475
  }
384
479
  if (ret != GNUTLS_E_SUCCESS) {
385
480
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
386
481
            safer_gnutls_strerror(ret));
 
482
    gnutls_deinit (*session);
387
483
    return -1;
388
484
  }
389
485
  
422
518
  }
423
519
  
424
520
  if(debug){
425
 
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
426
 
            ip, port);
 
521
    fprintf(stderr, "Setting up a tcp connection to %s, port %" PRIu16
 
522
            "\n", ip, port);
427
523
  }
428
524
  
429
525
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
431
527
    perror("socket");
432
528
    return -1;
433
529
  }
434
 
 
 
530
  
435
531
  if(debug){
436
532
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
437
533
      perror("if_indextoname");
440
536
    fprintf(stderr, "Binding to interface %s\n", interface);
441
537
  }
442
538
  
443
 
  memset(&to,0,sizeof(to));     /* Spurious warning */
 
539
  memset(&to, 0, sizeof(to));
444
540
  to.in6.sin6_family = AF_INET6;
445
541
  /* It would be nice to have a way to detect if we were passed an
446
542
     IPv4 address here.   Now we assume an IPv6 address. */
453
549
    fprintf(stderr, "Bad address: %s\n", ip);
454
550
    return -1;
455
551
  }
456
 
  to.in6.sin6_port = htons(port);       /* Spurious warning */
 
552
  to.in6.sin6_port = htons(port); /* Spurious warning */
457
553
  
458
554
  to.in6.sin6_scope_id = (uint32_t)if_index;
459
555
  
460
556
  if(debug){
461
 
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
 
557
    fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
 
558
            port);
462
559
    char addrstr[INET6_ADDRSTRLEN] = "";
463
560
    if(inet_ntop(to.in6.sin6_family, &(to.in6.sin6_addr), addrstr,
464
561
                 sizeof(addrstr)) == NULL){
475
572
    perror("connect");
476
573
    return -1;
477
574
  }
478
 
 
 
575
  
479
576
  const char *out = mandos_protocol_version;
480
577
  written = 0;
481
578
  while (true){
499
596
      }
500
597
    }
501
598
  }
502
 
 
 
599
  
503
600
  if(debug){
504
601
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
505
602
  }
506
603
  
507
604
  gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
508
605
  
509
 
  ret = gnutls_handshake (session);
 
606
  do{
 
607
    ret = gnutls_handshake (session);
 
608
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
510
609
  
511
610
  if (ret != GNUTLS_E_SUCCESS){
512
611
    if(debug){
523
622
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
524
623
            ip);
525
624
  }
526
 
 
 
625
  
527
626
  while(true){
528
627
    buffer_capacity = adjustbuffer(&buffer, buffer_length,
529
628
                                   buffer_capacity);
544
643
      case GNUTLS_E_AGAIN:
545
644
        break;
546
645
      case GNUTLS_E_REHANDSHAKE:
547
 
        ret = gnutls_handshake (session);
 
646
        do{
 
647
          ret = gnutls_handshake (session);
 
648
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
548
649
        if (ret < 0){
549
650
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
550
651
          gnutls_perror (ret);
571
672
  gnutls_bye (session, GNUTLS_SHUT_RDWR);
572
673
  
573
674
  if (buffer_length > 0){
574
 
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
 
675
    decrypted_buffer_size = pgp_packet_decrypt(mc, buffer,
575
676
                                               buffer_length,
576
 
                                               &decrypted_buffer,
577
 
                                               keydir);
 
677
                                               &decrypted_buffer);
578
678
    if (decrypted_buffer_size >= 0){
579
679
      written = 0;
580
680
      while(written < (size_t) decrypted_buffer_size){
595
695
    } else {
596
696
      retval = -1;
597
697
    }
 
698
  } else {
 
699
    retval = -1;
598
700
  }
599
701
  
600
702
  /* Shutdown procedure */
601
703
  
602
704
 mandos_end:
603
705
  free(buffer);
604
 
  close(tcp_sd);
 
706
  ret = TEMP_FAILURE_RETRY(close(tcp_sd));
 
707
  if(ret == -1){
 
708
    perror("close");
 
709
  }
605
710
  gnutls_deinit (session);
606
 
  gnutls_certificate_free_credentials (mc->cred);
607
 
  gnutls_global_deinit ();
608
711
  return retval;
609
712
}
610
713
 
623
726
                             flags,
624
727
                             void* userdata) {
625
728
  mandos_context *mc = userdata;
626
 
  assert(r);                    /* Spurious warning */
 
729
  assert(r);
627
730
  
628
731
  /* Called whenever a service has been resolved successfully or
629
732
     timed out */
641
744
      char ip[AVAHI_ADDRESS_STR_MAX];
642
745
      avahi_address_snprint(ip, sizeof(ip), address);
643
746
      if(debug){
644
 
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %d) on"
645
 
                " 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);
646
750
      }
647
751
      int ret = start_mandos_communication(ip, port, interface, mc);
648
752
      if (ret == 0){
649
 
        exit(EXIT_SUCCESS);
 
753
        avahi_simple_poll_quit(mc->simple_poll);
650
754
      }
651
755
    }
652
756
  }
664
768
                             flags,
665
769
                             void* userdata) {
666
770
  mandos_context *mc = userdata;
667
 
  assert(b);                    /* Spurious warning */
 
771
  assert(b);
668
772
  
669
773
  /* Called whenever a new services becomes available on the LAN or
670
774
     is removed from the LAN */
704
808
  }
705
809
}
706
810
 
707
 
/* Combines file name and path and returns the malloced new
708
 
   string. some sane checks could/should be added */
709
 
static const char *combinepath(const char *first, const char *second){
710
 
  size_t f_len = strlen(first);
711
 
  size_t s_len = strlen(second);
712
 
  char *tmp = malloc(f_len + s_len + 2);
713
 
  if (tmp == NULL){
714
 
    return NULL;
715
 
  }
716
 
  if(f_len > 0){
717
 
    memcpy(tmp, first, f_len);  /* Spurious warning */
718
 
  }
719
 
  tmp[f_len] = '/';
720
 
  if(s_len > 0){
721
 
    memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
722
 
  }
723
 
  tmp[f_len + 1 + s_len] = '\0';
724
 
  return tmp;
725
 
}
726
 
 
727
 
 
728
811
int main(int argc, char *argv[]){
729
812
    AvahiSServiceBrowser *sb = NULL;
730
813
    int error;
736
819
    uid_t uid;
737
820
    gid_t gid;
738
821
    char *connect_to = NULL;
 
822
    char tempdir[] = "/tmp/mandosXXXXXX";
739
823
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
740
 
    const char *pubkeyfile = "pubkey.txt";
741
 
    const char *seckeyfile = "seckey.txt";
 
824
    const char *seckey = PATHDIR "/" SECKEY;
 
825
    const char *pubkey = PATHDIR "/" PUBKEY;
 
826
    
742
827
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
743
 
                          .dh_bits = 1024, .priority = "SECURE256"};
 
828
                          .dh_bits = 1024, .priority = "SECURE256"
 
829
                          ":!CTYPE-X.509:+CTYPE-OPENPGP" };
 
830
    bool gnutls_initalized = false;
 
831
    bool gpgme_initalized = false;
744
832
    
745
833
    {
746
834
      struct argp_option options[] = {
747
835
        { .name = "debug", .key = 128,
748
836
          .doc = "Debug mode", .group = 3 },
749
837
        { .name = "connect", .key = 'c',
750
 
          .arg = "IP",
751
 
          .doc = "Connect directly to a sepcified mandos server",
 
838
          .arg = "ADDRESS:PORT",
 
839
          .doc = "Connect directly to a specific Mandos server",
752
840
          .group = 1 },
753
841
        { .name = "interface", .key = 'i',
754
 
          .arg = "INTERFACE",
755
 
          .doc = "Interface that Avahi will conntect through",
756
 
          .group = 1 },
757
 
        { .name = "keydir", .key = 'd',
758
 
          .arg = "KEYDIR",
759
 
          .doc = "Directory where the openpgp keyring is",
 
842
          .arg = "NAME",
 
843
          .doc = "Interface that will be used to search for Mandos"
 
844
          " servers",
760
845
          .group = 1 },
761
846
        { .name = "seckey", .key = 's',
762
 
          .arg = "SECKEY",
763
 
          .doc = "Secret openpgp key for gnutls authentication",
 
847
          .arg = "FILE",
 
848
          .doc = "OpenPGP secret key file base name",
764
849
          .group = 1 },
765
850
        { .name = "pubkey", .key = 'p',
766
 
          .arg = "PUBKEY",
767
 
          .doc = "Public openpgp key for gnutls authentication",
 
851
          .arg = "FILE",
 
852
          .doc = "OpenPGP public key file base name",
768
853
          .group = 2 },
769
854
        { .name = "dh-bits", .key = 129,
770
855
          .arg = "BITS",
771
 
          .doc = "dh-bits to use in gnutls communication",
 
856
          .doc = "Bit length of the prime number used in the"
 
857
          " Diffie-Hellman key exchange",
772
858
          .group = 2 },
773
859
        { .name = "priority", .key = 130,
774
 
          .arg = "PRIORITY",
775
 
          .doc = "GNUTLS priority", .group = 1 },
 
860
          .arg = "STRING",
 
861
          .doc = "GnuTLS priority string for the TLS handshake",
 
862
          .group = 1 },
776
863
        { .name = NULL }
777
864
      };
778
 
 
779
865
      
780
866
      error_t parse_opt (int key, char *arg,
781
867
                         struct argp_state *state) {
782
868
        /* Get the INPUT argument from `argp_parse', which we know is
783
869
           a pointer to our plugin list pointer. */
784
870
        switch (key) {
785
 
        case 128:
 
871
        case 128:               /* --debug */
786
872
          debug = true;
787
873
          break;
788
 
        case 'c':
 
874
        case 'c':               /* --connect */
789
875
          connect_to = arg;
790
876
          break;
791
 
        case 'i':
 
877
        case 'i':               /* --interface */
792
878
          interface = arg;
793
879
          break;
794
 
        case 'd':
795
 
          keydir = arg;
796
 
          break;
797
 
        case 's':
798
 
          seckeyfile = arg;
799
 
          break;
800
 
        case 'p':
801
 
          pubkeyfile = arg;
802
 
          break;
803
 
        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 */
804
887
          errno = 0;
805
888
          mc.dh_bits = (unsigned int) strtol(arg, NULL, 10);
806
889
          if (errno){
808
891
            exit(EXIT_FAILURE);
809
892
          }
810
893
          break;
811
 
        case 130:
 
894
        case 130:               /* --priority */
812
895
          mc.priority = arg;
813
896
          break;
814
897
        case ARGP_KEY_ARG:
815
898
          argp_usage (state);
 
899
        case ARGP_KEY_END:
816
900
          break;
817
 
          case ARGP_KEY_END:
818
 
            break;
819
901
        default:
820
902
          return ARGP_ERR_UNKNOWN;
821
903
        }
822
904
        return 0;
823
905
      }
824
 
 
 
906
      
825
907
      struct argp argp = { .options = options, .parser = parse_opt,
826
908
                           .args_doc = "",
827
909
                           .doc = "Mandos client -- Get and decrypt"
828
 
                           " passwords from mandos server" };
829
 
      argp_parse (&argp, argc, argv, 0, 0, NULL);
830
 
    }
831
 
      
832
 
    pubkeyfile = combinepath(keydir, pubkeyfile);
833
 
    if (pubkeyfile == NULL){
834
 
      perror("combinepath");
835
 
      exitcode = EXIT_FAILURE;
836
 
      goto end;
837
 
    }
838
 
    
839
 
    seckeyfile = combinepath(keydir, seckeyfile);
840
 
    if (seckeyfile == NULL){
841
 
      perror("combinepath");
842
 
      goto end;
843
 
    }
844
 
 
845
 
    ret = init_gnutls_global(&mc, pubkeyfile, seckeyfile);
846
 
    if (ret == -1){
847
 
      fprintf(stderr, "init_gnutls_global\n");
848
 
      goto end;
849
 
    }
850
 
 
 
910
                           " passwords from a Mandos server" };
 
911
      ret = argp_parse (&argp, argc, argv, 0, 0, NULL);
 
912
      if (ret == ARGP_ERR_UNKNOWN){
 
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
    
851
949
    uid = getuid();
852
950
    gid = getgid();
853
 
 
 
951
    
854
952
    ret = setuid(uid);
855
953
    if (ret == -1){
856
954
      perror("setuid");
861
959
      perror("setgid");
862
960
    }
863
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
    
864
985
    if_index = (AvahiIfIndex) if_nametoindex(interface);
865
986
    if(if_index == 0){
866
987
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
894
1015
      goto end;
895
1016
    }
896
1017
    
897
 
    /* If the interface is down, bring it up */
898
 
    {
899
 
      sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
900
 
      if(sd < 0) {
901
 
        perror("socket");
902
 
        exitcode = EXIT_FAILURE;
903
 
        goto end;
904
 
      }
905
 
      strcpy(network.ifr_name, interface); /* Spurious warning */
906
 
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
907
 
      if(ret == -1){
908
 
        perror("ioctl SIOCGIFFLAGS");
909
 
        exitcode = EXIT_FAILURE;
910
 
        goto end;
911
 
      }
912
 
      if((network.ifr_flags & IFF_UP) == 0){
913
 
        network.ifr_flags |= IFF_UP;
914
 
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
915
 
        if(ret == -1){
916
 
          perror("ioctl SIOCSIFFLAGS");
917
 
          exitcode = EXIT_FAILURE;
918
 
          goto end;
919
 
        }
920
 
      }
921
 
      close(sd);
922
 
    }
923
 
    
924
1018
    if (not debug){
925
1019
      avahi_set_log_function(empty_log);
926
1020
    }
936
1030
        exitcode = EXIT_FAILURE;
937
1031
        goto end;
938
1032
    }
939
 
 
 
1033
    
940
1034
    {
941
1035
      AvahiServerConfig config;
942
1036
      /* Do not publish any local Zeroconf records */
945
1039
      config.publish_addresses = 0;
946
1040
      config.publish_workstation = 0;
947
1041
      config.publish_domain = 0;
948
 
 
 
1042
      
949
1043
      /* Allocate a new server */
950
1044
      mc.server = avahi_server_new(avahi_simple_poll_get
951
1045
                                   (mc.simple_poll), &config, NULL,
952
1046
                                   NULL, &error);
953
 
    
 
1047
      
954
1048
      /* Free the Avahi configuration data */
955
1049
      avahi_server_config_free(&config);
956
1050
    }
976
1070
    }
977
1071
    
978
1072
    /* Run the main loop */
979
 
 
 
1073
    
980
1074
    if (debug){
981
1075
      fprintf(stderr, "Starting Avahi loop search\n");
982
1076
    }
984
1078
    avahi_simple_poll_loop(mc.simple_poll);
985
1079
    
986
1080
 end:
987
 
 
 
1081
    
988
1082
    if (debug){
989
1083
      fprintf(stderr, "%s exiting\n", argv[0]);
990
1084
    }
995
1089
    
996
1090
    if (mc.server != NULL)
997
1091
        avahi_server_free(mc.server);
998
 
 
 
1092
    
999
1093
    if (mc.simple_poll != NULL)
1000
1094
        avahi_simple_poll_free(mc.simple_poll);
1001
 
    free(pubkeyfile);
1002
 
    free(seckeyfile);
1003
 
    
 
1095
    
 
1096
    if (gnutls_initalized){
 
1097
      gnutls_certificate_free_credentials(mc.cred);
 
1098
      gnutls_global_deinit ();
 
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
          
1004
1143
    return exitcode;
1005
1144
}