/mandos/release

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

« 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:
32
32
#define _LARGEFILE_SOURCE
33
33
#define _FILE_OFFSET_BITS 64
34
34
 
35
 
#include <stdio.h>
36
 
#include <assert.h>
37
 
#include <stdlib.h>
38
 
#include <time.h>
39
 
#include <net/if.h>             /* if_nametoindex */
40
 
#include <sys/ioctl.h>          /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
41
 
                                   SIOCSIFFLAGS */
 
35
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
 
36
 
 
37
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
 
38
                                   stdout, ferror() */
 
39
#include <stdint.h>             /* uint16_t, uint32_t */
 
40
#include <stddef.h>             /* NULL, size_t, ssize_t */
 
41
#include <stdlib.h>             /* free(), EXIT_SUCCESS, EXIT_FAILURE,
 
42
                                   srand() */
 
43
#include <stdbool.h>            /* bool, true */
 
44
#include <string.h>             /* memset(), strcmp(), strlen(),
 
45
                                   strerror(), asprintf(), strcpy() */
 
46
#include <sys/ioctl.h>          /* ioctl */
 
47
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
 
48
                                   sockaddr_in6, PF_INET6,
 
49
                                   SOCK_STREAM, INET6_ADDRSTRLEN,
 
50
                                   uid_t, gid_t */
 
51
#include <inttypes.h>           /* PRIu16 */
 
52
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
 
53
                                   struct in6_addr, inet_pton(),
 
54
                                   connect() */
 
55
#include <assert.h>             /* assert() */
 
56
#include <errno.h>              /* perror(), errno */
 
57
#include <time.h>               /* time() */
42
58
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
43
 
                                   SIOCSIFFLAGS */
 
59
                                   SIOCSIFFLAGS, if_indextoname(),
 
60
                                   if_nametoindex(), IF_NAMESIZE */
 
61
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
 
62
                                   getuid(), getgid(), setuid(),
 
63
                                   setgid() */
 
64
#include <netinet/in.h>
 
65
#include <arpa/inet.h>          /* inet_pton(), htons */
 
66
#include <iso646.h>             /* not, and */
 
67
#include <argp.h>               /* struct argp_option, error_t, struct
 
68
                                   argp_state, struct argp,
 
69
                                   argp_parse(), ARGP_KEY_ARG,
 
70
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
44
71
 
 
72
/* Avahi */
 
73
/* All Avahi types, constants and functions
 
74
 Avahi*, avahi_*,
 
75
 AVAHI_* */
45
76
#include <avahi-core/core.h>
46
77
#include <avahi-core/lookup.h>
47
78
#include <avahi-core/log.h>
49
80
#include <avahi-common/malloc.h>
50
81
#include <avahi-common/error.h>
51
82
 
52
 
/* Mandos client part */
53
 
#include <sys/types.h>          /* socket(), inet_pton() */
54
 
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
55
 
                                   struct in6_addr, inet_pton() */
56
 
#include <gnutls/gnutls.h>      /* All GnuTLS stuff */
57
 
#include <gnutls/openpgp.h>     /* GnuTLS with openpgp stuff */
58
 
 
59
 
#include <unistd.h>             /* close() */
60
 
#include <netinet/in.h>
61
 
#include <stdbool.h>            /* true */
62
 
#include <string.h>             /* memset */
63
 
#include <arpa/inet.h>          /* inet_pton() */
64
 
#include <iso646.h>             /* not */
65
 
#include <net/if.h>             /* IF_NAMESIZE */
 
83
/* GnuTLS */
 
84
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
 
85
                                   functions:
 
86
                                   gnutls_*
 
87
                                   init_gnutls_session(),
 
88
                                   GNUTLS_* */
 
89
#include <gnutls/openpgp.h>     /* gnutls_certificate_set_openpgp_key_file(),
 
90
                                   GNUTLS_OPENPGP_FMT_BASE64 */
66
91
 
67
92
/* GPGME */
68
 
#include <errno.h>              /* perror() */
69
 
#include <gpgme.h>
70
 
 
71
 
/* getopt_long */
72
 
#include <getopt.h>
 
93
#include <gpgme.h>              /* All GPGME types, constants and
 
