/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/password-request.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-29 05:53:59 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080829055359-wkdasnyxtylmnxus
* mandos.xml (EXAMPLE): Replaced all occurences of command name with
                        "&COMMANDNAME;".

* plugins.d/password-prompt.c (main): Improved some documentation
                                      strings.  Do perror() of
                                      tcgetattr() fails.  Add debug
                                      output if interrupted by signal.
                                      Loop over write() instead of
                                      using fwrite() when outputting
                                      password.  Add debug output if
                                      getline() returns 0, unless it
                                      was caused by a signal.  Add
                                      exit status code to debug
                                      output.

* plugins.d/password-prompt.xml: Changed all single quotes to double
                                 quotes for consistency.  Removed
                                 <?xml-stylesheet>.
  (ENTITY TIMESTAMP): New.  Automatically updated by Emacs time-stamp
                      by using Emacs local variables.
  (/refentry/refentryinfo/title): Changed to "Mandos Manual".
  (/refentry/refentryinfo/productname): Changed to "Mandos".
  (/refentry/refentryinfo/date): New; set to "&TIMESTAMP;".
  (/refentry/refentryinfo/copyright): Split copyright holders.
  (/refentry/refnamediv/refpurpose): Improved wording.
  (SYNOPSIS): Fix to use correct markup.  Add short options.
  (DESCRIPTION, OPTIONS): Improved wording.
  (OPTIONS): Improved wording.  Use more correct markup.  Document
             short options.
  (EXIT STATUS): Add text.
  (ENVIRONMENT): Document use of "cryptsource" and "crypttarget".
  (FILES): REMOVED.
  (BUGS): Add text.
  (EXAMPLE): Added some examples.
  (SECURITY): Added text.
  (SEE ALSO): Remove reference to mandos(8).  Add reference to
              crypttab(5).

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 © 2007-2008 Teddy Hogeborn & Björn Påhlsson
14
13
 * 
15
14
 * This program is free software: you can redistribute it and/or
16
15
 * modify it under the terms of the GNU General Public License as
36
35
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
37
36
 
38
37
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
39
 
                                   stdout, ferror(), sscanf */
 
38
                                   stdout, ferror() */
40
39
#include <stdint.h>             /* uint16_t, uint32_t */
41
40
#include <stddef.h>             /* NULL, size_t, ssize_t */
42
41
#include <stdlib.h>             /* free(), EXIT_SUCCESS, EXIT_FAILURE,
48
47
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
49
48
                                   sockaddr_in6, PF_INET6,
50
49
                                   SOCK_STREAM, INET6_ADDRSTRLEN,
51
 
                                   uid_t, gid_t, open(), opendir(),
52
 
                                   DIR */
53
 
#include <sys/stat.h>           /* open() */
 
50
                                   uid_t, gid_t */
 
51
#include <inttypes.h>           /* PRIu16 */
54
52
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
55
53
                                   struct in6_addr, inet_pton(),
56
54
                                   connect() */
57
 
#include <fcntl.h>              /* open() */
58
 
#include <dirent.h>             /* opendir(), struct dirent, readdir()
59
 
                                 */
60
 
#include <inttypes.h>           /* PRIu16, SCNu16 */
61
55
#include <assert.h>             /* assert() */
62
56
#include <errno.h>              /* perror(), errno */
63
57
#include <time.h>               /* time() */
64
58
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
65
59
                                   SIOCSIFFLAGS, if_indextoname(),
66
60
                                   if_nametoindex(), IF_NAMESIZE */
67
 
#include <netinet/in.h>
68
61
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
69
62
                                   getuid(), getgid(), setuid(),
70
63
                                   setgid() */
 
64
#include <netinet/in.h>
71
65
#include <arpa/inet.h>          /* inet_pton(), htons */
72
 
#include <iso646.h>             /* not, and, or */
 
66
#include <iso646.h>             /* not, and */
73
67
#include <argp.h>               /* struct argp_option, error_t, struct
74
68
                                   argp_state, struct argp,
75
69
                                   argp_parse(), ARGP_KEY_ARG,
92
86
                                   gnutls_*
93
87
                                   init_gnutls_session(),
94
88
                                   GNUTLS_* */
95
 
#include <gnutls/openpgp.h>
96
 
                          /* gnutls_certificate_set_openpgp_key_file(),
 
89
#include <gnutls/openpgp.h>     /* gnutls_certificate_set_openpgp_key_file(),
97
90
                                   GNUTLS_OPENPGP_FMT_BASE64 */
98
91
 
99
92
/* GPGME */
105
98
 
106
99
#define BUFFER_SIZE 256
107
100
 
108
 
#define PATHDIR "/conf/conf.d/mandos"
109
 
#define SECKEY "seckey.txt"
110
 
#define PUBKEY "pubkey.txt"
111
 
 
112
101
bool debug = false;
 
102
static const char *keydir = "/conf/conf.d/mandos";
113
103
static const char mandos_protocol_version[] = "1";
114
 
const char *argp_program_version = "mandos-client " VERSION;
 
104
const char *argp_program_version = "password-request 1.0";
115
105
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
116
106
 
117
107
/* Used for passing in values through the Avahi callback functions */
122
112
  unsigned int dh_bits;
123
113
  gnutls_dh_params_t dh_params;
124
114
  const char *priority;
125
 
  gpgme_ctx_t ctx;
126
115
} mandos_context;
127
116
 
128
117
/*
132
121
 */
