/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: 2011-12-31 20:07:11 UTC
  • mfrom: (535.1.9 wireless-network-hook)
  • Revision ID: teddy@recompile.se-20111231200711-6dli3r8drftem57r
Merge new wireless network hook.  Fix bridge network hook to use
hardware addresses instead of interface names.  Implement and document
new "CONNECT" environment variable for network hooks.

Show diffs side-by-side

added added

removed removed

Lines of Context:
73
73
                                */
74
74
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
75
75
                                   getuid(), getgid(), seteuid(),
76
 
                                   setgid(), pause() */
 
76
                                   setgid(), pause(), _exit() */
77
77
#include <arpa/inet.h>          /* inet_pton(), htons, inet_ntop() */
78
78
#include <iso646.h>             /* not, or, and */
79
79
#include <argp.h>               /* struct argp_option, error_t, struct
87
87
                                   EX_NOHOST, EX_IOERR, EX_PROTOCOL */
88
88
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
89
89
                                   WEXITSTATUS(), WTERMSIG() */
 
90
#include <grp.h>                /* setgroups() */
90
91
 
91
92
#ifdef __linux__
92
93
#include <sys/klog.h>           /* klogctl() */
110
111
                                   init_gnutls_session(),
111
112
                                   GNUTLS_* */
112
113
#include <gnutls/openpgp.h>
113
 
                          /* gnutls_certificate_set_openpgp_key_file(),
114
 
                                   GNUTLS_OPENPGP_FMT_BASE64 */
 
114
                         /* gnutls_certificate_set_openpgp_key_file(),
 
115
                            GNUTLS_OPENPGP_FMT_BASE64 */
115
116
 
116
117
/* GPGME */
117
118
#include <gpgme.h>              /* All GPGME types, constants and
133
134
const char *argp_program_bug_address = "<mandos@recompile.se>";
134
135
static const char sys_class_net[] = "/sys/class/net";
135
136
char *connect_to = NULL;
 
137
const char *hookdir = HOOKDIR;
136
138
 
137
139
/* Doubly linked list that need to be circularly linked when used */
138
140
typedef struct server{
168
170
 
169
171
/* Function to use when printing errors */
170
172
void perror_plus(const char *print_text){
 
173
  int e = errno;
171
174
  fprintf(stderr, "Mandos plugin %s: ",
172
175
          program_invocation_short_name);
 
176
  errno = e;
173
177
  perror(print_text);
174
178
}
175
179
 
 
180
__attribute__((format (gnu_printf, 2, 3)))
 
181
int fprintf_plus(FILE *stream, const char *format, ...){
 
182
  va_list ap;
 
183
  va_start (ap, format);
 
184
  
 
185
  TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
 
186
                             program_invocation_short_name));
 
187
  return TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
 
188
}
 
189
 
176
190
/*
177
191
 * Make additional room in "buffer" for at least BUFFER_SIZE more
178
192
 * bytes. "buffer_capacity" is how much is currently allocated,
179
193
 * "buffer_length" is how much is already used.
180
194
 */
181
195
size_t incbuffer(char **buffer, size_t buffer_length,
182
 
                  size_t buffer_capacity){
 
196
                 size_t buffer_capacity){
183
197
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
184
198
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
185
199
    if(buffer == NULL){
191
205
}
192
206
 
193
207
/* Add server to set of servers to retry periodically */
194
 
int add_server(const char *ip, uint16_t port,
195
 
                 AvahiIfIndex if_index,
196
 
                 int af){
 
208
bool add_server(const char *ip, uint16_t port, AvahiIfIndex if_index,
 
209
                int af){
197
210
  int ret;
198
211
  server *new_server = malloc(sizeof(server));
199
212
  if(new_server == NULL){
200
213
    perror_plus("malloc");
201
 
    return -1;
 
214
    return false;
202
215
  }
203
216
  *new_server = (server){ .ip = strdup(ip),
204
 
                         .port = port,
205
 
                         .if_index = if_index,
206
 
                         .af = af };
 
217
                          .port = port,
 
218
                          .if_index = if_index,
 
219
                          .af = af };
207
220
  if(new_server->ip == NULL){
208
221
    perror_plus("strdup");
209
 
    return -1;
 
222
    return false;
210
223
  }
211
224
  /* Special case of first server */
212
225
  if (mc.current_server == NULL){
223
236
  ret = clock_gettime(CLOCK_MONOTONIC, &mc.current_server->last_seen);
224
237
  if(ret == -1){
225
238
    perror_plus("clock_gettime");
226
 
    return -1;
 
239
    return false;
227
240
  }
228
 
  return 0;
 
241
  return true;
229
242
}
230
243
 
231
244
/* 
232
245
 * Initialize GPGME.
233
246
 */
234
 
static bool init_gpgme(const char *seckey,
235
 
                       const char *pubkey, const char *tempdir){
 
247
static bool init_gpgme(const char *seckey, const char *pubkey,
 
248
                       const char *tempdir){
236
249
  gpgme_error_t rc;
237
250
  gpgme_engine_info_t engine_info;
238
251
  
253
266
    
254
267
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
255
268
    if(rc != GPG_ERR_NO_ERROR){
256
 
      fprintf(stderr, "Mandos plugin mandos-client: "
257
 
              "bad gpgme_data_new_from_fd: %s: %s\n",
258
 
              gpgme_strsource(rc), gpgme_strerror(rc));
 
269
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
 
270
                   gpgme_strsource(rc), gpgme_strerror(rc));
259
271
      return false;
260
272
    }
261
273
    
262
274
    rc = gpgme_op_import(mc.ctx, pgp_data);
263
275
    if(rc != GPG_ERR_NO_ERROR){
264
 
      fprintf(stderr, "Mandos plugin mandos-client: "
265
 
              "bad gpgme_op_import: %s: %s\n",
266
 
              gpgme_strsource(rc), gpgme_strerror(rc));
 
276
      fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
 
277
                   gpgme_strsource(rc), gpgme_strerror(rc));
267
278
      return false;
268
279
    }
269
280
    
276
287
  }
277
288
  
278
289
  if(debug){
279
 
    fprintf(stderr, "Mandos plugin mandos-client: "
280
 
            "Initializing GPGME\n");
 
290
    fprintf_plus(stderr, "Initializing GPGME\n");
281
291
  }
282
292
  
283
293
  /* Init GPGME */
284
294
  gpgme_check_version(NULL);
285
295
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
286
296
  if(rc != GPG_ERR_NO_ERROR){
287
 
    fprintf(stderr, "Mandos plugin mandos-client: "
288
 
            "bad gpgme_engine_check_version: %s: %s\n",
289
 
            gpgme_strsource(rc), gpgme_strerror(rc));
 
297
    fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
 
298
                 gpgme_strsource(rc), gpgme_strerror(rc));
290
299
    return false;
291
300
  }
292
301
  
293
302
  /* Set GPGME home directory for the OpenPGP engine only */
294
303
  rc = gpgme_get_engine_info(&engine_info);
295
304
  if(rc != GPG_ERR_NO_ERROR){
296
 
    fprintf(stderr, "Mandos plugin mandos-client: "
297
 
            "bad gpgme_get_engine_info: %s: %s\n",
298
 
            gpgme_strsource(rc), gpgme_strerror(rc));
 
305
    fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
 
306
                 gpgme_strsource(rc), gpgme_strerror(rc));
299
307
    return false;
300
308
  }
301
309
  while(engine_info != NULL){
307
315
    engine_info = engine_info->next;
308
316
  }
309
317
  if(engine_info == NULL){
310
 
    fprintf(stderr, "Mandos plugin mandos-client: "
311
 
            "Could not set GPGME home dir to %s\n", tempdir);
 
318
    fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
 
319
                 tempdir);
