/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: 2015-05-23 20:18:34 UTC
  • mto: This revision was merged to the branch mainline in revision 756.
  • Revision ID: teddy@recompile.se-20150523201834-e89ex4ito93yni8x
mandos: Use multiprocessing module to run checkers.

For a long time, the Mandos server has occasionally logged the message
"ERROR: Child process vanished".  This was never a fatal error, but it
has been annoying and slightly worrying, since a definite cause was
not found.  One potential cause could be the "multiprocessing" and
"subprocess" modules conflicting w.r.t. SIGCHLD.  To avoid this,
change the running of checkers from using subprocess.Popen
asynchronously to instead first create a multiprocessing.Process()
(which is asynchronous) calling a function, and have that function
then call subprocess.call() (which is synchronous).  In this way, the
only thing using any asynchronous subprocesses is the multiprocessing
module.

This makes it necessary to change one small thing in the D-Bus API,
since the subprocesses.call() function does not expose the raw wait(2)
status value.

DBUS-API (CheckerCompleted): Change the second value provided by this
                             D-Bus signal from the raw wait(2) status
                             to the actual terminating signal number.
mandos (subprocess_call_pipe): New function to be called by
                               multiprocessing.Process (starting a
                               separate process).
(Client.last_checker signal): New attribute for signal which
                              terminated last checker.  Like
                              last_checker_status, only not accessible
                              via D-Bus.
(Client.checker_callback): Take new "connection" argument and use it
                           to get returncode; set last_checker_signal.
                           Return False so gobject does not call this
                           callback again.
(Client.start_checker): Start checker using a multiprocessing.Process
                        instead of a subprocess.Popen.
(ClientDBus.checker_callback): Take new "connection" argument.        Call
                               Client.checker_callback early to have
                               it set last_checker_status and
                               last_checker_signal; use those.  Change
                               second value provided to D-Bus signal
                               CheckerCompleted to use
                               last_checker_signal if checker was
                               terminated by signal.
mandos-monitor: Update to reflect DBus API change.
(MandosClientWidget.checker_completed): Take "signal" instead of
                                        "condition" argument.  Use it
                                        accordingly.  Remove dead code
                                        (os.WCOREDUMP case).

Show diffs side-by-side

added added

removed removed

Lines of Context:
240
240
  ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
241
241
  if(ret == -1){
242
242
    perror_plus("clock_gettime");
243
 
    free(new_server->ip);
 
243
#ifdef __GNUC__
 
244
#pragma GCC diagnostic push
 
245
#pragma GCC diagnostic ignored "-Wcast-qual"
 
246
#endif
 
247
    free((char *)(new_server->ip));
 
248
#ifdef __GNUC__
 
249
#pragma GCC diagnostic pop
 
250
#endif
244
251
    free(new_server);
245
252
    return false;
246
253
  }
486
493
  return plaintext_length;
487
494
}
488
495
 
 
496
__attribute__((warn_unused_result, const))
 
497
static const char *safe_string(const char *str){
 
498
  if(str == NULL)
 
499
    return "(unknown)";
 
500
  return str;
 
501
}
 
502
 
489
503
__attribute__((warn_unused_result))
490
504
static const char *safer_gnutls_strerror(int value){
491
505
  const char *ret = gnutls_strerror(value);
492
 
  if(ret == NULL)
493
 
    ret = "(unknown)";
494
 
  return ret;
 
506
  return safe_string(ret);
495
507
}
496
508
 
497
509
/* GnuTLS log function callback */
506
518
                              const char *seckeyfilename,
507
519
                              mandos_context *mc){
508
520
  int ret;
 
521
  unsigned int uret;
509
522
  
510
523
  if(debug){
511
524
    fprintf_plus(stderr, "Initializing GnuTLS\n");
562
575
                 safer_gnutls_strerror(ret));
563
576
    goto globalfail;
564
577
  }
 