133
122
size_t adjustbuffer(char **buffer, size_t buffer_length,
134
123
                  size_t buffer_capacity){
135
 
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
 
124
  if (buffer_length + BUFFER_SIZE > buffer_capacity){
136
125
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
137
 
    if(buffer == NULL){
 
126
    if (buffer == NULL){
138
127
      return 0;
139
128
    }
140
129
    buffer_capacity += BUFFER_SIZE;
143
132
}
144
133
 
145
134
/* 
146
 
 * Initialize GPGME.
 
135
 * Decrypt OpenPGP data using keyrings in HOMEDIR.
 
136
 * Returns -1 on error
147
137
 */
148
 
static bool init_gpgme(mandos_context *mc, const char *seckey,
149
 
                       const char *pubkey, const char *tempdir){
150
 
  int ret;
 
138
static ssize_t pgp_packet_decrypt (const char *cryptotext,
 
139
                                   size_t crypto_size,
 
140
                                   char **plaintext,
 
141
                                   const char *homedir){
 
142
  gpgme_data_t dh_crypto, dh_plain;
 
143
  gpgme_ctx_t ctx;
151
144
  gpgme_error_t rc;
 
145
  ssize_t ret;
 
146
  size_t plaintext_capacity = 0;
 
147
  ssize_t plaintext_length = 0;
152
148
  gpgme_engine_info_t engine_info;
153
149
  
154
 
  
155
 
  /*
156
 
   * Helper function to insert pub and seckey to the enigne keyring.
157
 
   */
158
 
  bool import_key(const char *filename){
159
 
    int fd;
160
 
    gpgme_data_t pgp_data;
161
 
    
162
 
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
163
 
    if(fd == -1){
164
 
      perror("open");
165
 
      return false;
166
 
    }
167
 
    
168
 
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
169
 
    if(rc != GPG_ERR_NO_ERROR){
170
 
      fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
171
 
              gpgme_strsource(rc), gpgme_strerror(rc));
172
 
      return false;
173
 
    }
174
 
    
175
 
    rc = gpgme_op_import(mc->ctx, pgp_data);
176
 
    if(rc != GPG_ERR_NO_ERROR){
177
 
      fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
178
 
              gpgme_strsource(rc), gpgme_strerror(rc));
179
 
      return false;
180
 
    }
181
 
    
182
 
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
183
 
    if(ret == -1){
184
 
      perror("close");
185
 
    }
186
 
    gpgme_data_release(pgp_data);
187
 
    return true;
188
 
  }
189
 
  
190
 
  if(debug){
191
 
    fprintf(stderr, "Initialize gpgme\n");
 
150
  if (debug){
 
151
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
192
152
  }
193
153
  
194
154
  /* Init GPGME */
195
155
  gpgme_check_version(NULL);
196
156
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
197
 
  if(rc != GPG_ERR_NO_ERROR){
 
157
  if (rc != GPG_ERR_NO_ERROR){
198
158
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
199
159
            gpgme_strsource(rc), gpgme_strerror(rc));
200
 
    return false;
 
160
    return -1;
201
161
  }
202
162
  
203
 
    /* Set GPGME home directory for the OpenPGP engine only */
204
 
  rc = gpgme_get_engine_info(&engine_info);
205
 
  if(rc != GPG_ERR_NO_ERROR){
 
163
  /* Set GPGME home directory for the OpenPGP engine only */
 
164
  rc = gpgme_get_engine_info (&engine_info);
 
165
  if (rc != GPG_ERR_NO_ERROR){
206
166
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
207
167
            gpgme_strsource(rc), gpgme_strerror(rc));
208
 
    return false;
 
168
    return -1;
209
169
  }
210
170
  while(engine_info != NULL){
211
171
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
212
172
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
213
 
                            engine_info->file_name, tempdir);
 
173
                            engine_info->file_name, homedir);
214
174
      break;
215
175
    }
216
176
    engine_info = engine_info->next;
217
177
  }
218
178
  if(engine_info == NULL){
219
 
    fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
220
 
    return false;
221
 
  }
222
 
  
223
 
  /* Create new GPGME "context" */
224
 
  rc = gpgme_new(&(mc->ctx));
225
 
  if(rc != GPG_ERR_NO_ERROR){
226
 
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
227
 
            gpgme_strsource(rc), gpgme_strerror(rc));
228
 
    return false;
229
 
  }
230
 
  
231
 
  if(not import_key(pubkey) or not import_key(seckey)){
232
 
    return false;
233
 
  }
234
 
  
235
 
  return true; 
236
 
}
237
 
 
238
 
/* 
239
 
 * Decrypt OpenPGP data.
240
 
 * Returns -1 on error
241
 
 */
242
 
static ssize_t pgp_packet_decrypt(const mandos_context *mc,
243
 
                                  const char *cryptotext,
244
 
                                  size_t crypto_size,
245
 
                                  char **plaintext){
246
 
  gpgme_data_t dh_crypto, dh_plain;
247
 
  gpgme_error_t rc;
248
 
  ssize_t ret;
249
 
  size_t plaintext_capacity = 0;
250
 
  ssize_t plaintext_length = 0;
251
 
  
252
 
  if(debug){
253
 
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
 
179
    fprintf(stderr, "Could not set GPGME home dir to %s\n", homedir);
 
180
    return -1;
254
181
  }
255
182
  
256
183
  /* Create new GPGME data buffer from memory cryptotext */
257
184
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
258
185
                               0);