312
320
    return false;
313
321
  }
314
322
  
315
323
  /* Create new GPGME "context" */
316
324
  rc = gpgme_new(&(mc.ctx));
317
325
  if(rc != GPG_ERR_NO_ERROR){
318
 
    fprintf(stderr, "Mandos plugin mandos-client: "
319
 
            "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
320
 
            gpgme_strerror(rc));
 
326
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
 
327
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
 
328
                 gpgme_strerror(rc));
321
329
    return false;
322
330
  }
323
331
  
342
350
  ssize_t plaintext_length = 0;
343
351
  
344
352
  if(debug){
345
 
    fprintf(stderr, "Mandos plugin mandos-client: "
346
 
            "Trying to decrypt OpenPGP data\n");
 
353
    fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
347
354
  }
348
355
  
349
356
  /* Create new GPGME data buffer from memory cryptotext */
350
357
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
351
358
                               0);
352
359
  if(rc != GPG_ERR_NO_ERROR){
353
 
    fprintf(stderr, "Mandos plugin mandos-client: "
354
 
            "bad gpgme_data_new_from_mem: %s: %s\n",
355
 
            gpgme_strsource(rc), gpgme_strerror(rc));
 
360
    fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
 
361
                 gpgme_strsource(rc), gpgme_strerror(rc));
356
362
    return -1;
357
363
  }
358
364
  
359
365
  /* Create new empty GPGME data buffer for the plaintext */
360
366
  rc = gpgme_data_new(&dh_plain);
361
367
  if(rc != GPG_ERR_NO_ERROR){
362
 
    fprintf(stderr, "Mandos plugin mandos-client: "
363
 
            "bad gpgme_data_new: %s: %s\n",
364
 
            gpgme_strsource(rc), gpgme_strerror(rc));
 
368
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
 
369
                 "bad gpgme_data_new: %s: %s\n",
 
370
                 gpgme_strsource(rc), gpgme_strerror(rc));
365
371
    gpgme_data_release(dh_crypto);
366
372
    return -1;
367
373
  }
370
376
     data buffer */
371
377
  rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
372
378
  if(rc != GPG_ERR_NO_ERROR){
373
 
    fprintf(stderr, "Mandos plugin mandos-client: "
374
 
            "bad gpgme_op_decrypt: %s: %s\n",
375
 
            gpgme_strsource(rc), gpgme_strerror(rc));
 
379
    fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
 
380
                 gpgme_strsource(rc), gpgme_strerror(rc));
376
381
    plaintext_length = -1;
377
382
    if(debug){
378
383
      gpgme_decrypt_result_t result;
379
384
      result = gpgme_op_decrypt_result(mc.ctx);
380
385
      if(result == NULL){
381
 
        fprintf(stderr, "Mandos plugin mandos-client: "
382
 
                "gpgme_op_decrypt_result failed\n");
 
386
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
383
387
      } else {
384
 
        fprintf(stderr, "Mandos plugin mandos-client: "
385
 
                "Unsupported algorithm: %s\n",
386
 
                result->unsupported_algorithm);
387
 
        fprintf(stderr, "Mandos plugin mandos-client: "
388
 
                "Wrong key usage: %u\n",
389
 
                result->wrong_key_usage);
 
388
        fprintf_plus(stderr, "Unsupported algorithm: %s\n",
 
389
                     result->unsupported_algorithm);
 
390
        fprintf_plus(stderr, "Wrong key usage: %u\n",
 
391
                     result->wrong_key_usage);
390
392
        if(result->file_name != NULL){
391
 
          fprintf(stderr, "Mandos plugin mandos-client: "
392
 
                  "File name: %s\n", result->file_name);
 
393
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
393
394
        }
394
395
        gpgme_recipient_t recipient;
395
396
        recipient = result->recipients;
396
397
        while(recipient != NULL){
397
 
          fprintf(stderr, "Mandos plugin mandos-client: "
398
 
                  "Public key algorithm: %s\n",
399
 
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
400
 
          fprintf(stderr, "Mandos plugin mandos-client: "
401
 
                  "Key ID: %s\n", recipient->keyid);
402
 
          fprintf(stderr, "Mandos plugin mandos-client: "
403
 
                  "Secret key available: %s\n",
404
 
                  recipient->status == GPG_ERR_NO_SECKEY
405
 
                  ? "No" : "Yes");
 
398
          fprintf_plus(stderr, "Public key algorithm: %s\n",
 
399
                       gpgme_pubkey_algo_name
 
400
                       (recipient->pubkey_algo));
 
401
          fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
 
402
          fprintf_plus(stderr, "Secret key available: %s\n",
 
403
                       recipient->status == GPG_ERR_NO_SECKEY
 
404
                       ? "No" : "Yes");
406
405
          recipient = recipient->next;
407
406
        }
408
407
      }
411
410
  }
412
411
  
413
412
  if(debug){
414
 
    fprintf(stderr, "Mandos plugin mandos-client: "
415
 
            "Decryption of OpenPGP data succeeded\n");
 
413
    fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
416
414
  }
417
415
  
418
416
  /* Seek back to the beginning of the GPGME plaintext data buffer */
425
423
  *plaintext = NULL;
426
424
  while(true){
427
425
    plaintext_capacity = incbuffer(plaintext,
428
 
                                      (size_t)plaintext_length,
429
 
                                      plaintext_capacity);
 
426
                                   (size_t)plaintext_length,
 
427
                                   plaintext_capacity);
430
428
    if(plaintext_capacity == 0){
431
 
        perror_plus("incbuffer");
432
 
        plaintext_length = -1;
433
 
        goto decrypt_end;
 
429
      perror_plus("incbuffer");
 
430
      plaintext_length = -1;
 
431
      goto decrypt_end;
434
432
    }
435
433
    
436
434
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
449
447
  }
450
448
  
451
449
  if(debug){
452
 
    fprintf(stderr, "Mandos plugin mandos-client: "
453
 
            "Decrypted password is: ");
 
450
    fprintf_plus(stderr, "Decrypted password is: ");
454
451
    for(ssize_t i = 0; i < plaintext_length; i++){
455
452
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
456
453
    }
478
475
/* GnuTLS log function callback */
479
476
static void debuggnutls(__attribute__((unused)) int level,
480
477
                        const char* string){
481
 
  fprintf(stderr, "Mandos plugin mandos-client: GnuTLS: %s", string);
 
478
  fprintf_plus(stderr, "GnuTLS: %s", string);
482
479
}
483
480
 
484
481
static int init_gnutls_global(const char *pubkeyfilename,
486
483
  int ret;
487
484
  
488
485
  if(debug){
489
 
    fprintf(stderr, "Mandos plugin mandos-client: "
490
 
            "Initializing GnuTLS\n");
 
486
    fprintf_plus(stderr, "Initializing GnuTLS\n");
491
487
  }
492
488
  
493
489
  ret = gnutls_global_init();
494
490
  if(ret != GNUTLS_E_SUCCESS){
495
 
    fprintf(stderr, "Mandos plugin mandos-client: "
496
 
            "GnuTLS global_init: %s\n", safer_gnutls_strerror(ret));
 
491
    fprintf_plus(stderr, "GnuTLS global_init: %s\n",
 
492
                 safer_gnutls_strerror(ret));
497
493
    return -1;
498
494
  }
499
495
  
508
504
  /* OpenPGP credentials */
509
505
  ret = gnutls_certificate_allocate_credentials(&mc.cred);
510
506
  if(ret != GNUTLS_E_SUCCESS){
511
 
    fprintf(stderr, "Mandos plugin mandos-client: "
512
 
            "GnuTLS memory error: %s\n", safer_gnutls_strerror(ret));
 
507
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
 
508
                 safer_gnutls_strerror(ret));
513
509
    gnutls_global_deinit();
514
510
    return -1;
515
511
  }
516
512
  
517
513
  if(debug){
518
 
    fprintf(stderr, "Mandos plugin mandos-client: "
519
 
            "Attempting to use OpenPGP public key %s and"
520
 
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
521
 
            seckeyfilename);
 
514
    fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
 
515
                 " secret key %s as GnuTLS credentials\n",
 
516
                 pubkeyfilename,
 
517
                 seckeyfilename);
522
518
  }