94
                                   functions:
 
95
                                   gpgme_*
 
96
                                   GPGME_PROTOCOL_OpenPGP,
 
97
                                   GPG_ERR_NO_* */
73
98
 
74
99
#define BUFFER_SIZE 256
75
100
 
 
101
bool debug = false;
76
102
static const char *keydir = "/conf/conf.d/mandos";
77
 
static const char *pubkeyfile = "pubkey.txt";
78
 
static const char *seckeyfile = "seckey.txt";
79
 
 
80
 
bool debug = false;
81
 
 
82
 
/* Used for passing in values through all the callback functions */
 
103
static const char mandos_protocol_version[] = "1";
 
104
const char *argp_program_version = "password-request 1.0";
 
105
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
106
 
 
107
/* Used for passing in values through the Avahi callback functions */
83
108
typedef struct {
84
109
  AvahiSimplePoll *simple_poll;
85
110
  AvahiServer *server;
86
111
  gnutls_certificate_credentials_t cred;
87
112
  unsigned int dh_bits;
 
113
  gnutls_dh_params_t dh_params;
88
114
  const char *priority;
89
115
} mandos_context;
90
116
 
 
117
/*
 
118
 * Make room in "buffer" for at least BUFFER_SIZE additional bytes.
 
119
 * "buffer_capacity" is how much is currently allocated,
 
120
 * "buffer_length" is how much is already used.
 
121
 */
 
122
size_t adjustbuffer(char **buffer, size_t buffer_length,
 
123
                  size_t buffer_capacity){
 
124
  if (buffer_length + BUFFER_SIZE > buffer_capacity){
 
125
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
126
    if (buffer == NULL){
 
127
      return 0;
 
128
    }
 
129
    buffer_capacity += BUFFER_SIZE;
 
130
  }
 
131
  return buffer_capacity;
 
132
}
 
133
 
91
134
/* 
92
135
 * Decrypt OpenPGP data using keyrings in HOMEDIR.
93
136
 * Returns -1 on error
171
214
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
172
215
            gpgme_strsource(rc), gpgme_strerror(rc));
173
216
    plaintext_length = -1;
 
217
    if (debug){
 
218
      gpgme_decrypt_result_t result;
 
219
      result = gpgme_op_decrypt_result(ctx);
 
220
      if (result == NULL){
 
221
        fprintf(stderr, "gpgme_op_decrypt_result failed\n");
 
222
      } else {
 
223
        fprintf(stderr, "Unsupported algorithm: %s\n",
 
224
                result->unsupported_algorithm);
 
225
        fprintf(stderr, "Wrong key usage: %u\n",
 
226
                result->wrong_key_usage);
 
227
        if(result->file_name != NULL){
 
228
          fprintf(stderr, "File name: %s\n", result->file_name);
 
229
        }
 
230
        gpgme_recipient_t recipient;
 
231
        recipient = result->recipients;
 
232
        if(recipient){
 
233
          while(recipient != NULL){
 
234
            fprintf(stderr, "Public key algorithm: %s\n",
 
235
                    gpgme_pubkey_algo_name(recipient->pubkey_algo));
 
236
            fprintf(stderr, "Key ID: %s\n", recipient->keyid);
 
237
            fprintf(stderr, "Secret key available: %s\n",
 
238
                    recipient->status == GPG_ERR_NO_SECKEY
 
239
                    ? "No" : "Yes");
 
240
            recipient = recipient->next;
 
241
          }
 
242
        }
 
243
      }
 
244
    }
174
245
    goto decrypt_end;
175
246
  }
176
247
  
178
249
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
179
250
  }
180
251
  
181
 
  if (debug){
182
 
    gpgme_decrypt_result_t result;
183
 
    result = gpgme_op_decrypt_result(ctx);
184
 
    if (result == NULL){
185
 
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
186
 
    } else {
187
 
      fprintf(stderr, "Unsupported algorithm: %s\n",
188
 
              result->unsupported_algorithm);
189
 
      fprintf(stderr, "Wrong key usage: %d\n",
190
 
              result->wrong_key_usage);
191
 
      if(result->file_name != NULL){
192
 
        fprintf(stderr, "File name: %s\n", result->file_name);
193
 
      }
194
 
      gpgme_recipient_t recipient;
195
 
      recipient = result->recipients;
196
 
      if(recipient){
197
 
        while(recipient != NULL){
198
 
          fprintf(stderr, "Public key algorithm: %s\n",
199
 
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
200
 
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
201
 
          fprintf(stderr, "Secret key available: %s\n",
202
 
                  recipient->status == GPG_ERR_NO_SECKEY
203
 
                  ? "No" : "Yes");
204
 
          recipient = recipient->next;
205
 
        }
206
 
      }
207
 
    }
208
 
  }
209
 
  
210
252
  /* Seek back to the beginning of the GPGME plaintext data buffer */