259
 
  if(rc != GPG_ERR_NO_ERROR){
 
186
  if (rc != GPG_ERR_NO_ERROR){
260
187
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
261
188
            gpgme_strsource(rc), gpgme_strerror(rc));
262
189
    return -1;
264
191
  
265
192
  /* Create new empty GPGME data buffer for the plaintext */
266
193
  rc = gpgme_data_new(&dh_plain);
267
 
  if(rc != GPG_ERR_NO_ERROR){
 
194
  if (rc != GPG_ERR_NO_ERROR){
268
195
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
269
196
            gpgme_strsource(rc), gpgme_strerror(rc));
270
197
    gpgme_data_release(dh_crypto);
271
198
    return -1;
272
199
  }
273
200
  
 
201
  /* Create new GPGME "context" */
 
202
  rc = gpgme_new(&ctx);
 
203
  if (rc != GPG_ERR_NO_ERROR){
 
204
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
 
205
            gpgme_strsource(rc), gpgme_strerror(rc));
 
206
    plaintext_length = -1;
 
207
    goto decrypt_end;
 
208
  }
 
209
  
274
210
  /* Decrypt data from the cryptotext data buffer to the plaintext
275
211
     data buffer */
276
 
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
277
 
  if(rc != GPG_ERR_NO_ERROR){
 
212
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
 
213
  if (rc != GPG_ERR_NO_ERROR){
278
214
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
279
215
            gpgme_strsource(rc), gpgme_strerror(rc));
280
216
    plaintext_length = -1;
281
 
    if(debug){
 
217
    if (debug){
282
218
      gpgme_decrypt_result_t result;
283
 
      result = gpgme_op_decrypt_result(mc->ctx);
284
 
      if(result == NULL){
 
219
      result = gpgme_op_decrypt_result(ctx);
 
220
      if (result == NULL){
285
221
        fprintf(stderr, "gpgme_op_decrypt_result failed\n");
286
222
      } else {
287
223
        fprintf(stderr, "Unsupported algorithm: %s\n",
314
250
  }
315
251
  
316
252
  /* Seek back to the beginning of the GPGME plaintext data buffer */
317
 
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
318
 
    perror("gpgme_data_seek");
 
253
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
 
254
    perror("pgpme_data_seek");
319
255
    plaintext_length = -1;
320
256
    goto decrypt_end;
321
257
  }
325
261
    plaintext_capacity = adjustbuffer(plaintext,
326
262
                                      (size_t)plaintext_length,
327
263
                                      plaintext_capacity);
328
 
    if(plaintext_capacity == 0){
 
264
    if (plaintext_capacity == 0){
329
265
        perror("adjustbuffer");
330
266
        plaintext_length = -1;
331
267
        goto decrypt_end;
334
270
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
335
271
                          BUFFER_SIZE);
336
272
    /* Print the data, if any */
337
 
    if(ret == 0){
 
273
    if (ret == 0){
338
274
      /* EOF */
339
275
      break;
340
276
    }
345
281
    }
346
282
    plaintext_length += ret;
347
283
  }
348
 
  
 
284
 
349
285
  if(debug){
350
286
    fprintf(stderr, "Decrypted password is: ");
351
287
    for(ssize_t i = 0; i < plaintext_length; i++){
364
300
  return plaintext_length;
365
301
}
366
302
 
367
 
static const char * safer_gnutls_strerror(int value) {
368
 
  const char *ret = gnutls_strerror(value); /* Spurious warning */
369
 
  if(ret == NULL)
 
303
static const char * safer_gnutls_strerror (int value) {
 
304
  const char *ret = gnutls_strerror (value); /* Spurious warning */
 
305
  if (ret == NULL)
370
306
    ret = "(unknown)";
371
307
  return ret;
372
308
}
387
323
  }
388
324
  
389
325
  ret = gnutls_global_init();
390
 
  if(ret != GNUTLS_E_SUCCESS) {
391
 
    fprintf(stderr, "GnuTLS global_init: %s\n",
392
 
            safer_gnutls_strerror(ret));
 
326
  if (ret != GNUTLS_E_SUCCESS) {
 
327
    fprintf (stderr, "GnuTLS global_init: %s\n",
 
328
             safer_gnutls_strerror(ret));
393
329
    return -1;
394
330
  }
395
331
  
396
 
  if(debug){
 
332
  if (debug){
397
333
    /* "Use a log level over 10 to enable all debugging options."
398
334
     * - GnuTLS manual
399
335
     */
403
339
  
404
340
  /* OpenPGP credentials */
405
341
  gnutls_certificate_allocate_credentials(&mc->cred);
406
 
  if(ret != GNUTLS_E_SUCCESS){
407
 
    fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious
408
 
                                                    warning */
409
 
            safer_gnutls_strerror(ret));
410
 
    gnutls_global_deinit();
 
342
  if (ret != GNUTLS_E_SUCCESS){
 
343
    fprintf (stderr, "GnuTLS memory error: %s\n", /* Spurious
 
344
                                                     warning */
 
345
             safer_gnutls_strerror(ret));
 
346
    gnutls_global_deinit ();
411
347
    return -1;
412
348
  }
413
349
  
414
350
  if(debug){
415
 
    fprintf(stderr, "Attempting to use OpenPGP public key %s and"
416
 
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
 
351
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
 
352
            " and keyfile %s as GnuTLS credentials\n", pubkeyfilename,
417
353
            seckeyfilename);
418
354
  }
419
355
  
420
356
  ret = gnutls_certificate_set_openpgp_key_file
421
357
    (mc->cred, pubkeyfilename, seckeyfilename,
422
358
     GNUTLS_OPENPGP_FMT_BASE64);
423
 
  if(ret != GNUTLS_E_SUCCESS) {
 
359
  if (ret != GNUTLS_E_SUCCESS) {
424
360
    fprintf(stderr,
425
361
            "Error[%d] while reading the OpenPGP key pair ('%s',"
426
362
            " '%s')\n", ret, pubkeyfilename, seckeyfilename);
427
 
    fprintf(stderr, "The GnuTLS error is: %s\n",
 
363
    fprintf(stdout, "The GnuTLS error is: %s\n",
428
364
            safer_gnutls_strerror(ret));
429
365
    goto globalfail;
430
366
  }
431
367
  
432
368
  /* GnuTLS server initialization */
433
369
  ret = gnutls_dh_params_init(&mc->dh_params);
434
 
  if(ret != GNUTLS_E_SUCCESS) {
435
 
    fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
436
 
            " %s\n", safer_gnutls_strerror(ret));
 
370
  if (ret != GNUTLS_E_SUCCESS) {
 
371
    fprintf (stderr, "Error in GnuTLS DH parameter initialization:"
 
372
             " %s\n", safer_gnutls_strerror(ret));
437
373
    goto globalfail;
438
374
  }
439
375
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
440
 
  if(ret != GNUTLS_E_SUCCESS) {
441
 
    fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
442
 
            safer_gnutls_strerror(ret));
 
376
  if (ret != GNUTLS_E_SUCCESS) {
 
377
    fprintf (stderr, "Error in GnuTLS prime generation: %s\n",
 
378
             safer_gnutls_strerror(ret));
443
379
    goto globalfail;
444
380
  }
445
381
  
446
382
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
447
 
  
 
383
 
448
384
  return 0;
449
 
  
 
385
 
450
386
 globalfail:
451
 
  
 
387
 
452
388
  gnutls_certificate_free_credentials(mc->cred);
453
389
  gnutls_global_deinit();
454
 
  gnutls_dh_params_deinit(mc->dh_params);
455
390
  return -1;
 
391
 
456
392
}
457
393
 
458
394
static int init_gnutls_session(mandos_context *mc,
460
396
  int ret;
461
397
  /* GnuTLS session creation */
462
398
  ret = gnutls_init(session, GNUTLS_SERVER);
463
 
  if(ret != GNUTLS_E_SUCCESS){
 
399
  if (ret != GNUTLS_E_SUCCESS){
464
400
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
465
401
            safer_gnutls_strerror(ret));
466
402
  }
468
404
  {
469
405
    const char *err;
470
406
    ret = gnutls_priority_set_direct(*session, mc->priority, &err);
471
 
    if(ret != GNUTLS_E_SUCCESS) {
 
407
    if (ret != GNUTLS_E_SUCCESS) {
472
408
      fprintf(stderr, "Syntax error at: %s\n", err);
473
409
      fprintf(stderr, "GnuTLS error: %s\n",
474
410
              safer_gnutls_strerror(ret));
475
 
      gnutls_deinit(*session);
 
411
      gnutls_deinit (*session);
476
412
      return -1;
477
413
    }
478
414
  }
479
415
  
480
416
  ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
481
417
                               mc->cred);
482
 
  if(ret != GNUTLS_E_SUCCESS) {
 
418
  if (ret != GNUTLS_E_SUCCESS) {
483
419
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
484
420
            safer_gnutls_strerror(ret));
485
 
    gnutls_deinit(*session);
 
421
    gnutls_deinit (*session);
486
422
    return -1;
487
423
  }
488
424
  
489
425
  /* ignore client certificate if any. */
490
 
  gnutls_certificate_server_set_request(*session,
491
 
                                        GNUTLS_CERT_IGNORE);
 
426
  gnutls_certificate_server_set_request (*session,
 
427
                                         GNUTLS_CERT_IGNORE);
492
428
  
493
 
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
 
429
  gnutls_dh_set_prime_bits (*session, mc->dh_bits);
494
430
  
495
431
  return 0;
496
432
}
504
440
                                      AvahiIfIndex if_index,
505
441
                                      mandos_context *mc){
506
442
  int ret, tcp_sd;
507
 
  ssize_t sret;
508
443
  union { struct sockaddr in; struct sockaddr_in6 in6; } to;
509
444
  char *buffer = NULL;
510
445
  char *decrypted_buffer;
516
451
  char interface[IF_NAMESIZE];
517
452
  gnutls_session_t session;
518
453
  
519
 
  ret = init_gnutls_session(mc, &session);
520
 
  if(ret != 0){
 
454
  ret = init_gnutls_session (mc, &session);
 
455
  if (ret != 0){
521
456
    return -1;
522
457
  }
523
458
  
531
466
    perror("socket");
532
467
    return -1;
533
468
  }
534
 
  
 
469
 
535
470
  if(debug){
536
471
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
537
472
      perror("if_indextoname");
545
480
  /* It would be nice to have a way to detect if we were passed an
546
481
     IPv4 address here.   Now we assume an IPv6 address. */
547
482
  ret = inet_pton(AF_INET6, ip, &to.in6.sin6_addr);
548
 
  if(ret < 0 ){
 
483
  if (ret < 0 ){
549
484
    perror("inet_pton");
550
485
    return -1;
551
486
  }
572
507
  }
573
508
  
574
509
  ret = connect(tcp_sd, &to.in, sizeof(to));
575
 
  if(ret < 0){
 
510
  if (ret < 0){
576
511
    perror("connect");
577
512
    return -1;
578
513
  }
579
 
  
 
514
 
580
515
  const char *out = mandos_protocol_version;
581
516
  written = 0;
582
 
  while(true){
 
517
  while (true){
583
518
    size_t out_size = strlen(out);
584
 
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
519
    ret = TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
585
520
                                   out_size - written));
586
 
    if(ret == -1){
 
521
    if (ret == -1){
587
522
      perror("write");
588
523
      retval = -1;
589
524
      goto mandos_end;
592
527
    if(written < out_size){
593
528
      continue;
594
529
    } else {
595
 
      if(out == mandos_protocol_version){
 
530
      if (out == mandos_protocol_version){
596
531
        written = 0;
597
532
        out = "\r\n";
598
533
      } else {
600
535
      }
601
536
    }
602
537
  }
603
 
  
 
538
 
604
539
  if(debug){
605
540
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
606
541
  }
607
542
  
608
 
  gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
609
 
  
 
543
  gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
 
544
 
610
545
  do{
611
 
    ret = gnutls_handshake(session);
 
546
    ret = gnutls_handshake (session);
612
547
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
613
548
  
614
 
  if(ret != GNUTLS_E_SUCCESS){
 
549
  if (ret != GNUTLS_E_SUCCESS){
615
550
    if(debug){
616
551
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
617
 
      gnutls_perror(ret);
 
552
      gnutls_perror (ret);
618
553
    }
619
554
    retval = -1;
620
555
    goto mandos_end;
626
561
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
627
562
            ip);
628
563
  }
629
 
  
 
564
 
630
565
  while(true){
631
566
    buffer_capacity = adjustbuffer(&buffer, buffer_length,
632
567
                                   buffer_capacity);
633
 
    if(buffer_capacity == 0){
 
568
    if (buffer_capacity == 0){
634
569
      perror("adjustbuffer");
635
570
      retval = -1;
636
571
      goto mandos_end;
637
572
    }
638
573
    
639
 
    sret = gnutls_record_recv(session, buffer+buffer_length,
640
 
                              BUFFER_SIZE);
641
 
    if(sret == 0){
 
574
    ret = gnutls_record_recv(session, buffer+buffer_length,
 
575
                             BUFFER_SIZE);
 
576
    if (ret == 0){
642
577
      break;
643
578
    }
644
 
    if(sret < 0){
645
 
      switch(sret){
 
579
    if (ret < 0){
 
580
      switch(ret){
646
581
      case GNUTLS_E_INTERRUPTED:
647
582
      case GNUTLS_E_AGAIN:
648
583
        break;
649
584
      case GNUTLS_E_REHANDSHAKE:
650
585
        do{
651
 
          ret = gnutls_handshake(session);
 
586
          ret = gnutls_handshake (session);
652
587
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
653
 
        if(ret < 0){
 
588
        if (ret < 0){
654
589
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
655
 
          gnutls_perror(ret);
 
590
          gnutls_perror (ret);
656
591
          retval = -1;
657
592
          goto mandos_end;
658
593
        }
661
596
        fprintf(stderr, "Unknown error while reading data from"
662
597
                " encrypted session with Mandos server\n");
663
598
        retval = -1;
664
 
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
599
        gnutls_bye (session, GNUTLS_SHUT_RDWR);
665
600
        goto mandos_end;
666
601
      }
667
602
    } else {
668
 
      buffer_length += (size_t) sret;
 
603
      buffer_length += (size_t) ret;
669
604
    }
670
605
  }
671
606
  
673
608
    fprintf(stderr, "Closing TLS session\n");
674
609
  }
675
610
  
676
 
  gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
611
  gnutls_bye (session, GNUTLS_SHUT_RDWR);
677
612
  
678
 
  if(buffer_length > 0){
679
 
    decrypted_buffer_size = pgp_packet_decrypt(mc, buffer,
 
613
  if (buffer_length > 0){
 
614
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
680
615
                                               buffer_length,
681
 
                                               &decrypted_buffer);
682
 
    if(decrypted_buffer_size >= 0){
 
616
                                               &decrypted_buffer,
 
617
                                               keydir);
 
618
    if (decrypted_buffer_size >= 0){
683
619
      written = 0;
684
620
      while(written < (size_t) decrypted_buffer_size){
685
 
        ret = (int)fwrite(decrypted_buffer + written, 1,
686
 
                          (size_t)decrypted_buffer_size - written,
687
 
                          stdout);
 
621
        ret = (int)fwrite (decrypted_buffer + written, 1,
 
622
                           (size_t)decrypted_buffer_size - written,
 
623
                           stdout);
688
624
        if(ret == 0 and ferror(stdout)){
689
625
          if(debug){
690
626
            fprintf(stderr, "Error writing encrypted data: %s\n",
707
643
  
708
644
 mandos_end:
709
645
  free(buffer);
710
 
  ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
711
 
  if(ret == -1){
712
 
    perror("close");
713
 
  }
714
 
  gnutls_deinit(session);
 
646
  close(tcp_sd);
 
647
  gnutls_deinit (session);
715
648
  return retval;
716
649
}
717
650
 
735
668
  /* Called whenever a service has been resolved successfully or
736
669
     timed out */
737
670
  
738
 
  switch(event) {
 
671
  switch (event) {
739
672
  default:
740
673
  case AVAHI_RESOLVER_FAILURE:
741
674
    fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
753
686
                interface, port);
754
687
      }
755
688
      int ret = start_mandos_communication(ip, port, interface, mc);
756
 
      if(ret == 0){
 
689
      if (ret == 0){
757
690
        avahi_simple_poll_quit(mc->simple_poll);
758
691
      }
759
692
    }
777
710
  /* Called whenever a new services becomes available on the LAN or
778
711
     is removed from the LAN */
779
712
  
780
 
  switch(event) {
 
713
  switch (event) {
781
714
  default:
782
715
  case AVAHI_BROWSER_FAILURE:
783
716
    
792
725
       the callback function is called the Avahi server will free the
793
726
       resolver for us. */
794
727
    
795
 
    if(!(avahi_s_service_resolver_new(mc->server, interface,
 
728
    if (!(avahi_s_service_resolver_new(mc->server, interface,
796
729
                                       protocol, name, type, domain,
797
730
                                       AVAHI_PROTO_INET6, 0,
798
731
                                       resolve_callback, mc)))
812
745
  }
813
746
}
814
747
 
 
748
/* Combines file name and path and returns the malloced new
 
749
   string. some sane checks could/should be added */
 
750
static char *combinepath(const char *first, const char *second){
 
751
  char *tmp;
 
752
  int ret = asprintf(&tmp, "%s/%s", first, second);
 
753
  if(ret < 0){
 
754
    return NULL;
 
755
  }
 
756
  return tmp;
 
757
}
 
758
 
 
759
 
815
760
int main(int argc, char *argv[]){
816
761
    AvahiSServiceBrowser *sb = NULL;
817
762
    int error;
823
768
    uid_t uid;
824
769
    gid_t gid;
825
770
    char *connect_to = NULL;
826
 
    char tempdir[] = "/tmp/mandosXXXXXX";
827
771
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
828
 
    const char *seckey = PATHDIR "/" SECKEY;
829
 
    const char *pubkey = PATHDIR "/" PUBKEY;
830
 
    
 
772
    char *pubkeyfilename = NULL;
 
773
    char *seckeyfilename = NULL;
 
774
    const char *pubkeyname = "pubkey.txt";
 
775
    const char *seckeyname = "seckey.txt";
831
776
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
832
 
                          .dh_bits = 1024, .priority = "SECURE256"
833
 
                          ":!CTYPE-X.509:+CTYPE-OPENPGP" };
 
777
                          .dh_bits = 1024, .priority = "SECURE256"};
834
778
    bool gnutls_initalized = false;
835
 
    bool gpgme_initalized = false;
836
779
    
837
780
    {
838
781
      struct argp_option options[] = {
839
782
        { .name = "debug", .key = 128,
840
783
          .doc = "Debug mode", .group = 3 },
841
784
        { .name = "connect", .key = 'c',
842
 
          .arg = "ADDRESS:PORT",
843
 
          .doc = "Connect directly to a specific Mandos server",
 
785
          .arg = "IP",
 
786
          .doc = "Connect directly to a sepcified mandos server",
844
787
          .group = 1 },
845
788
        { .name = "interface", .key = 'i',
846
 
          .arg = "NAME",
847
 
          .doc = "Interface that will be used to search for Mandos"
848
 
          " servers",
 
789
          .arg = "INTERFACE",
 
790
          .doc = "Interface that Avahi will conntect through",
 
791
          .group = 1 },
 
792
        { .name = "keydir", .key = 'd',
 
793
          .arg = "KEYDIR",
 
794
          .doc = "Directory where the openpgp keyring is",
849
795
          .group = 1 },
850
796
        { .name = "seckey", .key = 's',
851
 
          .arg = "FILE",
852
 
          .doc = "OpenPGP secret key file base name",
 
797
          .arg = "SECKEY",
 
798
          .doc = "Secret openpgp key for gnutls authentication",
853
799
          .group = 1 },
854
800
        { .name = "pubkey", .key = 'p',
855
 
          .arg = "FILE",
856
 
          .doc = "OpenPGP public key file base name",
 
801
          .arg = "PUBKEY",
 
802
          .doc = "Public openpgp key for gnutls authentication",
857
803
          .group = 2 },
858
804
        { .name = "dh-bits", .key = 129,
859
805
          .arg = "BITS",
860
 
          .doc = "Bit length of the prime number used in the"
861
 
          " Diffie-Hellman key exchange",
 
806
          .doc = "dh-bits to use in gnutls communication",
862
807
          .group = 2 },
863
808
        { .name = "priority", .key = 130,
864
 
          .arg = "STRING",
865
 
          .doc = "GnuTLS priority string for the TLS handshake",
866
 
          .group = 1 },
 
809
          .arg = "PRIORITY",
 
810
          .doc = "GNUTLS priority", .group = 1 },
867
811
        { .name = NULL }
868
812
      };
 
813
 
869
814
      
870
 
      error_t parse_opt(int key, char *arg,
871
 
                        struct argp_state *state) {
872
 
        switch(key) {
873
 
        case 128:               /* --debug */
 
815
      error_t parse_opt (int key, char *arg,
 
816
                         struct argp_state *state) {
 
817
        /* Get the INPUT argument from `argp_parse', which we know is
 
818
           a pointer to our plugin list pointer. */
 
819
        switch (key) {
 
820
        case 128:
874
821
          debug = true;
875
822
          break;
876
 
        case 'c':               /* --connect */
 
823
        case 'c':
877
824
          connect_to = arg;
878
825
          break;
879
 
        case 'i':               /* --interface */
 
826
        case 'i':
880
827
          interface = arg;
881
828
          break;
882
 
        case 's':               /* --seckey */
883
 
          seckey = arg;
884
 
          break;
885
 
        case 'p':               /* --pubkey */
886
 
          pubkey = arg;
887
 
          break;
888
 
        case 129:               /* --dh-bits */
889
 
          ret = sscanf(arg, "%u", &mc.dh_bits);
890
 
          if(ret != 1){
891
 
            fprintf(stderr, "Bad number of DH bits\n");
 
829
        case 'd':
 
830
          keydir = arg;
 
831
          break;
 
832
        case 's':
 
833
          seckeyname = arg;
 
834
          break;
 
835
        case 'p':
 
836
          pubkeyname = arg;
 
837
          break;
 
838
        case 129:
 
839
          errno = 0;
 
840
          mc.dh_bits = (unsigned int) strtol(arg, NULL, 10);
 
841
          if (errno){
 
842
            perror("strtol");
892
843
            exit(EXIT_FAILURE);
893
844
          }
894
845
          break;
895
 
        case 130:               /* --priority */
 
846
        case 130:
896
847
          mc.priority = arg;
897
848
          break;
898
849
        case ARGP_KEY_ARG:
899
 
          argp_usage(state);
 
850
          argp_usage (state);
900
851
        case ARGP_KEY_END:
901
852
          break;
902
853
        default:
904
855
        }
905
856
        return 0;
906
857
      }
907
 
      
 
858
 
908
859
      struct argp argp = { .options = options, .parser = parse_opt,
909
860
                           .args_doc = "",
910
861
                           .doc = "Mandos client -- Get and decrypt"
911
 
                           " passwords from a Mandos server" };
912
 
      ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
913
 
      if(ret == ARGP_ERR_UNKNOWN){
 
862
                           " passwords from mandos server" };
 
863
      ret = argp_parse (&argp, argc, argv, 0, 0, NULL);
 
864
      if (ret == ARGP_ERR_UNKNOWN){
914
865
        fprintf(stderr, "Unknown error while parsing arguments\n");
915
866
        exitcode = EXIT_FAILURE;
916
867
        goto end;
917
868
      }
918
869
    }
 
870
      
 
871
    pubkeyfilename = combinepath(keydir, pubkeyname);
 
872
    if (pubkeyfilename == NULL){
 
873
      perror("combinepath");
 
874
      exitcode = EXIT_FAILURE;
 
875
      goto end;
 
876
    }
 
877
    
 
878
    seckeyfilename = combinepath(keydir, seckeyname);
 
879
    if (seckeyfilename == NULL){
 
880
      perror("combinepath");
 
881
      exitcode = EXIT_FAILURE;
 
882
      goto end;
 
883
    }
 
884
 
 
885
    ret = init_gnutls_global(&mc, pubkeyfilename, seckeyfilename);
 
886
    if (ret == -1){
 
887
      fprintf(stderr, "init_gnutls_global failed\n");
 
888
      exitcode = EXIT_FAILURE;
 
889
      goto end;
 
890
    } else {
 
891
      gnutls_initalized = true;
 
892
    }
919
893
    
920
894
    /* If the interface is down, bring it up */
921
895
    {
941
915
          goto end;
942
916
        }
943
917
      }
944
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
945
 
      if(ret == -1){
946
 
        perror("close");
947
 
      }
 
918
      close(sd);
948
919
    }
949
920
    
950
921
    uid = getuid();
951
922
    gid = getgid();
952
923
    
953
924
    ret = setuid(uid);
954
 
    if(ret == -1){
 
925
    if (ret == -1){
955
926
      perror("setuid");
956
927
    }
957
928
    
958
929
    setgid(gid);
959
 
    if(ret == -1){
 
930
    if (ret == -1){
960
931
      perror("setgid");
961
932
    }
962
933
    
963
 
    ret = init_gnutls_global(&mc, pubkey, seckey);
964
 
    if(ret == -1){
965
 
      fprintf(stderr, "init_gnutls_global failed\n");
966
 
      exitcode = EXIT_FAILURE;
967
 
      goto end;
968
 
    } else {
969
 
      gnutls_initalized = true;
970
 
    }
971
 
    
972
 
    if(mkdtemp(tempdir) == NULL){
973
 
      perror("mkdtemp");
974
 
      tempdir[0] = '\0';
975
 
      goto end;
976
 
    }
977
 
    
978
 
    if(not init_gpgme(&mc, pubkey, seckey, tempdir)){
979
 
      fprintf(stderr, "gpgme_initalized failed\n");
980
 
      exitcode = EXIT_FAILURE;
981
 
      goto end;
982
 
    } else {
983
 
      gpgme_initalized = true;
984
 
    }
985
 
    
986
934
    if_index = (AvahiIfIndex) if_nametoindex(interface);
987
935
    if(if_index == 0){
988
936
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
998
946
        exitcode = EXIT_FAILURE;
999
947
        goto end;
1000
948
      }
1001
 
      uint16_t port;
1002
 
      ret = sscanf(address+1, "%" SCNu16, &port);
1003
 
      if(ret != 1){
1004
 
        fprintf(stderr, "Bad port number\n");
 
949
      errno = 0;
 
950
      uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
 
951
      if(errno){
 
952
        perror("Bad port number");
1005
953
        exitcode = EXIT_FAILURE;
1006
954
        goto end;
1007
955
      }
1016
964
      goto end;
1017
965
    }
1018
966
    
1019
 
    if(not debug){
 
967
    if (not debug){
1020
968
      avahi_set_log_function(empty_log);
1021
969
    }
1022
970
    
1025
973
    
1026
974
    /* Allocate main Avahi loop object */
1027
975
    mc.simple_poll = avahi_simple_poll_new();
1028
 
    if(mc.simple_poll == NULL) {
 
976
    if (mc.simple_poll == NULL) {
1029
977
        fprintf(stderr, "Avahi: Failed to create simple poll"
1030
978
                " object.\n");
1031
979
        exitcode = EXIT_FAILURE;
1032
980
        goto end;
1033
981
    }
1034
 
    
 
982
 
1035
983
    {
1036
984
      AvahiServerConfig config;
1037
985
      /* Do not publish any local Zeroconf records */
1040
988
      config.publish_addresses = 0;
1041
989
      config.publish_workstation = 0;
1042
990
      config.publish_domain = 0;
1043
 
      
 
991
 
1044
992
      /* Allocate a new server */
1045
993
      mc.server = avahi_server_new(avahi_simple_poll_get
1046
994
                                   (mc.simple_poll), &config, NULL,
1047
995
                                   NULL, &error);
1048
 
      
 
996
    
1049
997
      /* Free the Avahi configuration data */
1050
998
      avahi_server_config_free(&config);
1051
999
    }
1052
1000
    
1053
1001
    /* Check if creating the Avahi server object succeeded */
1054
 
    if(mc.server == NULL) {
 
1002
    if (mc.server == NULL) {
1055
1003
        fprintf(stderr, "Failed to create Avahi server: %s\n",
1056
1004
                avahi_strerror(error));
1057
1005
        exitcode = EXIT_FAILURE;
1063
1011
                                     AVAHI_PROTO_INET6,
1064
1012
                                     "_mandos._tcp", NULL, 0,
1065
1013
                                     browse_callback, &mc);
1066
 
    if(sb == NULL) {
 
1014
    if (sb == NULL) {
1067
1015
        fprintf(stderr, "Failed to create service browser: %s\n",
1068
1016
                avahi_strerror(avahi_server_errno(mc.server)));
1069
1017
        exitcode = EXIT_FAILURE;
1071
1019
    }
1072
1020
    
1073
1021
    /* Run the main loop */
1074
 
    
1075
 
    if(debug){
 
1022
 
 
1023
    if (debug){
1076
1024
      fprintf(stderr, "Starting Avahi loop search\n");
1077
1025
    }
1078
1026
    
1079
1027
    avahi_simple_poll_loop(mc.simple_poll);
1080
1028
    
1081
1029
 end:
1082
 
    
1083
 
    if(debug){
 
1030
 
 
1031
    if (debug){
1084
1032
      fprintf(stderr, "%s exiting\n", argv[0]);
1085
1033
    }
1086
1034
    
1087
1035
    /* Cleanup things */
1088
 
    if(sb != NULL)
 
1036
    if (sb != NULL)
1089
1037
        avahi_s_service_browser_free(sb);
1090
1038
    
1091
 
    if(mc.server != NULL)
 
1039
    if (mc.server != NULL)
1092
1040
        avahi_server_free(mc.server);
1093
 
    
1094
 
    if(mc.simple_poll != NULL)
 
1041
 
 
1042
    if (mc.simple_poll != NULL)
1095
1043
        avahi_simple_poll_free(mc.simple_poll);
1096
 
    
1097
 
    if(gnutls_initalized){
 
1044
    free(pubkeyfilename);
 
1045
    free(seckeyfilename);
 
1046
 
 
1047
    if (gnutls_initalized){
1098
1048
      gnutls_certificate_free_credentials(mc.cred);
1099
 
      gnutls_global_deinit();
1100
 
      gnutls_dh_params_deinit(mc.dh_params);
1101
 
    }
1102
 
    
1103
 
    if(gpgme_initalized){
1104
 
      gpgme_release(mc.ctx);
1105
 
    }
1106
 
    
1107
 
    /* Removes the temp directory used by GPGME */
1108
 
    if(tempdir[0] != '\0'){
1109
 
      DIR *d;
1110
 
      struct dirent *direntry;
1111
 
      d = opendir(tempdir);
1112
 
      if(d == NULL){
1113
 
        if(errno != ENOENT){
1114
 
          perror("opendir");
1115
 
        }
1116
 
      } else {
1117
 
        while(true){
1118
 
          direntry = readdir(d);
1119
 
          if(direntry == NULL){
1120
 
            break;
1121
 
          }
1122
 
          if(direntry->d_type == DT_REG){
1123
 
            char *fullname = NULL;
1124
 
            ret = asprintf(&fullname, "%s/%s", tempdir,
1125
 
                           direntry->d_name);
1126
 
            if(ret < 0){
1127
 
              perror("asprintf");
1128
 
              continue;
1129
 
            }
1130
 
            ret = unlink(fullname);
1131
 
            if(ret == -1){
1132
 
              fprintf(stderr, "unlink(\"%s\"): %s",
1133
 
                      fullname, strerror(errno));
1134
 
            }
1135
 
            free(fullname);
1136
 
          }
1137
 
        }
1138
 
        closedir(d);
1139
 
      }
1140
 
      ret = rmdir(tempdir);
1141
 
      if(ret == -1 and errno != ENOENT){
1142
 
        perror("rmdir");
1143
 
      }
1144
 
    }
1145
 
          
 
1049
      gnutls_global_deinit ();
 
1050
    }
 
1051
    
1146
1052
    return exitcode;
1147
1053
}