523
519
  
524
520
  ret = gnutls_certificate_set_openpgp_key_file
525
521
    (mc.cred, pubkeyfilename, seckeyfilename,
526
522
     GNUTLS_OPENPGP_FMT_BASE64);
527
523
  if(ret != GNUTLS_E_SUCCESS){
528
 
    fprintf(stderr,
529
 
            "Mandos plugin mandos-client: "
530
 
            "Error[%d] while reading the OpenPGP key pair ('%s',"
531
 
            " '%s')\n", ret, pubkeyfilename, seckeyfilename);
532
 
    fprintf(stderr, "Mandos plugin mandos-client: "
533
 
            "The GnuTLS error is: %s\n", safer_gnutls_strerror(ret));
 
524
    fprintf_plus(stderr,
 
525
                 "Error[%d] while reading the OpenPGP key pair ('%s',"
 
526
                 " '%s')\n", ret, pubkeyfilename, seckeyfilename);
 
527
    fprintf_plus(stderr, "The GnuTLS error is: %s\n",
 
528
                 safer_gnutls_strerror(ret));
534
529
    goto globalfail;
535
530
  }
536
531
  
537
532
  /* GnuTLS server initialization */
538
533
  ret = gnutls_dh_params_init(&mc.dh_params);
539
534
  if(ret != GNUTLS_E_SUCCESS){
540
 
    fprintf(stderr, "Mandos plugin mandos-client: "
541
 
            "Error in GnuTLS DH parameter initialization:"
542
 
            " %s\n", safer_gnutls_strerror(ret));
 
535
    fprintf_plus(stderr, "Error in GnuTLS DH parameter"
 
536
                 " initialization: %s\n",
 
537
                 safer_gnutls_strerror(ret));
543
538
    goto globalfail;
544
539
  }
545
540
  ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
546
541
  if(ret != GNUTLS_E_SUCCESS){
547
 
    fprintf(stderr, "Mandos plugin mandos-client: "
548
 
            "Error in GnuTLS prime generation: %s\n",
549
 
            safer_gnutls_strerror(ret));
 
542
    fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
 
543
                 safer_gnutls_strerror(ret));
550
544
    goto globalfail;
551
545
  }
552
546
  
572
566
    }
573
567
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
574
568
  if(ret != GNUTLS_E_SUCCESS){
575
 
    fprintf(stderr, "Mandos plugin mandos-client: "
576
 
            "Error in GnuTLS session initialization: %s\n",
577
 
            safer_gnutls_strerror(ret));
 
569
    fprintf_plus(stderr,
 
570
                 "Error in GnuTLS session initialization: %s\n",
 
571
                 safer_gnutls_strerror(ret));
578
572
  }
579
573
  
580
574
  {
587
581
      }
588
582
    } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
589
583
    if(ret != GNUTLS_E_SUCCESS){
590
 
      fprintf(stderr, "Mandos plugin mandos-client: "
591
 
              "Syntax error at: %s\n", err);
592
 
      fprintf(stderr, "Mandos plugin mandos-client: "
593
 
              "GnuTLS error: %s\n", safer_gnutls_strerror(ret));
 
584
      fprintf_plus(stderr, "Syntax error at: %s\n", err);
 
585
      fprintf_plus(stderr, "GnuTLS error: %s\n",
 
586
                   safer_gnutls_strerror(ret));
594
587
      gnutls_deinit(*session);
595
588
      return -1;
596
589
    }
605
598
    }
606
599
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
607
600
  if(ret != GNUTLS_E_SUCCESS){
608
 
    fprintf(stderr, "Mandos plugin mandos-client: "
609
 
            "Error setting GnuTLS credentials: %s\n",
610
 
            safer_gnutls_strerror(ret));
 
601
    fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
 
602
                 safer_gnutls_strerror(ret));
611
603
    gnutls_deinit(*session);
612
604
    return -1;
613
605
  }
658
650
    pf = PF_INET;
659
651
    break;
660
652
  default:
661
 
    fprintf(stderr, "Mandos plugin mandos-client: "
662
 
            "Bad address family: %d\n", af);
 
653
    fprintf_plus(stderr, "Bad address family: %d\n", af);
663
654
    errno = EINVAL;
664
655
    return -1;
665
656
  }
670
661
  }
671
662
  
672
663
  if(debug){
673
 
    fprintf(stderr, "Mandos plugin mandos-client: "
674
 
            "Setting up a TCP connection to %s, port %" PRIu16
675
 
            "\n", ip, port);
 
664
    fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
 
665
                 PRIu16 "\n", ip, port);
676
666
  }
677
667
  
678
668
  tcp_sd = socket(pf, SOCK_STREAM, 0);
704
694
  }
705
695
  if(ret == 0){
706
696
    int e = errno;
707
 
    fprintf(stderr, "Mandos plugin mandos-client: "
708
 
            "Bad address: %s\n", ip);
 
697
    fprintf_plus(stderr, "Bad address: %s\n", ip);
709
698
    errno = e;
710
699
    goto mandos_end;
711
700
  }
716
705
    
717
706
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
718
707
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
719
 
                              -Wunreachable-code*/
 
708
                                -Wunreachable-code*/
720
709
      if(if_index == AVAHI_IF_UNSPEC){
721
 
        fprintf(stderr, "Mandos plugin mandos-client: "
722
 
                "An IPv6 link-local address is incomplete"
723
 
                " without a network interface\n");
 
710
        fprintf_plus(stderr, "An IPv6 link-local address is"
 
711
                     " incomplete without a network interface\n");
724
712
        errno = EINVAL;
725
713
        goto mandos_end;
726
714
      }
744
732
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
745
733
        perror_plus("if_indextoname");
746
734
      } else {
747
 
        fprintf(stderr, "Mandos plugin mandos-client: "
748
 
                "Connection to: %s%%%s, port %" PRIu16 "\n",
749
 
                ip, interface, port);
 
735
        fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIu16
 
736
                     "\n", ip, interface, port);
750
737
      }
751
738
    } else {
752
 
      fprintf(stderr, "Mandos plugin mandos-client: "
753
 
              "Connection to: %s, port %" PRIu16 "\n", ip, port);
 
739
      fprintf_plus(stderr, "Connection to: %s, port %" PRIu16 "\n",
 
740
                   ip, port);
754
741
    }
755
742
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
756
743
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
766
753
      perror_plus("inet_ntop");
767
754
    } else {
768
755
      if(strcmp(addrstr, ip) != 0){
769
 
        fprintf(stderr, "Mandos plugin mandos-client: "
770
 
                "Canonical address form: %s\n", addrstr);
 
756
        fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
771
757
      }
772
758
    }
773
759
  }
801
787
  while(true){
802
788
    size_t out_size = strlen(out);
803
789
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
804
 
                                   out_size - written));
 
790
                                        out_size - written));
805
791
    if(ret == -1){
806
792
      int e = errno;
807
793
      perror_plus("write");
827
813
  }
828
814
  
829
815
  if(debug){
830
 
    fprintf(stderr, "Mandos plugin mandos-client: "
831
 
            "Establishing TLS session with %s\n", ip);
 
816
    fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
832
817
  }
833
818
  