211
253
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
212
254
    perror("pgpme_data_seek");
216
258
  
217
259
  *plaintext = NULL;
218
260
  while(true){
219
 
    if (plaintext_length + BUFFER_SIZE
220
 
        > (ssize_t) plaintext_capacity){
221
 
      *plaintext = realloc(*plaintext, plaintext_capacity
222
 
                            + BUFFER_SIZE);
223
 
      if (*plaintext == NULL){
224
 
        perror("realloc");
 
261
    plaintext_capacity = adjustbuffer(plaintext,
 
262
                                      (size_t)plaintext_length,
 
263
                                      plaintext_capacity);
 
264
    if (plaintext_capacity == 0){
 
265
        perror("adjustbuffer");
225
266
        plaintext_length = -1;
226
267
        goto decrypt_end;
227
 
      }
228
 
      plaintext_capacity += BUFFER_SIZE;
229
268
    }
230
269
    
231
270
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
262
301
}
263
302
 
264
303
static const char * safer_gnutls_strerror (int value) {
265
 
  const char *ret = gnutls_strerror (value);
 
304
  const char *ret = gnutls_strerror (value); /* Spurious warning */
266
305
  if (ret == NULL)
267
306
    ret = "(unknown)";
268
307
  return ret;
274
313
  fprintf(stderr, "GnuTLS: %s", string);
275
314
}
276
315
 
277
 
static int initgnutls(mandos_context *mc, gnutls_session_t *session,
278
 
                      gnutls_dh_params_t *dh_params){
 
316
static int init_gnutls_global(mandos_context *mc,
 
317
                              const char *pubkeyfilename,
 
318
                              const char *seckeyfilename){
279
319
  int ret;
280
320
  
281
321
  if(debug){
282
322
    fprintf(stderr, "Initializing GnuTLS\n");
283
323
  }
284
 
 
285
 
  if ((ret = gnutls_global_init ())
286
 
      != GNUTLS_E_SUCCESS) {
 
324
  
 
325
  ret = gnutls_global_init();
 
326
  if (ret != GNUTLS_E_SUCCESS) {
287
327
    fprintf (stderr, "GnuTLS global_init: %s\n",
288
328
             safer_gnutls_strerror(ret));
289
329
    return -1;
298
338
  }
299
339
  
300
340
  /* OpenPGP credentials */
301
 
  if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
302
 
      != GNUTLS_E_SUCCESS) {
303
 
    fprintf (stderr, "GnuTLS memory error: %s\n",
 
341
  gnutls_certificate_allocate_credentials(&mc->cred);
 
342
  if (ret != GNUTLS_E_SUCCESS){
 
343
    fprintf (stderr, "GnuTLS memory error: %s\n", /* Spurious
 
344
                                                     warning */
304
345
             safer_gnutls_strerror(ret));
 
346
    gnutls_global_deinit ();
305
347
    return -1;
306
348
  }
307
349
  
308
350
  if(debug){
309
351
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
310
 
            " and keyfile %s as GnuTLS credentials\n", pubkeyfile,
311
 
            seckeyfile);
 
352
            " and keyfile %s as GnuTLS credentials\n", pubkeyfilename,
 
353
            seckeyfilename);
312
354
  }
313
355
  
314
356
  ret = gnutls_certificate_set_openpgp_key_file
315
 
    (mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
 
357
    (mc->cred, pubkeyfilename, seckeyfilename,
 
358
     GNUTLS_OPENPGP_FMT_BASE64);
316
359
  if (ret != GNUTLS_E_SUCCESS) {
317
360
    fprintf(stderr,
318
361
            "Error[%d] while reading the OpenPGP key pair ('%s',"
319
 
            " '%s')\n", ret, pubkeyfile, seckeyfile);
 
362
            " '%s')\n", ret, pubkeyfilename, seckeyfilename);
320
363
    fprintf(stdout, "The GnuTLS error is: %s\n",
321
364
            safer_gnutls_strerror(ret));
322
 
    return -1;
 
365
    goto globalfail;
323
366
  }
324
367
  
325
368
  /* GnuTLS server initialization */
326
 
  ret = gnutls_dh_params_init(dh_params);
 
369
  ret = gnutls_dh_params_init(&mc->dh_params);
327
370
  if (ret != GNUTLS_E_SUCCESS) {
328
371
    fprintf (stderr, "Error in GnuTLS DH parameter initialization:"
329
372
             " %s\n", safer_gnutls_strerror(ret));
330
 
    return -1;
 
373
    goto globalfail;
331
374
  }
332
 
  ret = gnutls_dh_params_generate2(*dh_params, mc->dh_bits);
 
375
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
333
376
  if (ret != GNUTLS_E_SUCCESS) {
334
377
    fprintf (stderr, "Error in GnuTLS prime generation: %s\n",
335
378
             safer_gnutls_strerror(ret));
336
 
    return -1;
 
379
    goto globalfail;
337
380
  }
338
381
  
339
 
  gnutls_certificate_set_dh_params(mc->cred, *dh_params);
340
 
  
 
382
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
 
383
 
 
384
  return 0;
 
385
 
 
386
 globalfail:
 
387
 
 
388
  gnutls_certificate_free_credentials(mc->cred);
 
389
  gnutls_global_deinit();
 
390
  return -1;
 
391
 
 
392
}
 
393
 
 
394
static int init_gnutls_session(mandos_context *mc,
 
395
                               gnutls_session_t *session){
 
396
  int ret;
341
397
  /* GnuTLS session creation */
342
398
  ret = gnutls_init(session, GNUTLS_SERVER);
343
399
  if (ret != GNUTLS_E_SUCCESS){
352
408
      fprintf(stderr, "Syntax error at: %s\n", err);
353
409
      fprintf(stderr, "GnuTLS error: %s\n",
354
410
              safer_gnutls_strerror(ret));
 
411
      gnutls_deinit (*session);
355
412
      return -1;
356
413
    }
357
414
  }
361
418
  if (ret != GNUTLS_E_SUCCESS) {
362
419
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
363
420
            safer_gnutls_strerror(ret));
 
421
    gnutls_deinit (*session);
364
422
    return -1;
365
423
  }
366
424
  
382
440
                                      AvahiIfIndex if_index,
383
441
                                      mandos_context *mc){
384
442
  int ret, tcp_sd;
385
 
  struct sockaddr_in6 to;
 
443
  union { struct sockaddr in; struct sockaddr_in6 in6; } to;
386
444
  char *buffer = NULL;
387
445
  char *decrypted_buffer;
388
446
  size_t buffer_length = 0;
389
447
  size_t buffer_capacity = 0;
390
448
  ssize_t decrypted_buffer_size;
391
 
  size_t written = 0;
 
449
  size_t written;
392
450
  int retval = 0;
393
451
  char interface[IF_NAMESIZE];
394
452
  gnutls_session_t session;
395
 
  gnutls_dh_params_t dh_params;
396
453
  
397
 
  ret = initgnutls (mc, &session, &dh_params);
 
454
  ret = init_gnutls_session (mc, &session);
398
455
  if (ret != 0){
399
456
    return -1;
400
457
  }
401
458
  
402
459
  if(debug){
403
 
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
404
 
            ip, port);
 
460
    fprintf(stderr, "Setting up a tcp connection to %s, port %" PRIu16
 
461
            "\n", ip, port);
405
462
  }