578
  if(mc->dh_bits == 0){
 
579
    /* Find out the optimal number of DH bits */
 
580
    /* Try to read the private key file */
 
581
    gnutls_datum_t buffer = { .data = NULL, .size = 0 };
 
582
    {
 
583
      int secfile = open(seckeyfilename, O_RDONLY);
 
584
      size_t buffer_capacity = 0;
 
585
      while(true){
 
586
        buffer_capacity = incbuffer((char **)&buffer.data,
 
587
                                    (size_t)buffer.size,
 
588
                                    (size_t)buffer_capacity);
 
589
        if(buffer_capacity == 0){
 
590
          perror_plus("incbuffer");
 
591
          free(buffer.data);
 
592
          buffer.data = NULL;
 
593
          break;
 
594
        }
 
595
        ssize_t bytes_read = read(secfile, buffer.data + buffer.size,
 
596
                                  BUFFER_SIZE);
 
597
        /* EOF */
 
598
        if(bytes_read == 0){
 
599
          break;
 
600
        }
 
601
        /* check bytes_read for failure */
 
602
        if(bytes_read < 0){
 
603
          perror_plus("read");
 
604
          free(buffer.data);
 
605
          buffer.data = NULL;
 
606
          break;
 
607
        }
 
608
        buffer.size += (unsigned int)bytes_read;
 
609
      }
 
610
      close(secfile);
 
611
    }
 
612
    /* If successful, use buffer to parse private key */
 
613
    gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
614
    if(buffer.data != NULL){
 
615
      {
 
616
        gnutls_openpgp_privkey_t privkey = NULL;
 
617
        ret = gnutls_openpgp_privkey_init(&privkey);
 
618
        if(ret != GNUTLS_E_SUCCESS){
 
619
          fprintf_plus(stderr, "Error initializing OpenPGP key"
 
620
                       " structure: %s", safer_gnutls_strerror(ret));
 
621
          free(buffer.data);
 
622
          buffer.data = NULL;
 
623
        } else {
 
624
          ret = gnutls_openpgp_privkey_import(privkey, &buffer,
 
625
                                            GNUTLS_OPENPGP_FMT_BASE64,
 
626
                                              "", 0);
 
627
          if(ret != GNUTLS_E_SUCCESS){
 
628
            fprintf_plus(stderr, "Error importing OpenPGP key : %s",
 
629
                         safer_gnutls_strerror(ret));
 
630
            privkey = NULL;
 
631
          }
 
632
          free(buffer.data);
 
633
          buffer.data = NULL;
 
634
          if(privkey != NULL){
 
635
            /* Use private key to suggest an appropriate sec_param */
 
636
            sec_param = gnutls_openpgp_privkey_sec_param(privkey);
 
637
            gnutls_openpgp_privkey_deinit(privkey);
 
638
            if(debug){
 
639
              fprintf_plus(stderr, "This OpenPGP key implies using a"
 
640
                           " GnuTLS security parameter \"%s\".\n",
 
641
                           safe_string(gnutls_sec_param_get_name
 
642
                                       (sec_param)));
 
643
            }
 
644
          }
 
645
        }
 
646
      }
 
647
      if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
 
648
        /* Err on the side of caution */
 
649
        sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
650
        if(debug){
 
651
          fprintf_plus(stderr, "Falling back to security parameter"
 
652
                       " \"%s\"\n",
 
653
                       safe_string(gnutls_sec_param_get_name
 
654
                                   (sec_param)));
 
655
        }
 
656
      }
 
657
    }
 
658
    uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
 
659
    if(uret != 0){
 
660
      mc->dh_bits = uret;
 
661
      if(debug){
 
662
        fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
 
663
                     " implies %u DH bits; using that.\n",
 
664
                     safe_string(gnutls_sec_param_get_name
 
665
                                 (sec_param)),
 
666
                     mc->dh_bits);
 
667
      }
 
668
    } else {
 
669
      fprintf_plus(stderr, "Failed to get implied number of DH"
 
670
                   " bits for security parameter \"%s\"): %s\n",
 
671
                   safe_string(gnutls_sec_param_get_name(sec_param)),
 
672
                   safer_gnutls_strerror(ret));
 
673
      goto globalfail;
 
674
    }
 
675
  } else if(debug){
 
676
    fprintf_plus(stderr, "DH bits explicitly set to %u\n",
 
677
                 mc->dh_bits);
 
678
  }