834
819
  if(quit_now){
854
839
  
855
840
  if(ret != GNUTLS_E_SUCCESS){
856
841
    if(debug){
857
 
      fprintf(stderr, "Mandos plugin mandos-client: "
858
 
              "*** GnuTLS Handshake failed ***\n");
 
842
      fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
859
843
      gnutls_perror(ret);
860
844
    }
861
845
    errno = EPROTO;
865
849
  /* Read OpenPGP packet that contains the wanted password */
866
850
  
867
851
  if(debug){
868
 
    fprintf(stderr, "Mandos plugin mandos-client: "
869
 
            "Retrieving OpenPGP encrypted password from %s\n", ip);
 
852
    fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
 
853
                 " %s\n", ip);
870
854
  }
871
855
  
872
856
  while(true){
877
861
    }
878
862
    
879
863
    buffer_capacity = incbuffer(&buffer, buffer_length,
880
 
                                   buffer_capacity);
 
864
                                buffer_capacity);
881
865
    if(buffer_capacity == 0){
882
866
      int e = errno;
883
867
      perror_plus("incbuffer");
910
894
          }
911
895
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
912
896
        if(ret < 0){
913
 
          fprintf(stderr, "Mandos plugin mandos-client: "
914
 
                  "*** GnuTLS Re-handshake failed ***\n");
 
897
          fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
 
898
                       "***\n");
915
899
          gnutls_perror(ret);
916
900
          errno = EPROTO;
917
901
          goto mandos_end;
918
902
        }
919
903
        break;
920
904
      default:
921
 
        fprintf(stderr, "Mandos plugin mandos-client: "
922
 
                "Unknown error while reading data from"
923
 
                " encrypted session with Mandos server\n");
 
905
        fprintf_plus(stderr, "Unknown error while reading data from"
 
906
                     " encrypted session with Mandos server\n");
924
907
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
925
908
        errno = EIO;
926
909
        goto mandos_end;
931
914
  }
932
915
  
933
916
  if(debug){
934
 
    fprintf(stderr, "Mandos plugin mandos-client: "
935
 
            "Closing TLS session\n");
 
917
    fprintf_plus(stderr, "Closing TLS session\n");
936
918
  }
937
919
  
938
920
  if(quit_now){
950
932
  
951
933
  if(buffer_length > 0){
952
934
    ssize_t decrypted_buffer_size;
953
 
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
954
 
                                               buffer_length,
 
935
    decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
955
936
                                               &decrypted_buffer);
956
937
    if(decrypted_buffer_size >= 0){
957
938
      
968
949
        if(ret == 0 and ferror(stdout)){
969
950
          int e = errno;
970
951
          if(debug){
971
 
            fprintf(stderr, "Mandos plugin mandos-client: "
972
 
                    "Error writing encrypted data: %s\n",
973
 
                    strerror(errno));
 
952
            fprintf_plus(stderr, "Error writing encrypted data: %s\n",
 
953
                         strerror(errno));
974
954
          }
975
955
          errno = e;
976
956
          goto mandos_end;
1033
1013
  switch(event){
1034
1014
  default:
1035
1015
  case AVAHI_RESOLVER_FAILURE:
1036
 
    fprintf(stderr, "Mandos plugin mandos-client: "
1037
 
            "(Avahi Resolver) Failed to resolve service '%s'"
1038
 
            " of type '%s' in domain '%s': %s\n", name, type, domain,
1039
 
            avahi_strerror(avahi_server_errno(mc.server)));
 
1016
    fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
 
1017
                 "'%s' of type '%s' in domain '%s': %s\n", name, type,
 
1018
                 domain,
 
1019
                 avahi_strerror(avahi_server_errno(mc.server)));
1040
1020
    break;
1041
1021
    
1042
1022
  case AVAHI_RESOLVER_FOUND:
1044
1024
      char ip[AVAHI_ADDRESS_STR_MAX];
1045
1025
      avahi_address_snprint(ip, sizeof(ip), address);
1046
1026
      if(debug){
1047
 
        fprintf(stderr, "Mandos plugin mandos-client: "
1048
 
                "Mandos server \"%s\" found on %s (%s, %"
1049
 
                PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
1050
 
                ip, (intmax_t)interface, port);
 
1027
        fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
1028
                     PRIdMAX ") on port %" PRIu16 "\n", name,
 
1029
                     host_name, ip, (intmax_t)interface, port);
1051
1030
      }
1052
1031
      int ret = start_mandos_communication(ip, port, interface,
1053
1032
                                           avahi_proto_to_af(proto));
1054
1033
      if(ret == 0){
1055
1034
        avahi_simple_poll_quit(mc.simple_poll);
1056
1035
      } else {
1057
 
        ret = add_server(ip, port, interface,
1058
 
                         avahi_proto_to_af(proto));
 
1036
        if(not add_server(ip, port, interface,
 
1037
                          avahi_proto_to_af(proto))){
 
1038
          fprintf_plus(stderr, "Failed to add server \"%s\" to server"
 
1039
                       " list\n", name);
 
1040
        }
1059
1041
      }
1060
1042
    }
1061
1043
  }
1085
1067
  default:
1086
1068
  case AVAHI_BROWSER_FAILURE:
1087
1069
    
1088
 
    fprintf(stderr, "Mandos plugin mandos-client: "
1089
 
            "(Avahi browser) %s\n",
1090
 
            avahi_strerror(avahi_server_errno(mc.server)));
 
1070
    fprintf_plus(stderr, "(Avahi browser) %s\n",
 
1071
                 avahi_strerror(avahi_server_errno(mc.server)));
1091
1072
    avahi_simple_poll_quit(mc.simple_poll);
1092
1073
    return;
1093
1074
    
1100
1081
    if(avahi_s_service_resolver_new(mc.server, interface, protocol,
1101
1082
                                    name, type, domain, protocol, 0,
1102
1083
                                    resolve_callback, NULL) == NULL)
1103
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1104
 
              "Avahi: Failed to resolve service '%s': %s\n",
1105
 
              name, avahi_strerror(avahi_server_errno(mc.server)));
 
1084
      fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
 
1085
                   " %s\n", name,
 
1086
                   avahi_strerror(avahi_server_errno(mc.server)));
1106
1087
    break;
1107
1088
    
1108
1089
  case AVAHI_BROWSER_REMOVE:
1111
1092
  case AVAHI_BROWSER_ALL_FOR_NOW:
1112
1093
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
1113
1094
    if(debug){
1114
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1115
 
              "No Mandos server found, still searching...\n");
 
1095
      fprintf_plus(stderr, "No Mandos server found, still"
 
1096
                   " searching...\n");
1116
1097
    }
1117
1098
    break;
1118
1099
  }
1157
1138
  /* Reject the loopback device */
1158
1139
  if(ifr->ifr_flags & IFF_LOOPBACK){
1159
1140
    if(debug){
1160
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1161
 
              "Rejecting loopback interface \"%s\"\n", ifname);
 
1141
      fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
 
1142
                   ifname);
1162
1143
    }
1163
1144
    return false;
1164
1145
  }
1165
1146
  /* Accept point-to-point devices only if connect_to is specified */
1166
1147
  if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1167
1148
    if(debug){
1168
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1169
 
              "Accepting point-to-point interface \"%s\"\n", ifname);
 
1149
      fprintf_plus(stderr, "Accepting point-to-point interface"
 
1150
                   " \"%s\"\n", ifname);
1170
1151
    }
1171
1152
    return true;
1172
1153
  }
1173
1154
  /* Otherwise, reject non-broadcast-capable devices */
1174
1155
  if(not (ifr->ifr_flags & IFF_BROADCAST)){
1175
1156
    if(debug){
1176
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1177
 
              "Rejecting non-broadcast interface \"%s\"\n", ifname);
 
1157
      fprintf_plus(stderr, "Rejecting non-broadcast interface"
 
1158
                   " \"%s\"\n", ifname);
1178
1159
    }