406
463
  
407
464
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
418
475
    fprintf(stderr, "Binding to interface %s\n", interface);
419
476
  }
420
477
  
421
 
  memset(&to,0,sizeof(to));     /* Spurious warning */
422
 
  to.sin6_family = AF_INET6;
 
478
  memset(&to, 0, sizeof(to));
 
479
  to.in6.sin6_family = AF_INET6;
423
480
  /* It would be nice to have a way to detect if we were passed an
424
481
     IPv4 address here.   Now we assume an IPv6 address. */
425
 
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
 
482
  ret = inet_pton(AF_INET6, ip, &to.in6.sin6_addr);
426
483
  if (ret < 0 ){
427
484
    perror("inet_pton");
428
485
    return -1;
431
488
    fprintf(stderr, "Bad address: %s\n", ip);
432
489
    return -1;
433
490
  }
434
 
  to.sin6_port = htons(port);   /* Spurious warning */
 
491
  to.in6.sin6_port = htons(port); /* Spurious warning */
435
492
  
436
 
  to.sin6_scope_id = (uint32_t)if_index;
 
493
  to.in6.sin6_scope_id = (uint32_t)if_index;
437
494
  
438
495
  if(debug){
439
 
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
 
496
    fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
 
497
            port);
440
498
    char addrstr[INET6_ADDRSTRLEN] = "";