565
679
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
566
680
  if(ret != GNUTLS_E_SUCCESS){
567
 
    fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
568
 
                 safer_gnutls_strerror(ret));
 
681
    fprintf_plus(stderr, "Error in GnuTLS prime generation (%u bits):"
 
682
                 " %s\n", mc->dh_bits, safer_gnutls_strerror(ret));
569
683
    goto globalfail;
570
684
  }
571
685
  
1069
1183
     timed out */
1070
1184
  
1071
1185
  if(quit_now){
 
1186
    avahi_s_service_resolver_free(r);
1072
1187
    return;
1073
1188
  }
1074
1189
  
1642
1757
        _exit(EXIT_FAILURE);
1643
1758
      }
1644
1759
    } else {
 
1760
      if(hook_pid == -1){
 
1761
        perror_plus("fork");
 
1762
        free(direntry);
 
1763
        continue;
 
1764
      }
1645
1765
      int status;
1646
1766
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1647
1767
        perror_plus("waitpid");
 
1768
        free(direntry);
1648
1769
        continue;
1649
1770
      }
1650
1771
      if(WIFEXITED(status)){
1652
1773
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1653
1774
                       " with status %d\n", direntry->d_name,
1654
1775
                       WEXITSTATUS(status));
 
1776
          free(direntry);
1655
1777
          continue;
1656
1778
        }
1657
1779
      } else if(WIFSIGNALED(status)){
1658
1780
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1659
1781
                     " signal %d\n", direntry->d_name,
1660
1782
                     WTERMSIG(status));
 
1783
        free(direntry);
1661
1784
        continue;
1662
1785
      } else {
1663
1786
        fprintf_plus(stderr, "Warning: network hook \"%s\""
1664
1787
                     " crashed\n", direntry->d_name);
 
1788
        free(direntry);
1665
1789
        continue;
1666
1790
      }
1667
1791
    }
1669
1793
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1670
1794
                   direntry->d_name);
1671
1795
    }
 
1796
    free(direntry);
1672
1797
  }
1673
1798
  free(direntries);
1674
1799
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
1882
2007
}
1883
2008
 
1884
2009
int main(int argc, char *argv[]){
1885
 
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
 
2010
  mandos_context mc = { .server = NULL, .dh_bits = 0,
1886
2011
                        .priority = "SECURE256:!CTYPE-X.509:"
1887
 
                        "+CTYPE-OPENPGP", .current_server = NULL,
 
2012
                        "+CTYPE-OPENPGP:!RSA", .current_server = NULL,
1888
2013
                        .interfaces = NULL, .interfaces_size = 0 };
1889
2014
  AvahiSServiceBrowser *sb = NULL;
1890
2015
  error_t ret_errno;
2268
2393
        if(ret_errno != 0){
2269
2394
          errno = ret_errno;
2270
2395
          perror_plus("argz_add");
 
2396
          free(direntries[i]);
2271
2397
          continue;
2272
2398
        }
2273
2399
        if(debug){
2274
2400
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
2275
2401
                       direntries[i]->d_name);
2276
2402
        }
 
2403
        free(direntries[i]);
2277
2404
      }
2278
2405
      free(direntries);
2279
2406
    } else {
2549
2676
    mc.current_server->prev->next = NULL;
2550
2677
    while(mc.current_server != NULL){
2551
2678
      server *next = mc.current_server->next;
 
2679
#ifdef __GNUC__
 
2680
#pragma GCC diagnostic push
 
2681
#pragma GCC diagnostic ignored "-Wcast-qual"
 
2682
#endif
 
2683
      free((char *)(mc.current_server->ip));
 
2684
#ifdef __GNUC__
 
2685
#pragma GCC diagnostic pop
 
2686
#endif
2552
2687
      free(mc.current_server);
2553
2688
      mc.current_server = next;
2554
2689
    }
2623
2758
                         " \"%s\", 0): %s\n", tempdir,
2624
2759
                         direntries[i]->d_name, strerror(errno));
2625
2760
          }
 
2761
          free(direntries[i]);
2626
2762
        }
2627
2763
        
2628
2764
        /* need to clean even if 0 because man page doesn't specify */