1179
1160
    return false;
1180
1161
  }
1181
1162
  /* Reject non-ARP interfaces (including dummy interfaces) */
1182
1163
  if(ifr->ifr_flags & IFF_NOARP){
1183
1164
    if(debug){
1184
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1185
 
              "Rejecting non-ARP interface \"%s\"\n", ifname);
 
1165
      fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
 
1166
                   ifname);
1186
1167
    }
1187
1168
    return false;
1188
1169
  }
1189
1170
  
1190
1171
  /* Accept this device */
1191
1172
  if(debug){
1192
 
    fprintf(stderr, "Mandos plugin mandos-client: "
1193
 
            "Interface \"%s\" is good\n", ifname);
 
1173
    fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1194
1174
  }
1195
1175
  return true;
1196
1176
}
1208
1188
  struct ifreq ifr;
1209
1189
  if(not get_flags(if_entry->d_name, &ifr)){
1210
1190
    if(debug){
1211
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1212
 
              "Failed to get flags for interface \"%s\"\n",
1213
 
              if_entry->d_name);
 
1191
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1192
                   "\"%s\"\n", if_entry->d_name);
1214
1193
    }
1215
1194
    return 0;
1216
1195
  }
1234
1213
  struct ifreq ifr;
1235
1214
  if(not get_flags(if_entry->d_name, &ifr)){
1236
1215
    if(debug){
1237
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1238
 
              "Failed to get flags for interface \"%s\"\n",
1239
 
              if_entry->d_name);
 
1216
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1217
                   "\"%s\"\n", if_entry->d_name);
1240
1218
    }
1241
1219
    return 0;
1242
1220
  }
1244
1222
  /* Reject down interfaces */
1245
1223
  if(not (ifr.ifr_flags & IFF_UP)){
1246
1224
    if(debug){
1247
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1248
 
              "Rejecting down interface \"%s\"\n",
1249
 
              if_entry->d_name);
 
1225
      fprintf_plus(stderr, "Rejecting down interface \"%s\"\n",
 
1226
                   if_entry->d_name);
1250
1227
    }
1251
1228
    return 0;
1252
1229
  }
1254
1231
  /* Reject non-running interfaces */
1255
1232
  if(not (ifr.ifr_flags & IFF_RUNNING)){
1256
1233
    if(debug){
1257
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1258
 
              "Rejecting non-running interface \"%s\"\n",
1259
 
              if_entry->d_name);
 
1234
      fprintf_plus(stderr, "Rejecting non-running interface \"%s\"\n",
 
1235
                   if_entry->d_name);
1260
1236
    }
1261
1237
    return 0;
1262
1238
  }
1281
1257
/* Is this directory entry a runnable program? */
1282
1258
int runnable_hook(const struct dirent *direntry){
1283
1259
  int ret;
 
1260
  size_t sret;
1284
1261
  struct stat st;
1285
1262
  
1286
1263
  if((direntry->d_name)[0] == '\0'){
1288
1265
    return 0;
1289
1266
  }
1290
1267
  
1291
 
  /* Save pointer to last character */
1292
 
  char *end = strchr(direntry->d_name, '\0')-1;
1293
 
  
1294
 
  if(*end == '~'){
1295
 
    /* Backup name~ */
1296
 
    return 0;
1297
 
  }
1298
 
  
1299
 
  if(((direntry->d_name)[0] == '#')
1300
 
     and (*end == '#')){
1301
 
    /* Temporary #name# */
1302
 
    return 0;
1303
 
  }
1304
 
  
1305
 
  /* XXX more rules here */
1306
 
  
1307
 
  ret = stat(direntry->d_name, &st);
 
1268
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 
1269
                "abcdefghijklmnopqrstuvwxyz"
 
1270
                "0123456789"
 
1271
                "_-");
 
1272
  if((direntry->d_name)[sret] != '\0'){
 
1273
    /* Contains non-allowed characters */
 
1274
    if(debug){
 
1275
      fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
 
1276
                   direntry->d_name);
 
1277
    }
 
1278
    return 0;
 
1279
  }
 
1280
  
 
1281
  char *fullname = NULL;
 
1282
  ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
 
1283
  if(ret < 0){
 
1284
    perror_plus("asprintf");
 
1285
    return 0;
 
1286
  }
 
1287
  
 
1288
  ret = stat(fullname, &st);
1308
1289
  if(ret == -1){
1309
1290
    if(debug){
1310
 
      perror_plus("Could not stat plugin");
 
1291
      perror_plus("Could not stat hook");
1311
1292
    }
1312
1293
    return 0;
1313
1294
  }
1314
1295
  if(not (S_ISREG(st.st_mode))){
1315
1296
    /* Not a regular file */
 
1297
    if(debug){
 
1298
      fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
 
1299
                   direntry->d_name);
 
1300
    }
1316
1301
    return 0;
1317
1302
  }
1318
1303
  if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1319
1304
    /* Not executable */
 
1305
    if(debug){
 
1306
      fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
 
1307
                   direntry->d_name);
 
1308
    }
1320
1309
    return 0;
1321
1310
  }
 
1311
  if(debug){
 
1312
    fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
 
1313
                 direntry->d_name);
 
1314
  }
1322
1315
  return 1;
1323
1316
}
1324
1317
 
1331
1324
  while(true){
1332
1325
    if(mc.current_server == NULL){
1333
1326
      if (debug){
1334
 
        fprintf(stderr, "Mandos plugin mandos-client: "
1335
 
                "Wait until first server is found. No timeout!\n");
 
1327
        fprintf_plus(stderr, "Wait until first server is found."
 
1328
                     " No timeout!\n");
1336
1329
      }
1337
1330
      ret = avahi_simple_poll_iterate(s, -1);
1338
1331
    } else {
1339
1332
      if (debug){
1340
 
        fprintf(stderr, "Mandos plugin mandos-client: "
1341
 
                "Check current_server if we should run it,"
1342
 
                " or wait\n");
 
1333
        fprintf_plus(stderr, "Check current_server if we should run"
 
1334
                     " it, or wait\n");
1343
1335
      }
1344
1336
      /* the current time */
1345
1337
      ret = clock_gettime(CLOCK_MONOTONIC, &now);
1361
1353
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
1362
1354
      
1363
1355
      if (debug){
1364
 
        fprintf(stderr, "Mandos plugin mandos-client: "
1365
 
                "Blocking for %" PRIdMAX " ms\n", block_time);
 
1356
        fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
 
1357
                     block_time);
1366
1358
      }
1367
1359
      
1368
1360
      if(block_time <= 0){
1395
1387
  }
1396
1388
}
1397
1389
 
 
1390
bool run_network_hooks(const char *mode, const char *interface,
 
1391
                       const float delay){
 
1392
  struct dirent **direntries;
 
1393
  struct dirent *direntry;
 
1394
  int ret;
 
1395
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1396
                         alphasort);
 