441
 
    if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr,
 
499
    if(inet_ntop(to.in6.sin6_family, &(to.in6.sin6_addr), addrstr,
442
500
                 sizeof(addrstr)) == NULL){
443
501
      perror("inet_ntop");
444
502
    } else {
448
506
    }
449
507
  }
450
508
  
451
 
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
 
509
  ret = connect(tcp_sd, &to.in, sizeof(to));
452
510
  if (ret < 0){
453
511
    perror("connect");
454
512
    return -1;
455
513
  }
456
 
  
 
514
 
 
515
  const char *out = mandos_protocol_version;
 
516
  written = 0;
 
517
  while (true){
 
518
    size_t out_size = strlen(out);
 
519
    ret = TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
520
                                   out_size - written));
 
521
    if (ret == -1){
 
522
      perror("write");
 
523
      retval = -1;
 
524
      goto mandos_end;
 
525
    }
 
526
    written += (size_t)ret;
 
527
    if(written < out_size){
 
528
      continue;
 
529
    } else {
 
530
      if (out == mandos_protocol_version){
 
531
        written = 0;
 
532
        out = "\r\n";
 
533
      } else {
 
534
        break;
 
535
      }
 
536
    }
 
537
  }
 
538
 
457
539
  if(debug){
458
540
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
459
541
  }
460
542
  
461
543
  gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
462
 
  
463
 
  ret = gnutls_handshake (session);
 