1397
  if(numhooks == -1){
 
1398
    perror_plus("scandir");
 
1399
  } else {
 
1400
    int devnull = open("/dev/null", O_RDONLY);
 
1401
    for(int i = 0; i < numhooks; i++){
 
1402
      direntry = direntries[i];
 
1403
      char *fullname = NULL;
 
1404
      ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
 
1405
      if(ret < 0){
 
1406
        perror_plus("asprintf");
 
1407
        continue;
 
1408
      }
 
1409
      if(debug){
 
1410
        fprintf_plus(stderr, "Running network hook \"%s\"\n",
 
1411
                     direntry->d_name);
 
1412
      }
 
1413
      pid_t hook_pid = fork();
 
1414
      if(hook_pid == 0){
 
1415
        /* Child */
 
1416
        /* Raise privileges */
 
1417
        errno = 0;
 
1418
        ret = seteuid(0);
 
1419
        if(ret == -1){
 
1420
          perror_plus("seteuid");
 
1421
        }
 
1422
        /* Raise privileges even more */
 
1423
        errno = 0;
 
1424
        ret = setuid(0);
 
1425
        if(ret == -1){
 
1426
          perror_plus("setuid");
 
1427
        }
 
1428
        /* Set group */
 
1429
        errno = 0;
 
1430
        ret = setgid(0);
 
1431
        if(ret == -1){
 
1432
          perror_plus("setgid");
 
1433
        }
 
1434
        /* Reset supplementary groups */
 
1435
        errno = 0;
 
1436
        ret = setgroups(0, NULL);
 
1437
        if(ret == -1){
 
1438
          perror_plus("setgroups");
 
1439
        }
 
1440
        dup2(devnull, STDIN_FILENO);
 
1441
        close(devnull);
 
1442
        dup2(STDERR_FILENO, STDOUT_FILENO);
 
1443
        ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
 
1444
        if(ret == -1){
 
1445
          perror_plus("setenv");
 
1446
          _exit(EX_OSERR);
 
1447
        }
 
1448
        ret = setenv("DEVICE", interface, 1);
 
1449
        if(ret == -1){
 
1450
          perror_plus("setenv");
 
1451
          _exit(EX_OSERR);
 
1452
        }
 
1453
        ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
 
1454
        if(ret == -1){
 
1455
          perror_plus("setenv");
 
1456
          _exit(EX_OSERR);
 
1457
        }
 
1458
        ret = setenv("MODE", mode, 1);
 
1459
        if(ret == -1){
 
1460
          perror_plus("setenv");
 
1461
          _exit(EX_OSERR);
 
1462
        }
 
1463
        char *delaystring;
 
1464
        ret = asprintf(&delaystring, "%f", delay);
 
1465
        if(ret == -1){
 
1466
          perror_plus("asprintf");
 
1467
          _exit(EX_OSERR);
 
1468
        }
 
1469
        ret = setenv("DELAY", delaystring, 1);
 
1470
        if(ret == -1){
 
1471
          free(delaystring);
 
1472
          perror_plus("setenv");
 
1473
          _exit(EX_OSERR);
 
1474
        }
 
1475
        free(delaystring);
 
1476
        if(connect_to != NULL){
 
1477
          ret = setenv("CONNECT", connect_to, 1);
 
1478
          if(ret == -1){
 
1479
            perror_plus("setenv");
 
1480
            _exit(EX_OSERR);
 
1481
          }
 
1482
        }
 
1483
        if(execl(fullname, direntry->d_name, mode, NULL) == -1){
 
1484
          perror_plus("execl");
 
1485
          _exit(EXIT_FAILURE);
 
1486
        }
 
1487
      } else {
 
1488
        int status;
 
1489
        if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
 
1490
          perror_plus("waitpid");
 
1491
          free(fullname);
 
1492
          continue;
 
1493
        }
 
1494
        if(WIFEXITED(status)){
 
1495
          if(WEXITSTATUS(status) != 0){
 
1496
            fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
 
1497
                         " with status %d\n", direntry->d_name,
 
1498
                         WEXITSTATUS(status));
 
1499
            free(fullname);
 
1500
            continue;
 
1501
          }
 
1502
        } else if(WIFSIGNALED(status)){
 
1503
          fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
 
1504
                       " signal %d\n", direntry->d_name,
 
1505
                       WTERMSIG(status));
 
1506
          free(fullname);
 
1507
          continue;
 
1508
        } else {
 
1509
          fprintf_plus(stderr, "Warning: network hook \"%s\""
 
1510
                       " crashed\n", direntry->d_name);
 
1511
          free(fullname);
 
1512
          continue;
 
1513
        }
 
1514
      }
 
1515
      free(fullname);
 
1516
      if(debug){
 
1517
        fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
 
1518
                     direntry->d_name);
 
1519
      }
 
1520
    }
 
1521
    close(devnull);
 
1522
  }
 
1523
  return true;
 
1524
}
 
1525
 
1398
1526
int main(int argc, char *argv[]){
1399
1527
  AvahiSServiceBrowser *sb = NULL;
1400
1528
  int error;
1480
1608
        .group = 2 },
1481
1609
      { .name = "retry", .key = 132,
1482
1610
        .arg = "SECONDS",
1483
 
        .doc = "Retry interval used when denied by the mandos server",
 
1611
        .doc = "Retry interval used when denied by the Mandos server",
 
1612
        .group = 2 },
 
1613
      { .name = "network-hook-dir", .key = 133,
 
1614
        .arg = "DIR",
 
1615
        .doc = "Directory where network hooks are located",
1484
1616
        .group = 2 },
1485
1617
      /*
1486
1618
       * These reproduce what we would get without ARGP_NO_HELP
1540
1672
          argp_error(state, "Bad retry interval");
1541
1673
        }
1542
1674
        break;
 
1675
      case 133:                 /* --network-hook-dir */
 
1676
        hookdir = arg;
 
1677
        break;
1543
1678
        /*
1544
1679
         * These reproduce what we would get without ARGP_NO_HELP
1545
1680
         */
1551
1686
        argp_state_help(state, state->out_stream,
1552
1687
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
1553
1688
      case 'V':                 /* --version */
1554
 
        fprintf(state->out_stream, "Mandos plugin mandos-client: ");
1555
 
        fprintf(state->out_stream, "%s\n", argp_program_version);
 
1689
        fprintf_plus(state->out_stream, "%s\n", argp_program_version);
1556
1690
        exit(argp_err_exit_status);
1557
1691
        break;
1558
1692
      default:
1585
1719
  {
1586
1720
    /* Work around Debian bug #633582:
1587
1721
       <http://bugs.debian.org/633582> */
1588
 
    struct stat st;
1589
1722
    
1590
1723
    /* Re-raise priviliges */
1591
1724
    errno = 0;
1592
1725
    ret = seteuid(0);
1593
1726
    if(ret == -1){
1594
1727
      perror_plus("seteuid");
1595
 
    }
1596
 
    
1597
 
    if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
1598
 
      int seckey_fd = open(seckey, O_RDONLY);
1599
 
      if(seckey_fd == -1){
1600
 
        perror_plus("open");
1601
 
      } else {
1602
 
        ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
1603
 
        if(ret == -1){
1604
 
          perror_plus("fstat");
1605
 
        } else {
1606
 
          if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
1607
 
            ret = fchown(seckey_fd, uid, gid);
1608
 
            if(ret == -1){
1609
 
              perror_plus("fchown");
1610
 
            }
1611
 
          }
1612
 
        }
1613
 
        TEMP_FAILURE_RETRY(close(seckey_fd));
1614
 
      }
1615
 
    }
1616
 
    
1617
 
    if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
1618
 
      int pubkey_fd = open(pubkey, O_RDONLY);
1619
 
      if(pubkey_fd == -1){
1620
 
        perror_plus("open");
1621
 
      } else {
1622
 
        ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
1623
 
        if(ret == -1){
1624
 
          perror_plus("fstat");
1625
 
        } else {
1626
 
          if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
1627
 
            ret = fchown(pubkey_fd, uid, gid);
1628
 
            if(ret == -1){
1629
 
              perror_plus("fchown");
1630
 
            }
1631
 
          }
1632
 
        }
1633
 
        TEMP_FAILURE_RETRY(close(pubkey_fd));
1634
 
      }
1635
 
    }
1636
 
    
1637
 
    /* Lower privileges */
1638
 
    errno = 0;
1639
 
    ret = seteuid(uid);
1640
 
    if(ret == -1){
1641
 
      perror_plus("seteuid");
1642
 
    }
1643
 
  }
1644
 
  
1645
 
  /* Find network hooks and run them */
1646
 
  {
1647
 
    struct dirent **direntries;
1648
 
    struct dirent *direntry;
1649
 
    int numhooks = scandir(HOOKDIR, &direntries, runnable_hook,
1650
 
                           alphasort);
1651
 
    if(numhooks == -1){
1652
 
      perror_plus("scandir");
1653
1728
    } else {
1654
 
      int devnull = open("/dev/null", O_RDONLY);
1655
 
      for(int i = 0; i < numhooks; i++){
1656
 
        direntry = direntries[0];
1657
 
        char *fullname = NULL;
1658
 
        ret = asprintf(&fullname, "%s/%s", tempdir,
1659
 
                       direntry->d_name);
1660
 
        if(ret < 0){
1661
 
          perror_plus("asprintf");
1662
 
          continue;
1663
 
        }
1664
 
        pid_t hook_pid = fork();
1665
 
        if(hook_pid == 0){
1666
 
          /* Child */
1667
 
          dup2(devnull, STDIN_FILENO);
1668
 
          close(devnull);
1669
 
          dup2(STDERR_FILENO, STDOUT_FILENO);
1670
 
          ret = setenv("DEVICE", interface, 1);
1671
 
          if(ret == -1){
1672
 
            perror_plus("setenv");
1673
 
            exit(1);
1674
 
          }
1675
 
          ret = setenv("VERBOSE", debug ? "1" : "0", 1);
1676
 
          if(ret == -1){
1677
 
            perror_plus("setenv");
1678
 
            exit(1);
1679
 
          }
1680
 
          ret = setenv("MODE", "start", 1);
1681
 
          if(ret == -1){
1682
 
            perror_plus("setenv");
1683
 
            exit(1);
1684
 
          }
1685
 
          char *delaystring;
1686
 
          ret = asprintf(&delaystring, "%f", delay);
1687
 
          if(ret == -1){
1688
 
            perror_plus("asprintf");
1689
 
            exit(1);
1690
 
          }
1691
 
          ret = setenv("DELAY", delaystring, 1);
1692
 
          if(ret == -1){
1693
 
            free(delaystring);
1694
 
            perror_plus("setenv");
1695
 
            exit(1);
1696
 
          }
1697
 
          free(delaystring);
1698
 
          ret = execl(fullname, direntry->d_name, "start", NULL);
1699
 
          perror_plus("execl");
1700
 
        } else {
1701
 
          int status;
1702
 
          if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1703
 
            perror_plus("waitpid");
1704
 
            free(fullname);
1705
 
            continue;
1706
 
          }
1707
 
          if(WIFEXITED(status)){
1708
 
            if(WEXITSTATUS(status) != 0){
1709
 
              fprintf(stderr, "Mandos plugin mandos-client: "
1710
 
                      "Warning: network hook \"%s\" exited"
1711
 
                      " with status %d\n", direntry->d_name,
1712
 
                      WEXITSTATUS(status));
1713
 
              free(fullname);
1714
 
              continue;
1715
 
            }
1716
 
          } else if(WIFSIGNALED(status)){
1717
 
            fprintf(stderr, "Mandos plugin mandos-client: "
1718
 
                    "Warning: network hook \"%s\" died by"
1719
 
                    " signal %d\n", direntry->d_name,
1720
 
                    WTERMSIG(status));
1721
 
            free(fullname);
1722
 
            continue;
1723
 
          } else {
1724
 
            fprintf(stderr, "Mandos plugin mandos-client: "
1725
 
                    "Warning: network hook \"%s\" crashed\n",
1726
 
                    direntry->d_name);
1727
 
            free(fullname);
1728
 
            continue;
1729
 
          }
1730
 
        }
1731
 
        free(fullname);
1732
 
        if(quit_now){
1733
 
          goto end;
1734
 
        }
1735
 
      }
1736
 
      close(devnull);
 
1729
      struct stat st;
 
1730
      
 
1731
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
 
1732
        int seckey_fd = open(seckey, O_RDONLY);
 
1733
        if(seckey_fd == -1){
 
1734
          perror_plus("open");
 
1735
        } else {
 
1736
          ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
 
1737
          if(ret == -1){
 
1738
            perror_plus("fstat");
 
1739
          } else {
 
1740
            if(S_ISREG(st.st_mode)
 
1741
               and st.st_uid == 0 and st.st_gid == 0){
 
1742
              ret = fchown(seckey_fd, uid, gid);
 
1743
              if(ret == -1){
 
1744
                perror_plus("fchown");
 
1745
              }
 
1746
            }
 
1747
          }
 
1748
          TEMP_FAILURE_RETRY(close(seckey_fd));
 
1749
        }
 
1750
      }
 
1751
    
 
1752
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
 
1753
        int pubkey_fd = open(pubkey, O_RDONLY);
 
1754
        if(pubkey_fd == -1){
 
1755
          perror_plus("open");
 
1756
        } else {
 
1757
          ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
 
1758
          if(ret == -1){
 
1759
            perror_plus("fstat");
 
1760
          } else {
 
1761
            if(S_ISREG(st.st_mode)
 
1762
               and st.st_uid == 0 and st.st_gid == 0){
 
1763
              ret = fchown(pubkey_fd, uid, gid);
 
1764
              if(ret == -1){
 
1765
                perror_plus("fchown");
 
1766
              }
 
1767
            }
 
1768
          }
 
1769
          TEMP_FAILURE_RETRY(close(pubkey_fd));
 
1770
        }
 
1771
      }
 
1772
    
 
1773
      /* Lower privileges */
 
1774
      errno = 0;
 
1775
      ret = seteuid(uid);
 
1776
      if(ret == -1){
 
1777
        perror_plus("seteuid");
 
1778
      }
1737
1779
    }
1738
1780
  }
1739
1781
  
 
1782
  /* Run network hooks */
 
1783
  if(not run_network_hooks("start", interface, delay)){
 
1784
    goto end;
 
1785
  }
 
1786
  
1740
1787
  if(not debug){
1741
1788
    avahi_set_log_function(empty_log);
1742
1789
  }
1756
1803
      /* Pick the first interface returned */
1757
1804
      interface = strdup(direntries[0]->d_name);
1758
1805
      if(debug){
1759
 
        fprintf(stderr, "Mandos plugin mandos-client: "
1760
 
                "Using interface \"%s\"\n", interface);
 
1806
        fprintf_plus(stderr, "Using interface \"%s\"\n", interface);
1761
1807
      }
1762
1808
      if(interface == NULL){
1763
1809
        perror_plus("malloc");
1768
1814
      free(direntries);
1769
1815
    } else {
1770
1816
      free(direntries);
1771
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1772
 
              "Could not find a network interface\n");
 
1817
      fprintf_plus(stderr, "Could not find a network interface\n");
1773
1818
      exitcode = EXIT_FAILURE;
1774
1819
      goto end;
1775
1820
    }
1781
1826
  srand((unsigned int) time(NULL));
1782
1827
  mc.simple_poll = avahi_simple_poll_new();
1783
1828
  if(mc.simple_poll == NULL){
1784
 
    fprintf(stderr, "Mandos plugin mandos-client: "
1785
 
            "Avahi: Failed to create simple poll object.\n");
 
1829
    fprintf_plus(stderr,
 
1830
                 "Avahi: Failed to create simple poll object.\n");
1786
1831
    exitcode = EX_UNAVAILABLE;
1787
1832
    goto end;
1788
1833
  }
1854
1899
  if(strcmp(interface, "none") != 0){
1855
1900
    if_index = (AvahiIfIndex) if_nametoindex(interface);
1856
1901
    if(if_index == 0){
1857
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1858
 
              "No such interface: \"%s\"\n", interface);
 
1902
      fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1859
1903
      exitcode = EX_UNAVAILABLE;
1860
1904
      goto end;
1861
1905
    }