544
 
 
545
  do{
 
546
    ret = gnutls_handshake (session);
 
547
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
464
548
  
465
549
  if (ret != GNUTLS_E_SUCCESS){
466
550
    if(debug){
479
563
  }
480
564
 
481
565
  while(true){
482
 
    if (buffer_length + BUFFER_SIZE > buffer_capacity){
483
 
      buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
484
 
      if (buffer == NULL){
485
 
        perror("realloc");
486
 
        retval = -1;
487
 
        goto mandos_end;
488
 
      }
489
 
      buffer_capacity += BUFFER_SIZE;
 
566
    buffer_capacity = adjustbuffer(&buffer, buffer_length,
 
567
                                   buffer_capacity);
 
568
    if (buffer_capacity == 0){
 
569
      perror("adjustbuffer");
 
570
      retval = -1;
 
571
      goto mandos_end;
490
572
    }
491
573
    
492
574
    ret = gnutls_record_recv(session, buffer+buffer_length,
500
582
      case GNUTLS_E_AGAIN:
501
583
        break;
502
584
      case GNUTLS_E_REHANDSHAKE:
503
 
        ret = gnutls_handshake (session);
 
585
        do{
 
586
          ret = gnutls_handshake (session);
 
587
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
504
588
        if (ret < 0){
505
589
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
506
590
          gnutls_perror (ret);
532
616
                                               &decrypted_buffer,
533
617
                                               keydir);
534
618
    if (decrypted_buffer_size >= 0){
 
619
      written = 0;
535
620
      while(written < (size_t) decrypted_buffer_size){
536
621
        ret = (int)fwrite (decrypted_buffer + written, 1,
537
622
                           (size_t)decrypted_buffer_size - written,
550
635
    } else {
551
636
      retval = -1;
552
637
    }
 
638
  } else {
 
639
    retval = -1;
553
640
  }
554
641
  
555
642
  /* Shutdown procedure */
558
645
  free(buffer);
559
646
  close(tcp_sd);
560
647
  gnutls_deinit (session);
561
 
  gnutls_certificate_free_credentials (mc->cred);
562
 
  gnutls_global_deinit ();
563
648
  return retval;
564
649
}
565
650
 
578
663
                             flags,
579
664
                             void* userdata) {
580
665
  mandos_context *mc = userdata;
581
 
  assert(r);                    /* Spurious warning */
 
666
  assert(r);
582
667
  
583
668
  /* Called whenever a service has been resolved successfully or
584
669
     timed out */
596
681
      char ip[AVAHI_ADDRESS_STR_MAX];
597
682
      avahi_address_snprint(ip, sizeof(ip), address);
598
683
      if(debug){
599
 
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %d) on"
600
 
                " port %d\n", name, host_name, ip, interface, port);
 
684
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
685
                PRIu16 ") on port %d\n", name, host_name, ip,
 
686
                interface, port);
601
687
      }
602
688
      int ret = start_mandos_communication(ip, port, interface, mc);
603
689
      if (ret == 0){
604
 
        exit(EXIT_SUCCESS);
 
690
        avahi_simple_poll_quit(mc->simple_poll);
605
691
      }
606
692
    }
607
693
  }
619
705
                             flags,
620
706
                             void* userdata) {
621
707
  mandos_context *mc = userdata;
622
 
  assert(b);                    /* Spurious warning */
 
708
  assert(b);
623
709
  
624
710
  /* Called whenever a new services becomes available on the LAN or
625
711
     is removed from the LAN */
661
747
 
662
748
/* Combines file name and path and returns the malloced new
663
749
   string. some sane checks could/should be added */
664
 
static const char *combinepath(const char *first, const char *second){
665
 
  size_t f_len = strlen(first);
666
 
  size_t s_len = strlen(second);
667
 
  char *tmp = malloc(f_len + s_len + 2);
668
 
  if (tmp == NULL){
 
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){
669
754
    return NULL;
670
755
  }
671
 
  if(f_len > 0){
672
 
    memcpy(tmp, first, f_len);  /* Spurious warning */
673
 
  }
674
 
  tmp[f_len] = '/';
675
 
  if(s_len > 0){
676
 
    memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
677
 
  }
678
 
  tmp[f_len + 1 + s_len] = '\0';
679
756
  return tmp;
680
757
}
681
758
 
688
765
    const char *interface = "eth0";
689
766
    struct ifreq network;
690
767
    int sd;
 
768
    uid_t uid;
 
769
    gid_t gid;
691
770
    char *connect_to = NULL;
692
771
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
772
    char *pubkeyfilename = NULL;
 
773
    char *seckeyfilename = NULL;
 
774
    const char *pubkeyname = "pubkey.txt";
 
775
    const char *seckeyname = "seckey.txt";
693
776
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
694
777
                          .dh_bits = 1024, .priority = "SECURE256"};
 
778
    bool gnutls_initalized = false;
695
779
    
696
780
    {
697
 
      /* Temporary int to get the address of for getopt_long */
698
 
      int debug_int = debug ? 1 : 0;
699
 
      while (true){
700
 
        struct option long_options[] = {
701
 
          {"debug", no_argument, &debug_int, 1},
702
 
          {"connect", required_argument, NULL, 'c'},
703
 
          {"interface", required_argument, NULL, 'i'},
704
 
          {"keydir", required_argument, NULL, 'd'},
705
 
          {"seckey", required_argument, NULL, 's'},
706
 
          {"pubkey", required_argument, NULL, 'p'},
707
 
          {"dh-bits", required_argument, NULL, 'D'},
708
 
          {"priority", required_argument, NULL, 'P'},
709
 
          {0, 0, 0, 0} };
710
 
      
711
 
        int option_index = 0;
712
 
        ret = getopt_long (argc, argv, "i:", long_options,
713
 
                           &option_index);
714
 
      
715
 
        if (ret == -1){
 
781
      struct argp_option options[] = {
 
782
        { .name = "debug", .key = 128,
 
783
          .doc = "Debug mode", .group = 3 },
 
784
        { .name = "connect", .key = 'c',
 
785
          .arg = "IP",
 
786
          .doc = "Connect directly to a sepcified mandos server",
 
787
          .group = 1 },
 
788
        { .name = "interface", .key = 'i',
 
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",
 
795
          .group = 1 },
 
796
        { .name = "seckey", .key = 's',
 
797
          .arg = "SECKEY",
 
798
          .doc = "Secret openpgp key for gnutls authentication",
 
799
          .group = 1 },
 
800
        { .name = "pubkey", .key = 'p',
 
801
          .arg = "PUBKEY",
 
802
          .doc = "Public openpgp key for gnutls authentication",
 
803
          .group = 2 },
 
804
        { .name = "dh-bits", .key = 129,
 
805
          .arg = "BITS",
 
806
          .doc = "dh-bits to use in gnutls communication",
 
807
          .group = 2 },
 
808
        { .name = "priority", .key = 130,
 
809
          .arg = "PRIORITY",
 
810
          .doc = "GNUTLS priority", .group = 1 },
 
811
        { .name = NULL }
 
812
      };
 
813
 
 
814
      
 
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:
 
821
          debug = true;
716
822
          break;
717
 
        }
718
 
      
719
 
        switch(ret){
720
 
        case 0:
 
823
        case 'c':
 
824
          connect_to = arg;
721
825
          break;
722
826
        case 'i':
723
 
          interface = optarg;
724
 
          break;
725
 
        case 'c':
726
 
          connect_to = optarg;
 
827
          interface = arg;
727
828
          break;
728
829
        case 'd':
729
 
          keydir = optarg;
 
830
          keydir = arg;
 
831
          break;
 
832
        case 's':
 
833
          seckeyname = arg;
730
834
          break;
731
835
        case 'p':
732
 
          pubkeyfile = optarg;
733
 
          break;
734
 
        case 's':
735
 
          seckeyfile = optarg;
736
 
          break;
737
 
        case 'D':
 
836
          pubkeyname = arg;
 
837
          break;
 
838
        case 129:
738
839
          errno = 0;
739
 
          mc.dh_bits = (unsigned int) strtol(optarg, NULL, 10);
 
840
          mc.dh_bits = (unsigned int) strtol(arg, NULL, 10);
740
841
          if (errno){
741
842
            perror("strtol");
742
843
            exit(EXIT_FAILURE);
743
844
          }
744
845
          break;
745
 
        case 'P':
746
 
          mc.priority = optarg;
747
 
          break;
748
 
        case '?':
 
846
        case 130:
 
847
          mc.priority = arg;
 
848
          break;
 
849
        case ARGP_KEY_ARG:
 
850
          argp_usage (state);
 
851
        case ARGP_KEY_END:
 
852
          break;
749
853
        default:
750
 
          /* getopt_long() has already printed a message about the
751
 
             unrcognized option, so just exit. */
752
 
          exit(EXIT_FAILURE);
753
 
        }
754
 
      }
755
 
      /* Set the global debug flag from the temporary int */
756
 
      debug = debug_int ? true : false;
757
 
    }
758
 
    
759
 
    pubkeyfile = combinepath(keydir, pubkeyfile);
760
 
    if (pubkeyfile == NULL){
761
 
      perror("combinepath");
762
 
      exitcode = EXIT_FAILURE;
763
 
      goto end;
764
 
    }
765
 
    
766
 
    seckeyfile = combinepath(keydir, seckeyfile);
767
 
    if (seckeyfile == NULL){
768
 
      perror("combinepath");
769
 
      goto end;
 
854
          return ARGP_ERR_UNKNOWN;
 
855
        }
 
856
        return 0;
 
857
      }
 
858
 
 
859
      struct argp argp = { .options = options, .parser = parse_opt,
 
860
                           .args_doc = "",
 
861
                           .doc = "Mandos client -- Get and decrypt"
 
862
                           " passwords from mandos server" };
 
863
      ret = argp_parse (&argp, argc, argv, 0, 0, NULL);
 