1981
2025
#endif  /* __linux__ */
1982
2026
    /* Lower privileges */
1983
2027
    errno = 0;
1984
 
    if(take_down_interface){
1985
 
      /* Lower privileges */
1986
 
      ret = seteuid(uid);
1987
 
      if(ret == -1){
1988
 
        perror_plus("seteuid");
1989
 
      }
1990
 
    } else {
1991
 
      /* Lower privileges permanently */
1992
 
      ret = setuid(uid);
1993
 
      if(ret == -1){
1994
 
        perror_plus("setuid");
1995
 
      }
 
2028
    /* Lower privileges */
 
2029
    ret = seteuid(uid);
 
2030
    if(ret == -1){
 
2031
      perror_plus("seteuid");
1996
2032
    }
1997
2033
  }
1998
2034
  
2002
2038
  
2003
2039
  ret = init_gnutls_global(pubkey, seckey);
2004
2040
  if(ret == -1){
2005
 
    fprintf(stderr, "Mandos plugin mandos-client: "
2006
 
            "init_gnutls_global failed\n");
 
2041
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2007
2042
    exitcode = EX_UNAVAILABLE;
2008
2043
    goto end;
2009
2044
  } else {
2025
2060
  }
2026
2061
  
2027
2062
  if(not init_gpgme(pubkey, seckey, tempdir)){
2028
 
    fprintf(stderr, "Mandos plugin mandos-client: "
2029
 
            "init_gpgme failed\n");
 
2063
    fprintf_plus(stderr, "init_gpgme failed\n");
2030
2064
    exitcode = EX_UNAVAILABLE;
2031
2065
    goto end;
2032
2066
  } else {
2042
2076
    /* (Mainly meant for debugging) */
2043
2077
    char *address = strrchr(connect_to, ':');
2044
2078
    if(address == NULL){
2045
 
      fprintf(stderr, "Mandos plugin mandos-client: "
2046
 
              "No colon in address\n");
 
2079
      fprintf_plus(stderr, "No colon in address\n");
2047
2080
      exitcode = EX_USAGE;
2048
2081
      goto end;
2049
2082
    }
2057
2090
    tmpmax = strtoimax(address+1, &tmp, 10);
2058
2091
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
2059
2092
       or tmpmax != (uint16_t)tmpmax){
2060
 
      fprintf(stderr, "Mandos plugin mandos-client: "
2061
 
              "Bad port number\n");
 
2093
      fprintf_plus(stderr, "Bad port number\n");
2062
2094
      exitcode = EX_USAGE;
2063
2095
      goto end;
2064
2096
    }
2094
2126
        break;
2095
2127
      }
2096
2128
      if(debug){
2097
 
        fprintf(stderr, "Mandos plugin mandos-client: "
2098
 
                "Retrying in %d seconds\n", (int)retry_interval);
 
2129
        fprintf_plus(stderr, "Retrying in %d seconds\n",
 
2130
                     (int)retry_interval);
2099
2131
      }
2100
2132
      sleep((int)retry_interval);
2101
2133
    }
2131
2163
  
2132
2164
  /* Check if creating the Avahi server object succeeded */
2133
2165
  if(mc.server == NULL){
2134
 
    fprintf(stderr, "Mandos plugin mandos-client: "
2135
 
            "Failed to create Avahi server: %s\n",
2136
 
            avahi_strerror(error));
 
2166
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
 
2167
                 avahi_strerror(error));
2137
2168
    exitcode = EX_UNAVAILABLE;
2138
2169
    goto end;
2139
2170
  }
2147
2178
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2148
2179
                                   NULL, 0, browse_callback, NULL);
2149
2180
  if(sb == NULL){
2150
 
    fprintf(stderr, "Mandos plugin mandos-client: "
2151
 
            "Failed to create service browser: %s\n",
2152
 
            avahi_strerror(avahi_server_errno(mc.server)));
 
2181
    fprintf_plus(stderr, "Failed to create service browser: %s\n",
 
2182
                 avahi_strerror(avahi_server_errno(mc.server)));
2153
2183
    exitcode = EX_UNAVAILABLE;
2154
2184
    goto end;
2155
2185
  }
2161
2191
  /* Run the main loop */
2162
2192
  
2163
2193
  if(debug){
2164
 
    fprintf(stderr, "Mandos plugin mandos-client: "
2165
 
            "Starting Avahi loop search\n");
 
2194
    fprintf_plus(stderr, "Starting Avahi loop search\n");
2166
2195
  }
2167
2196
 
2168
2197
  ret = avahi_loop_with_timeout(mc.simple_poll,
2169
2198
                                (int)(retry_interval * 1000));
2170
2199
  if(debug){
2171
 
    fprintf(stderr, "Mandos plugin mandos-client: "
2172
 
            "avahi_loop_with_timeout exited %s\n",
2173
 
            (ret == 0) ? "successfully" : "with error");
 
2200
    fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
 
2201
                 (ret == 0) ? "successfully" : "with error");
2174
2202
  }
2175
2203
  
2176
2204
 end:
2177
2205
  
2178
2206
  if(debug){
2179
 
    fprintf(stderr, "Mandos plugin mandos-client: "
2180
 
            "%s exiting\n", argv[0]);
 
2207
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
2181
2208
  }
2182
2209
  
2183
2210
  /* Cleanup things */
2199
2226
  if(gpgme_initialized){
2200
2227
    gpgme_release(mc.ctx);
2201
2228
  }
2202
 
 
 
2229
  
2203
2230
  /* Cleans up the circular linked list of Mandos servers the client
2204
2231
     has seen */
2205
2232
  if(mc.current_server != NULL){
2211
2238
    }
2212
2239
  }
2213
2240
  
2214
 
  /* XXX run network hooks "stop" here  */
 
2241
  /* Run network hooks */
 
2242
  run_network_hooks("stop", interface, delay);
2215
2243
  
2216
 
  /* Take down the network interface */
2217
 
  if(take_down_interface){
2218
 
    /* Re-raise priviliges */
 
2244
  /* Re-raise priviliges */
 
2245
  {
2219
2246
    errno = 0;
2220
2247
    ret = seteuid(0);
2221
2248
    if(ret == -1){
2222
2249
      perror_plus("seteuid");
2223
2250
    }
2224
 
    if(geteuid() == 0){
 
2251
    
 
2252
    /* Take down the network interface */
 
2253
    if(take_down_interface and geteuid() == 0){
2225
2254
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
2226
2255
      if(ret == -1){
2227
2256
        perror_plus("ioctl SIOCGIFFLAGS");
2236
2265
      if(ret == -1){
2237
2266
        perror_plus("close");
2238
2267
      }
2239
 
      /* Lower privileges permanently */
2240
 
      errno = 0;
2241
 
      ret = setuid(uid);
2242
 
      if(ret == -1){
2243
 
        perror_plus("setuid");
2244
 
      }
2245
2268
    }
2246
2269
  }
 
2270
  /* Lower privileges permanently */
 
2271
  errno = 0;
 
2272
  ret = setuid(uid);
 
2273
  if(ret == -1){
 
2274
    perror_plus("setuid");
 
2275
  }
2247
2276
  
2248
2277
  /* Removes the GPGME temp directory and all files inside */
2249
2278
  if(tempdir_created){
2263
2292
        }
2264
2293
        ret = remove(fullname);
2265
2294
        if(ret == -1){
2266
 
          fprintf(stderr, "Mandos plugin mandos-client: "
2267
 
                  "remove(\"%s\"): %s\n", fullname, strerror(errno));
 
2295
          fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
 
2296
                       strerror(errno));
2268
2297
        }
2269
2298
        free(fullname);
2270
2299
      }