864
      if (ret == ARGP_ERR_UNKNOWN){
 
865
        fprintf(stderr, "Unknown error while parsing arguments\n");
 
866
        exitcode = EXIT_FAILURE;
 
867
        goto end;
 
868
      }
 
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
    }
 
893
    
 
894
    /* If the interface is down, bring it up */
 
895
    {
 
896
      sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
897
      if(sd < 0) {
 
898
        perror("socket");
 
899
        exitcode = EXIT_FAILURE;
 
900
        goto end;
 
901
      }
 
902
      strcpy(network.ifr_name, interface);
 
903
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
904
      if(ret == -1){
 
905
        perror("ioctl SIOCGIFFLAGS");
 
906
        exitcode = EXIT_FAILURE;
 
907
        goto end;
 
908
      }
 
909
      if((network.ifr_flags & IFF_UP) == 0){
 
910
        network.ifr_flags |= IFF_UP;
 
911
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
912
        if(ret == -1){
 
913
          perror("ioctl SIOCSIFFLAGS");
 
914
          exitcode = EXIT_FAILURE;
 
915
          goto end;
 
916
        }
 
917
      }
 
918
      close(sd);
 
919
    }
 
920
    
 
921
    uid = getuid();
 
922
    gid = getgid();
 
923
    
 
924
    ret = setuid(uid);
 
925
    if (ret == -1){
 
926
      perror("setuid");
 
927
    }
 
928
    
 
929
    setgid(gid);
 
930
    if (ret == -1){
 
931
      perror("setgid");
770
932
    }
771
933
    
772
934
    if_index = (AvahiIfIndex) if_nametoindex(interface);
781
943
      char *address = strrchr(connect_to, ':');
782
944
      if(address == NULL){
783
945
        fprintf(stderr, "No colon in address\n");
784
 
        exit(EXIT_FAILURE);
 
946
        exitcode = EXIT_FAILURE;
 
947
        goto end;
785
948
      }
786
949
      errno = 0;
787
950
      uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
788
951
      if(errno){
789
952
        perror("Bad port number");
790
 
        exit(EXIT_FAILURE);
 
953
        exitcode = EXIT_FAILURE;
 
954
        goto end;
791
955
      }
792
956
      *address = '\0';
793
957
      address = connect_to;
794
958
      ret = start_mandos_communication(address, port, if_index, &mc);
795
959
      if(ret < 0){
796
 
        exit(EXIT_FAILURE);
 
960
        exitcode = EXIT_FAILURE;
797
961
      } else {
798
 
        exit(EXIT_SUCCESS);
799
 
      }
800
 
    }
801
 
    
802
 
    /* If the interface is down, bring it up */
803
 
    {
804
 
      sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
805
 
      if(sd < 0) {
806
 
        perror("socket");
807
 
        exitcode = EXIT_FAILURE;
808
 
        goto end;
809
 
      }
810
 
      strcpy(network.ifr_name, interface); /* Spurious warning */
811
 
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
812
 
      if(ret == -1){
813
 
        perror("ioctl SIOCGIFFLAGS");
814
 
        exitcode = EXIT_FAILURE;
815
 
        goto end;
816
 
      }
817
 
      if((network.ifr_flags & IFF_UP) == 0){
818
 
        network.ifr_flags |= IFF_UP;
819
 
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
820
 
        if(ret == -1){
821
 
          perror("ioctl SIOCSIFFLAGS");
822
 
          exitcode = EXIT_FAILURE;
823
 
          goto end;
824
 
        }
825
 
      }
826
 
      close(sd);
 
962
        exitcode = EXIT_SUCCESS;
 
963
      }
 
964
      goto end;
827
965
    }
828
966
    
829
967
    if (not debug){
903
1041
 
904
1042
    if (mc.simple_poll != NULL)
905
1043
        avahi_simple_poll_free(mc.simple_poll);
906
 
    free(pubkeyfile);
907
 
    free(seckeyfile);
 
1044
    free(pubkeyfilename);
 
1045
    free(seckeyfilename);
 
1046
 
 
1047
    if (gnutls_initalized){
 
1048
      gnutls_certificate_free_credentials(mc.cred);
 
1049
      gnutls_global_deinit ();
 
1050
    }
908
1051
    
909
1052
    return exitcode;
910
1053
}