/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: 2026-05-17 23:56:03 UTC
  • Revision ID: teddy@recompile.se-20260517235603-5ygymkxkh32rxjkf
Remove white space at end of most lines

Remove white space at end of most lines.  Except for where it is
significant, like in shell script "here documents" where the delimiter
word is prefixed by "-", in block comments, and in config file example
settings.  Also not in README files, the INSTALL file, the DBUS-API
file, or the COPYING file (the last of which was copied verbatim from
FSF).

* dbus-mandos.conf: Remove white space at end of lines.
* debian/mandos-client.postinst: - '' -
* debian/mandos-client.templates: - '' -
* debian/mandos.postinst: - '' -
* dracut-module/password-agent.c: - '' -
* dracut-module/password-agent.xml: - '' -
* intro.xml: - '' -
* legalnotice.xml: - '' -
* mandos-clients.conf.xml: - '' -
* mandos-ctl.xml: - '' -
* mandos-keygen: - '' -
* mandos-keygen.xml: - '' -
* mandos-monitor.xml: - '' -
* mandos-options.xml: - '' -
* mandos.conf.xml: - '' -
* mandos.xml: - '' -
* network-hooks.d/wireless: - '' -
* plugin-helpers/mandos-client-iprouteadddel.c: - '' -
* plugin-runner.c: - '' -
* plugin-runner.xml: - '' -
* plugins.d/askpass-fifo.c: - '' -
* plugins.d/askpass-fifo.xml: - '' -
* plugins.d/mandos-client.c: - '' -
* plugins.d/mandos-client.xml: - '' -
* plugins.d/password-prompt.c: - '' -
* plugins.d/password-prompt.xml: - '' -
* plugins.d/plymouth.c: - '' -
* plugins.d/plymouth.xml: - '' -
* plugins.d/splashy.c: - '' -
* plugins.d/splashy.xml: - '' -
* plugins.d/usplash.c: - '' -
* plugins.d/usplash.xml: - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
233
233
int fprintf_plus(FILE *stream, const char *format, ...){
234
234
  va_list ap;
235
235
  va_start (ap, format);
236
 
  
 
236
 
237
237
  TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
238
238
                             program_invocation_short_name));
239
239
  return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
372
372
                       mandos_context *mc){
373
373
  gpgme_error_t rc;
374
374
  gpgme_engine_info_t engine_info;
375
 
  
 
375
 
376
376
  /*
377
377
   * Helper function to insert pub and seckey to the engine keyring.
378
378
   */
380
380
    int ret;
381
381
    int fd;
382
382
    gpgme_data_t pgp_data;
383
 
    
 
383
 
384
384
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
385
385
    if(fd == -1){
386
386
      perror_plus("open");
387
387
      return false;
388
388
    }
389
 
    
 
389
 
390
390
    /* Workaround for systems without a real-time clock; see also
391
391
       Debian bug #894495: <https://bugs.debian.org/894495> */
392
392
    do {
442
442
                   gpgme_strsource(rc), gpgme_strerror(rc));
443
443
      return false;
444
444
    }
445
 
    
 
445
 
446
446
    rc = gpgme_op_import(mc->ctx, pgp_data);
447
447
    if(rc != GPG_ERR_NO_ERROR){
448
448
      fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
524
524
        return false;
525
525
      }
526
526
    }
527
 
    
 
527
 
528
528
    ret = close(fd);
529
529
    if(ret == -1){
530
530
      perror_plus("close");
532
532
    gpgme_data_release(pgp_data);
533
533
    return true;
534
534
  }
535
 
  
 
535
 
536
536
  if(debug){
537
537
    fprintf_plus(stderr, "Initializing GPGME\n");
538
538
  }
539
 
  
 
539
 
540
540
  /* Init GPGME */
541
541
  gpgme_check_version(NULL);
542
542
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
545
545
                 gpgme_strsource(rc), gpgme_strerror(rc));
546
546
    return false;
547
547
  }
548
 
  
 
548
 
549
549
  /* Set GPGME home directory for the OpenPGP engine only */
550
550
  rc = gpgme_get_engine_info(&engine_info);
551
551
  if(rc != GPG_ERR_NO_ERROR){
566
566
                 tempdir);
567
567
    return false;
568
568
  }
569
 
  
 
569
 
570
570
  /* Create new GPGME "context" */
571
571
  rc = gpgme_new(&(mc->ctx));
572
572
  if(rc != GPG_ERR_NO_ERROR){
574
574
                 gpgme_strsource(rc), gpgme_strerror(rc));
575
575
    return false;
576
576
  }
577
 
  
 
577
 
578
578
  if(not import_key(pubkey) or not import_key(seckey)){
579
579
    return false;
580
580
  }
581
 
  
 
581
 
582
582
  return true;
583
583
}
584
584
 
596
596
  ssize_t ret;
597
597
  size_t plaintext_capacity = 0;
598
598
  ssize_t plaintext_length = 0;
599
 
  
 
599
 
600
600
  if(debug){
601
601
    fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
602
602
  }
603
 
  
 
603
 
604
604
  /* Create new GPGME data buffer from memory cryptotext */
605
605
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
606
606
                               0);
609
609
                 gpgme_strsource(rc), gpgme_strerror(rc));
610
610
    return -1;
611
611
  }
612
 
  
 
612
 
613
613
  /* Create new empty GPGME data buffer for the plaintext */
614
614
  rc = gpgme_data_new(&dh_plain);
615
615
  if(rc != GPG_ERR_NO_ERROR){
618
618
    gpgme_data_release(dh_crypto);
619
619
    return -1;
620
620
  }
621
 
  
 
621
 
622
622
  /* Decrypt data from the cryptotext data buffer to the plaintext
623
623
     data buffer */
624
624
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
654
654
    }
655
655
    goto decrypt_end;
656
656
  }
657
 
  
 
657
 
658
658
  if(debug){
659
659
    fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
660
660
  }
661
 
  
 
661
 
662
662
  /* Seek back to the beginning of the GPGME plaintext data buffer */
663
663
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
664
664
    perror_plus("gpgme_data_seek");
665
665
    plaintext_length = -1;
666
666
    goto decrypt_end;
667
667
  }
668
 
  
 
668
 
669
669
  *plaintext = NULL;
670
670
  while(true){
671
671
    plaintext_capacity = incbuffer(plaintext,
676
676
      plaintext_length = -1;
677
677
      goto decrypt_end;
678
678
    }
679
 
    
 
679
 
680
680
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
681
681
                          BUFFER_SIZE);
682
682
    /* Print the data, if any */
691
691
    }
692
692
    plaintext_length += ret;
693
693
  }
694
 
  
 
694
 
695
695
  if(debug){
696
696
    fprintf_plus(stderr, "Decrypted password is: ");
697
697
    for(ssize_t i = 0; i < plaintext_length; i++){
699
699
    }
700
700
    fprintf(stderr, "\n");
701
701
  }
702
 
  
 
702
 
703
703
 decrypt_end:
704
 
  
 
704
 
705
705
  /* Delete the GPGME cryptotext data buffer */
706
706
  gpgme_data_release(dh_crypto);
707
 
  
 
707
 
708
708
  /* Delete the GPGME plaintext data buffer */
709
709
  gpgme_data_release(dh_plain);
710
710
  return plaintext_length;
736
736
                              const char *dhparamsfilename,
737
737
                              mandos_context *mc){
738
738
  int ret;
739
 
  
 
739
 
740
740
  if(debug){
741
741
    fprintf_plus(stderr, "Initializing GnuTLS\n");
742
742
  }
743
 
  
 
743
 
744
744
  if(debug){
745
745
    /* "Use a log level over 10 to enable all debugging options."
746
746
     * - GnuTLS manual
748
748
    gnutls_global_set_log_level(11);
749
749
    gnutls_global_set_log_function(debuggnutls);
750
750
  }
751
 
  
 
751
 
752
752
  /* OpenPGP credentials */
753
753
  ret = gnutls_certificate_allocate_credentials(&mc->cred);
754
754
  if(ret != GNUTLS_E_SUCCESS){
756
756
                 safer_gnutls_strerror(ret));
757
757
    return -1;
758
758
  }
759
 
  
 
759
 
760
760
  if(debug){
761
761
    fprintf_plus(stderr, "Attempting to use public key %s and"
762
762
                 " private key %s as GnuTLS credentials\n",
763
763
                 pubkeyfilename,
764
764
                 seckeyfilename);
765
765
  }
766
 
  
 
766
 
767
767
#if GNUTLS_VERSION_NUMBER >= 0x030606
768
768
  ret = gnutls_certificate_set_rawpk_key_file
769
769
    (mc->cred, pubkeyfilename, seckeyfilename,
791
791
                 safer_gnutls_strerror(ret));
792
792
    goto globalfail;
793
793
  }
794
 
  
 
794
 
795
795
  /* GnuTLS server initialization */
796
796
  ret = gnutls_dh_params_init(&mc->dh_params);
797
797
  if(ret != GNUTLS_E_SUCCESS){
982
982
      gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
983
983
    }
984
984
  }
985
 
  
 
985
 
986
986
  return 0;
987
 
  
 
987
 
988
988
 globalfail:
989
 
  
 
989
 
990
990
  gnutls_certificate_free_credentials(mc->cred);
991
991
  gnutls_dh_params_deinit(mc->dh_params);
992
992
  return -1;
1015
1015
                 "Error in GnuTLS session initialization: %s\n",
1016
1016
                 safer_gnutls_strerror(ret));
1017
1017
  }
1018
 
  
 
1018
 
1019
1019
  {
1020
1020
    const char *err;
1021
1021
    do {
1033
1033
      return -1;
1034
1034
    }
1035
1035
  }
1036
 
  
 
1036
 
1037
1037
  do {
1038
1038
    ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
1039
1039
                                 mc->cred);
1048
1048
    gnutls_deinit(*session);
1049
1049
    return -1;
1050
1050
  }
1051
 
  
 
1051
 
1052
1052
  /* ignore client certificate if any. */
1053
1053
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
1054
 
  
 
1054
 
1055
1055
  return 0;
1056
1056
}
1057
1057
 
1077
1077
    }
1078
1078
    return false;
1079
1079
  }
1080
 
  
 
1080
 
1081
1081
  char interface[IF_NAMESIZE];
1082
1082
  if(if_indextoname((unsigned int)if_index, interface) == NULL){
1083
1083
    perror_plus("if_indextoname");
1084
1084
    return false;
1085
1085
  }
1086
 
  
 
1086
 
1087
1087
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1088
1088
  if(devnull == -1){
1089
1089
    perror_plus("open(\"/dev/null\", O_RDONLY)");
1249
1249
  gnutls_session_t session;
1250
1250
  int pf;                       /* Protocol family */
1251
1251
  bool route_added = false;
1252
 
  
 
1252
 
1253
1253
  errno = 0;
1254
 
  
 
1254
 
1255
1255
  if(quit_now){
1256
1256
    errno = EINTR;
1257
1257
    return -1;
1258
1258
  }
1259
 
  
 
1259
 
1260
1260
  switch(af){
1261
1261
  case AF_INET6:
1262
1262
    pf = PF_INET6;
1269
1269
    errno = EINVAL;
1270
1270
    return -1;
1271
1271
  }
1272
 
  
 
1272
 
1273
1273
  /* If the interface is specified and we have a list of interfaces */
1274
1274
  if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
1275
1275
    /* Check if the interface is one of the interfaces we are using */
1302
1302
      return -1;
1303
1303
    }
1304
1304
  }
1305
 
  
 
1305
 
1306
1306
  ret = init_gnutls_session(&session, mc);
1307
1307
  if(ret != 0){
1308
1308
    return -1;
1309
1309
  }
1310
 
  
 
1310
 
1311
1311
  if(debug){
1312
1312
    fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
1313
1313
                 PRIuMAX "\n", ip, (uintmax_t)port);
1314
1314
  }
1315
 
  
 
1315
 
1316
1316
  tcp_sd = socket(pf, SOCK_STREAM | SOCK_CLOEXEC, 0);
1317
1317
  if(tcp_sd < 0){
1318
1318
    int e = errno;
1320
1320
    errno = e;
1321
1321
    goto mandos_end;
1322
1322
  }
1323
 
  
 
1323
 
1324
1324
  if(quit_now){
1325
1325
    errno = EINTR;
1326
1326
    goto mandos_end;
1327
1327
  }
1328
 
  
 
1328
 
1329
1329
  if(af == AF_INET6){
1330
1330
    struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
1331
1331
    *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
1363
1363
  } else {
1364
1364
    ((struct sockaddr_in *)&to)->sin_port = htons(port);
1365
1365
  }
1366
 
  
 
1366
 
1367
1367
  if(quit_now){
1368
1368
    errno = EINTR;
1369
1369
    goto mandos_end;
1370
1370
  }
1371
 
  
 
1371
 
1372
1372
  if(debug){
1373
1373
    if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
1374
1374
      char interface[IF_NAMESIZE];
1403
1403
      fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
1404
1404
    }
1405
1405
  }
1406
 
  
 
1406
 
1407
1407
  if(quit_now){
1408
1408
    errno = EINTR;
1409
1409
    goto mandos_end;
1410
1410
  }
1411
 
  
 
1411
 
1412
1412
  while(true){
1413
1413
    if(af == AF_INET6){
1414
1414
      ret = connect(tcp_sd, (struct sockaddr *)&to,
1435
1435
           connect to a Mandos server announced by Avahi on a server
1436
1436
           host with a global address.  Work around this by retrying
1437
1437
           with an explicit route added with the server's address.
1438
 
           
 
1438
 
1439
1439
           Avahi bug reference:
1440
1440
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1441
1441
           https://bugs.debian.org/587961
1458
1458
      }
1459
1459
      goto mandos_end;
1460
1460
    }
1461
 
    
 
1461
 
1462
1462
    if(quit_now){
1463
1463
      errno = EINTR;
1464
1464
      goto mandos_end;
1465
1465
    }
1466
1466
    break;
1467
1467
  }
1468
 
  
 
1468
 
1469
1469
  const char *out = mandos_protocol_version;
1470
1470
  written = 0;
1471
1471
  while(true){
1489
1489
        break;
1490
1490
      }
1491
1491
    }
1492
 
  
 
1492
 
1493
1493
    if(quit_now){
1494
1494
      errno = EINTR;
1495
1495
      goto mandos_end;
1496
1496
    }
1497
1497
  }
1498
 
  
 
1498
 
1499
1499
  if(debug){
1500
1500
    fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
1501
1501
  }
1502
 
  
 
1502
 
1503
1503
  if(quit_now){
1504
1504
    errno = EINTR;
1505
1505
    goto mandos_end;
1506
1506
  }
1507
 
  
 
1507
 
1508
1508
  /* This casting via intptr_t is to eliminate warning about casting
1509
1509
     an int to a pointer type.  This is exactly how the Guile-GnuTLS
1510
1510
     function "set-session-transport-fd!" does it. */
1511
1511
  gnutls_transport_set_ptr(session,
1512
1512
                           (gnutls_transport_ptr_t)(intptr_t)tcp_sd);
1513
 
  
 
1513
 
1514
1514
  if(quit_now){
1515
1515
    errno = EINTR;
1516
1516
    goto mandos_end;
1517
1517
  }
1518
 
  
 
1518
 
1519
1519
  do {
1520
1520
    ret = gnutls_handshake(session);
1521
1521
    if(quit_now){
1523
1523
      goto mandos_end;
1524
1524
    }
1525
1525
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1526
 
  
 
1526
 
1527
1527
  if(ret != GNUTLS_E_SUCCESS){
1528
1528
    if(debug){
1529
1529
      fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
1532
1532
    errno = EPROTO;
1533
1533
    goto mandos_end;
1534
1534
  }
1535
 
  
 
1535
 
1536
1536
  /* Read OpenPGP packet that contains the wanted password */
1537
 
  
 
1537
 
1538
1538
  if(debug){
1539
1539
    fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
1540
1540
                 " %s\n", ip);
1541
1541
  }
1542
 
  
 
1542
 
1543
1543
  while(true){
1544
 
    
 
1544
 
1545
1545
    if(quit_now){
1546
1546
      errno = EINTR;
1547
1547
      goto mandos_end;
1548
1548
    }
1549
 
    
 
1549
 
1550
1550
    buffer_capacity = incbuffer(&buffer, buffer_length,
1551
1551
                                buffer_capacity);
1552
1552
    if(buffer_capacity == 0){
1555
1555
      errno = e;
1556
1556
      goto mandos_end;
1557
1557
    }
1558
 
    
 
1558
 
1559
1559
    if(quit_now){
1560
1560
      errno = EINTR;
1561
1561
      goto mandos_end;
1562
1562
    }
1563
 
    
 
1563
 
1564
1564
    sret = gnutls_record_recv(session, buffer+buffer_length,
1565
1565
                              BUFFER_SIZE);
1566
1566
    if(sret == 0){
1574
1574
      case GNUTLS_E_REHANDSHAKE:
1575
1575
        do {
1576
1576
          ret = gnutls_handshake(session);
1577
 
          
 
1577
 
1578
1578
          if(quit_now){
1579
1579
            errno = EINTR;
1580
1580
            goto mandos_end;
1599
1599
      buffer_length += (size_t) sret;
1600
1600
    }
1601
1601
  }
1602
 
  
 
1602
 
1603
1603
  if(debug){
1604
1604
    fprintf_plus(stderr, "Closing TLS session\n");
1605
1605
  }
1606
 
  
 
1606
 
1607
1607
  if(quit_now){
1608
1608
    errno = EINTR;
1609
1609
    goto mandos_end;
1610
1610
  }
1611
 
  
 
1611
 
1612
1612
  do {
1613
1613
    ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
1614
1614
    if(quit_now){
1616
1616
      goto mandos_end;
1617
1617
    }
1618
1618
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
1619
 
  
 
1619
 
1620
1620
  if(buffer_length > 0){
1621
1621
    ssize_t decrypted_buffer_size;
1622
1622
    decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1623
1623
                                               &decrypted_buffer, mc);
1624
1624
    if(decrypted_buffer_size >= 0){
1625
 
      
 
1625
 
1626
1626
      clearerr(stdout);
1627
1627
      written = 0;
1628
1628
      while(written < (size_t) decrypted_buffer_size){
1630
1630
          errno = EINTR;
1631
1631
          goto mandos_end;
1632
1632
        }
1633
 
        
 
1633
 
1634
1634
        ret = (int)fwrite(decrypted_buffer + written, 1,
1635
1635
                          (size_t)decrypted_buffer_size - written,
1636
1636
                          stdout);
1658
1658
      retval = 0;
1659
1659
    }
1660
1660
  }
1661
 
  
 
1661
 
1662
1662
  /* Shutdown procedure */
1663
 
  
 
1663
 
1664
1664
 mandos_end:
1665
1665
  {
1666
1666
    if(route_added){
1708
1708
  if(r == NULL){
1709
1709
    return;
1710
1710
  }
1711
 
  
 
1711
 
1712
1712
  /* Called whenever a service has been resolved successfully or
1713
1713
     timed out */
1714
 
  
 
1714
 
1715
1715
  if(quit_now){
1716
1716
    avahi_s_service_resolver_free(r);
1717
1717
    return;
1718
1718
  }
1719
 
  
 
1719
 
1720
1720
  switch(event){
1721
1721
  default:
1722
1722
  case AVAHI_RESOLVER_FAILURE:
1726
1726
                 avahi_strerror(avahi_server_errno
1727
1727
                                (((mandos_context*)mc)->server)));
1728
1728
    break;
1729
 
    
 
1729
 
1730
1730
  case AVAHI_RESOLVER_FOUND:
1731
1731
    {
1732
1732
      char ip[AVAHI_ADDRESS_STR_MAX];
1768
1768
  if(b == NULL){
1769
1769
    return;
1770
1770
  }
1771
 
  
 
1771
 
1772
1772
  /* Called whenever a new services becomes available on the LAN or
1773
1773
     is removed from the LAN */
1774
 
  
 
1774
 
1775
1775
  if(quit_now){
1776
1776
    return;
1777
1777
  }
1778
 
  
 
1778
 
1779
1779
  switch(event){
1780
1780
  default:
1781
1781
  case AVAHI_BROWSER_FAILURE:
1782
 
    
 
1782
 
1783
1783
    fprintf_plus(stderr, "(Avahi browser) %s\n",
1784
1784
                 avahi_strerror(avahi_server_errno
1785
1785
                                (((mandos_context*)mc)->server)));
1786
1786
    avahi_simple_poll_quit(simple_poll);
1787
1787
    return;
1788
 
    
 
1788
 
1789
1789
  case AVAHI_BROWSER_NEW:
1790
1790
    /* We ignore the returned Avahi resolver object. In the callback
1791
1791
       function we free it. If the Avahi server is terminated before
1792
1792
       the callback function is called the Avahi server will free the
1793
1793
       resolver for us. */
1794
 
    
 
1794
 
1795
1795
    if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1796
1796
                                    interface, protocol, name, type,
1797
1797
                                    domain, protocol, 0,
1801
1801
                   avahi_strerror(avahi_server_errno
1802
1802
                                  (((mandos_context*)mc)->server)));
1803
1803
    break;
1804
 
    
 
1804
 
1805
1805
  case AVAHI_BROWSER_REMOVE:
1806
1806
    break;
1807
 
    
 
1807
 
1808
1808
  case AVAHI_BROWSER_ALL_FOR_NOW:
1809
1809
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
1810
1810
    if(debug){
1834
1834
bool get_flags(const char *ifname, struct ifreq *ifr){
1835
1835
  int ret;
1836
1836
  int old_errno;
1837
 
  
 
1837
 
1838
1838
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1839
1839
  if(s < 0){
1840
1840
    old_errno = errno;
1868
1868
 
1869
1869
__attribute__((nonnull, warn_unused_result))
1870
1870
bool good_flags(const char *ifname, const struct ifreq *ifr){
1871
 
  
 
1871
 
1872
1872
  /* Reject the loopback device */
1873
1873
  if(ifr->ifr_flags & IFF_LOOPBACK){
1874
1874
    if(debug){
1901
1901
    }
1902
1902
    return false;
1903
1903
  }
1904
 
  
 
1904
 
1905
1905
  /* Accept this device */
1906
1906
  if(debug){
1907
1907
    fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1919
1919
  if(if_entry->d_name[0] == '.'){
1920
1920
    return 0;
1921
1921
  }
1922
 
  
 
1922
 
1923
1923
  struct ifreq ifr;
1924
1924
  if(not get_flags(if_entry->d_name, &ifr)){
1925
1925
    if(debug){
1928
1928
    }
1929
1929
    return 0;
1930
1930
  }
1931
 
  
 
1931
 
1932
1932
  if(not good_flags(if_entry->d_name, &ifr)){
1933
1933
    return 0;
1934
1934
  }
1948
1948
    }
1949
1949
    return false;
1950
1950
  }
1951
 
  
 
1951
 
1952
1952
  return (bool)(ifr.ifr_flags & IFF_UP);
1953
1953
}
1954
1954
 
1965
1965
    }
1966
1966
    return false;
1967
1967
  }
1968
 
  
 
1968
 
1969
1969
  return (bool)(ifr.ifr_flags & IFF_RUNNING);
1970
1970
}
1971
1971
 
1987
1987
  int ret;
1988
1988
  size_t sret;
1989
1989
  struct stat st;
1990
 
  
 
1990
 
1991
1991
  if((direntry->d_name)[0] == '\0'){
1992
1992
    /* Empty name? */
1993
1993
    return 0;
1994
1994
  }
1995
 
  
 
1995
 
1996
1996
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1997
1997
                "abcdefghijklmnopqrstuvwxyz"
1998
1998
                "0123456789"
2005
2005
    }
2006
2006
    return 0;
2007
2007
  }
2008
 
  
 
2008
 
2009
2009
  ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
2010
2010
  if(ret == -1){
2011
2011
    if(debug){
2043
2043
  struct timespec now;
2044
2044
  struct timespec waited_time;
2045
2045
  intmax_t block_time;
2046
 
  
 
2046
 
2047
2047
  while(true){
2048
2048
    if(mc->current_server == NULL){
2049
2049
      if(debug){
2074
2074
      block_time = ((retry_interval
2075
2075
                     - ((intmax_t)waited_time.tv_sec * 1000))
2076
2076
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
2077
 
      
 
2077
 
2078
2078
      if(debug){
2079
2079
        fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
2080
2080
                     block_time);
2081
2081
      }
2082
 
      
 
2082
 
2083
2083
      if(block_time <= 0){
2084
2084
        ret = start_mandos_communication(mc->current_server->ip,
2085
2085
                                         mc->current_server->port,
2099
2099
        block_time = 0;         /* Call avahi to find new Mandos
2100
2100
                                   servers, but don't block */
2101
2101
      }
2102
 
      
 
2102
 
2103
2103
      ret = avahi_simple_poll_iterate(s, (int)block_time);
2104
2104
    }
2105
2105
    if(ret != 0){
2303
2303
    errno = old_errno;
2304
2304
    return ENXIO;
2305
2305
  }
2306
 
  
 
2306
 
2307
2307
  if(quit_now){
2308
2308
    errno = old_errno;
2309
2309
    return EINTR;
2310
2310
  }
2311
 
  
 
2311
 
2312
2312
  if(not interface_is_up(interface)){
2313
2313
    int ret_errno = 0;
2314
2314
    int ioctl_errno = 0;
2320
2320
      return ret_errno;
2321
2321
    }
2322
2322
    network.ifr_flags |= IFF_UP; /* set flag */
2323
 
    
 
2323
 
2324
2324
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2325
2325
    if(sd == -1){
2326
2326
      ret_errno = errno;
2328
2328
      errno = old_errno;
2329
2329
      return ret_errno;
2330
2330
    }
2331
 
    
 
2331
 
2332
2332
    if(quit_now){
2333
2333
      ret = close(sd);
2334
2334
      if(ret == -1){
2337
2337
      errno = old_errno;
2338
2338
      return EINTR;
2339
2339
    }
2340
 
    
 
2340
 
2341
2341
    if(debug){
2342
2342
      fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
2343
2343
                   interface);
2344
2344
    }
2345
 
    
 
2345
 
2346
2346
    /* Raise privileges */
2347
2347
    ret_errno = raise_privileges();
2348
2348
    if(ret_errno != 0){
2349
2349
      errno = ret_errno;
2350
2350
      perror_plus("Failed to raise privileges");
2351
2351
    }
2352
 
    
 
2352
 
2353
2353
#ifdef __linux__
2354
2354
    int ret_linux;
2355
2355
    bool restore_loglevel = false;
2374
2374
      }
2375
2375
    }
2376
2376
#endif  /* __linux__ */
2377
 
    
 
2377
 
2378
2378
    /* If raise_privileges() succeeded above */
2379
2379
    if(ret_errno == 0){
2380
2380
      /* Lower privileges */
2384
2384
        perror_plus("Failed to lower privileges");
2385
2385
      }
2386
2386
    }
2387
 
    
 
2387
 
2388
2388
    /* Close the socket */
2389
2389
    ret = close(sd);
2390
2390
    if(ret == -1){
2391
2391
      perror_plus("close");
2392
2392
    }
2393
 
    
 
2393
 
2394
2394
    if(ret_setflags == -1){
2395
2395
      errno = ioctl_errno;
2396
2396
      perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
2401
2401
    fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
2402
2402
                 interface);
2403
2403
  }
2404
 
  
 
2404
 
2405
2405
  /* Sleep checking until interface is running.
2406
2406
     Check every 0.25s, up to total time of delay */
2407
2407
  for(int i = 0; i < delay * 4; i++){
2414
2414
      perror_plus("nanosleep");
2415
2415
    }
2416
2416
  }
2417
 
  
 
2417
 
2418
2418
  errno = old_errno;
2419
2419
  return 0;
2420
2420
}
2440
2440
      return ret_errno;
2441
2441
    }
2442
2442
    network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2443
 
    
 
2443
 
2444
2444
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
2445
2445
    if(sd == -1){
2446
2446
      ret_errno = errno;
2448
2448
      errno = old_errno;
2449
2449
      return ret_errno;
2450
2450
    }
2451
 
    
 
2451
 
2452
2452
    if(debug){
2453
2453
      fprintf_plus(stderr, "Taking down interface \"%s\"\n",
2454
2454
                   interface);
2455
2455
    }
2456
 
    
 
2456
 
2457
2457
    /* Raise privileges */
2458
2458
    ret_errno = raise_privileges();
2459
2459
    if(ret_errno != 0){
2460
2460
      errno = ret_errno;
2461
2461
      perror_plus("Failed to raise privileges");
2462
2462
    }
2463
 
    
 
2463
 
2464
2464
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
2465
2465
    ioctl_errno = errno;
2466
 
    
 
2466
 
2467
2467
    /* If raise_privileges() succeeded above */
2468
2468
    if(ret_errno == 0){
2469
2469
      /* Lower privileges */
2473
2473
        perror_plus("Failed to lower privileges");
2474
2474
      }
2475
2475
    }
2476
 
    
 
2476
 
2477
2477
    /* Close the socket */
2478
2478
    int ret = close(sd);
2479
2479
    if(ret == -1){
2480
2480
      perror_plus("close");
2481
2481
    }
2482
 
    
 
2482
 
2483
2483
    if(ret_setflags == -1){
2484
2484
      errno = ioctl_errno;
2485
2485
      perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2490
2490
    fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
2491
2491
                 interface);
2492
2492
  }
2493
 
  
 
2493
 
2494
2494
  errno = old_errno;
2495
2495
  return 0;
2496
2496
}
2529
2529
#endif
2530
2530
  const char *dh_params_file = NULL;
2531
2531
  char *interfaces_hooks = NULL;
2532
 
  
 
2532
 
2533
2533
  bool gnutls_initialized = false;
2534
2534
  bool gpgme_initialized = false;
2535
2535
  float delay = 2.5f;
2536
2536
  double retry_interval = 10; /* 10s between trying a server and
2537
2537
                                 retrying the same server again */
2538
 
  
 
2538
 
2539
2539
  struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
2540
2540
  struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
2541
 
  
 
2541
 
2542
2542
  uid = getuid();
2543
2543
  gid = getgid();
2544
 
  
 
2544
 
2545
2545
  /* Lower any group privileges we might have, just to be safe */
2546
2546
  errno = 0;
2547
2547
  ret = setgid(gid);
2548
2548
  if(ret == -1){
2549
2549
    perror_plus("setgid");
2550
2550
  }
2551
 
  
 
2551
 
2552
2552
  /* Lower user privileges (temporarily) */
2553
2553
  errno = 0;
2554
2554
  ret = seteuid(uid);
2555
2555
  if(ret == -1){
2556
2556
    perror_plus("seteuid");
2557
2557
  }
2558
 
  
 
2558
 
2559
2559
  if(quit_now){
2560
2560
    goto end;
2561
2561
  }
2562
 
  
 
2562
 
2563
2563
  {
2564
2564
    struct argp_option options[] = {
2565
2565
      { .name = "debug", .key = 128,
2634
2634
        .doc = "Print program version", .group = -1 },
2635
2635
      { .name = NULL }
2636
2636
    };
2637
 
    
 
2637
 
2638
2638
    error_t parse_opt(int key, char *arg,
2639
2639
                      struct argp_state *state){
2640
2640
      errno = 0;
2722
2722
      }
2723
2723
      return errno;
2724
2724
    }
2725
 
    
 
2725
 
2726
2726
    struct argp argp = { .options = options, .parser = parse_opt,
2727
2727
                         .args_doc = "",
2728
2728
                         .doc = "Mandos client -- Get and decrypt"
2743
2743
      goto end;
2744
2744
    }
2745
2745
  }
2746
 
  
 
2746
 
2747
2747
  {
2748
2748
    /* Re-raise privileges */
2749
2749
    ret = raise_privileges();
2752
2752
      perror_plus("Failed to raise privileges");
2753
2753
    } else {
2754
2754
      struct stat st;
2755
 
      
 
2755
 
2756
2756
      /* Work around Debian bug #633582:
2757
2757
         <https://bugs.debian.org/633582> */
2758
2758
 
2776
2776
          close(seckey_fd);
2777
2777
        }
2778
2778
      }
2779
 
      
 
2779
 
2780
2780
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2781
2781
        int pubkey_fd = open(pubkey, O_RDONLY);
2782
2782
        if(pubkey_fd == -1){
2797
2797
          close(pubkey_fd);
2798
2798
        }
2799
2799
      }
2800
 
      
 
2800
 
2801
2801
      if(dh_params_file != NULL
2802
2802
         and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2803
2803
        int dhparams_fd = open(dh_params_file, O_RDONLY);
2819
2819
          close(dhparams_fd);
2820
2820
        }
2821
2821
      }
2822
 
      
 
2822
 
2823
2823
      /* Work around Debian bug #981302
2824
2824
         <https://bugs.debian.org/981302> */
2825
2825
      if(lstat("/dev/fd", &st) != 0 and errno == ENOENT){
2837
2837
      }
2838
2838
    }
2839
2839
  }
2840
 
  
 
2840
 
2841
2841
  /* Remove invalid interface names (except "none") */
2842
2842
  {
2843
2843
    char *interface = NULL;
2854
2854
      }
2855
2855
    }
2856
2856
  }
2857
 
  
 
2857
 
2858
2858
  /* Run network hooks */
2859
2859
  {
2860
2860
    if(mc.interfaces != NULL){
2869
2869
    run_network_hooks("start", interfaces_hooks != NULL ?
2870
2870
                      interfaces_hooks : "", delay);
2871
2871
  }
2872
 
  
 
2872
 
2873
2873
  if(not debug){
2874
2874
    avahi_set_log_function(empty_log);
2875
2875
  }
2876
 
  
 
2876
 
2877
2877
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
2878
2878
     from the signal handler */
2879
2879
  /* Initialize the pseudo-RNG for Avahi */
2885
2885
    exitcode = EX_UNAVAILABLE;
2886
2886
    goto end;
2887
2887
  }
2888
 
  
 
2888
 
2889
2889
  sigemptyset(&sigterm_action.sa_mask);
2890
2890
  ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2891
2891
  if(ret == -1){
2948
2948
      goto end;
2949
2949
    }
2950
2950
  }
2951
 
  
 
2951
 
2952
2952
  /* If no interfaces were specified, make a list */
2953
2953
  if(mc.interfaces == NULL){
2954
2954
    struct dirent **direntries = NULL;
2982
2982
      goto end;
2983
2983
    }
2984
2984
  }
2985
 
  
 
2985
 
2986
2986
  /* Bring up interfaces which are down, and remove any "none"s */
2987
2987
  {
2988
2988
    char *interface = NULL;
3024
3024
      fprintf_plus(stderr, "No interfaces were brought up\n");
3025
3025
    }
3026
3026
  }
3027
 
  
 
3027
 
3028
3028
  /* If we only got one interface, explicitly use only that one */
3029
3029
  if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
3030
3030
    if(debug){
3033
3033
    }
3034
3034
    if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
3035
3035
  }
3036
 
  
 
3036
 
3037
3037
  if(quit_now){
3038
3038
    goto end;
3039
3039
  }
3040
 
  
 
3040
 
3041
3041
#if GNUTLS_VERSION_NUMBER >= 0x030606
3042
3042
  ret = init_gnutls_global(tls_pubkey, tls_privkey, dh_params_file, &mc);
3043
3043
#elif GNUTLS_VERSION_NUMBER < 0x030600
3052
3052
  } else {
3053
3053
    gnutls_initialized = true;
3054
3054
  }
3055
 
  
 
3055
 
3056
3056
  if(quit_now){
3057
3057
    goto end;
3058
3058
  }
3059
 
  
 
3059
 
3060
3060
  /* Try /run/tmp before /tmp */
3061
3061
  tempdir = mkdtemp(run_tempdir);
3062
3062
  if(tempdir == NULL and errno == ENOENT){
3070
3070
    perror_plus("mkdtemp");
3071
3071
    goto end;
3072
3072
  }
3073
 
  
 
3073
 
3074
3074
  if(quit_now){
3075
3075
    goto end;
3076
3076
  }
3077
 
  
 
3077
 
3078
3078
  if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
3079
3079
    fprintf_plus(stderr, "init_gpgme failed\n");
3080
3080
    exitcode = EX_UNAVAILABLE;
3082
3082
  } else {
3083
3083
    gpgme_initialized = true;
3084
3084
  }
3085
 
  
 
3085
 
3086
3086
  if(quit_now){
3087
3087
    goto end;
3088
3088
  }
3089
 
  
 
3089
 
3090
3090
  if(connect_to != NULL){
3091
3091
    /* Connect directly, do not use Zeroconf */
3092
3092
    /* (Mainly meant for debugging) */
3093
3093
    char *address = strrchr(connect_to, ':');
3094
 
    
 
3094
 
3095
3095
    if(address == NULL){
3096
3096
      fprintf_plus(stderr, "No colon in address\n");
3097
3097
      exitcode = EX_USAGE;
3098
3098
      goto end;
3099
3099
    }
3100
 
    
 
3100
 
3101
3101
    if(quit_now){
3102
3102
      goto end;
3103
3103
    }
3104
 
    
 
3104
 
3105
3105
    in_port_t port;
3106
3106
    errno = 0;
3107
3107
    tmpmax = strtoimax(address+1, &tmp, 10);
3111
3111
      exitcode = EX_USAGE;
3112
3112
      goto end;
3113
3113
    }
3114
 
    
 
3114
 
3115
3115
    if(quit_now){
3116
3116
      goto end;
3117
3117
    }
3118
 
    
 
3118
 
3119
3119
    port = (in_port_t)tmpmax;
3120
3120
    *address = '\0';
3121
3121
    /* Colon in address indicates IPv6 */
3132
3132
      af = AF_INET;
3133
3133
    }
3134
3134
    address = connect_to;
3135
 
    
 
3135
 
3136
3136
    if(quit_now){
3137
3137
      goto end;
3138
3138
    }
3139
 
    
 
3139
 
3140
3140
    while(not quit_now){
3141
3141
      ret = start_mandos_communication(address, port, if_index, af,
3142
3142
                                       &mc);
3149
3149
      }
3150
3150
      sleep((unsigned int)retry_interval);
3151
3151
    }
3152
 
    
 
3152
 
3153
3153
    if(not quit_now){
3154
3154
      exitcode = EXIT_SUCCESS;
3155
3155
    }
3156
 
    
 
3156
 
3157
3157
    goto end;
3158
3158
  }
3159
 
  
 
3159
 
3160
3160
  if(quit_now){
3161
3161
    goto end;
3162
3162
  }
3163
 
  
 
3163
 
3164
3164
  {
3165
3165
    AvahiServerConfig config;
3166
3166
    /* Do not publish any local Zeroconf records */
3169
3169
    config.publish_addresses = 0;
3170
3170
    config.publish_workstation = 0;
3171
3171
    config.publish_domain = 0;
3172
 
    
 
3172
 
3173
3173
    /* Allocate a new server */
3174
3174
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
3175
3175
                                 &config, NULL, NULL, &ret);
3176
 
    
 
3176
 
3177
3177
    /* Free the Avahi configuration data */
3178
3178
    avahi_server_config_free(&config);
3179
3179
  }
3180
 
  
 
3180
 
3181
3181
  /* Check if creating the Avahi server object succeeded */
3182
3182
  if(mc.server == NULL){
3183
3183
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
3185
3185
    exitcode = EX_UNAVAILABLE;
3186
3186
    goto end;
3187
3187
  }
3188
 
  
 
3188
 
3189
3189
  if(quit_now){
3190
3190
    goto end;
3191
3191
  }
3192
 
  
 
3192
 
3193
3193
  /* Create the Avahi service browser */
3194
3194
  sb = avahi_s_service_browser_new(mc.server, if_index,
3195
3195
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
3201
3201
    exitcode = EX_UNAVAILABLE;
3202
3202
    goto end;
3203
3203
  }
3204
 
  
 
3204
 
3205
3205
  if(quit_now){
3206
3206
    goto end;
3207
3207
  }
3208
 
  
 
3208
 
3209
3209
  /* Run the main loop */
3210
 
  
 
3210
 
3211
3211
  if(debug){
3212
3212
    fprintf_plus(stderr, "Starting Avahi loop search\n");
3213
3213
  }
3214
 
  
 
3214
 
3215
3215
  ret = avahi_loop_with_timeout(simple_poll,
3216
3216
                                (int)(retry_interval * 1000), &mc);
3217
3217
  if(debug){
3218
3218
    fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
3219
3219
                 (ret == 0) ? "successfully" : "with error");
3220
3220
  }
3221
 
  
 
3221
 
3222
3222
 end:
3223
 
  
 
3223
 
3224
3224
  if(debug){
3225
3225
    if(signal_received){
3226
3226
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
3230
3230
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
3231
3231
    }
3232
3232
  }
3233
 
  
 
3233
 
3234
3234
  /* Cleanup things */
3235
3235
  free(mc.interfaces);
3236
 
  
 
3236
 
3237
3237
  if(sb != NULL)
3238
3238
    avahi_s_service_browser_free(sb);
3239
 
  
 
3239
 
3240
3240
  if(mc.server != NULL)
3241
3241
    avahi_server_free(mc.server);
3242
 
  
 
3242
 
3243
3243
  if(simple_poll != NULL)
3244
3244
    avahi_simple_poll_free(simple_poll);
3245
 
  
 
3245
 
3246
3246
  if(gnutls_initialized){
3247
3247
    gnutls_certificate_free_credentials(mc.cred);
3248
3248
    gnutls_dh_params_deinit(mc.dh_params);
3249
3249
  }
3250
 
  
 
3250
 
3251
3251
  if(gpgme_initialized){
3252
3252
    gpgme_release(mc.ctx);
3253
3253
  }
3254
 
  
 
3254
 
3255
3255
  /* Cleans up the circular linked list of Mandos servers the client
3256
3256
     has seen */
3257
3257
  if(mc.current_server != NULL){
3270
3270
      mc.current_server = next;
3271
3271
    }
3272
3272
  }
3273
 
  
 
3273
 
3274
3274
  /* Re-raise privileges */
3275
3275
  {
3276
3276
    ret = raise_privileges();
3278
3278
      errno = ret;
3279
3279
      perror_plus("Failed to raise privileges");
3280
3280
    } else {
3281
 
      
 
3281
 
3282
3282
      /* Run network hooks */
3283
3283
      run_network_hooks("stop", interfaces_hooks != NULL ?
3284
3284
                        interfaces_hooks : "", delay);
3285
 
      
 
3285
 
3286
3286
      /* Take down the network interfaces which were brought up */
3287
3287
      {
3288
3288
        char *interface = NULL;
3301
3301
        }
3302
3302
      }
3303
3303
    }
3304
 
    
 
3304
 
3305
3305
    ret = lower_privileges_permanently();
3306
3306
    if(ret != 0){
3307
3307
      errno = ret;
3308
3308
      perror_plus("Failed to lower privileges permanently");
3309
3309
    }
3310
3310
  }
3311
 
  
 
3311
 
3312
3312
  free(interfaces_to_take_down);
3313
3313
  free(interfaces_hooks);
3314
 
  
 
3314
 
3315
3315
  void clean_dir_at(int base, const char * const dirname,
3316
3316
                    uintmax_t level){
3317
3317
    struct dirent **direntries = NULL;
3338
3338
          if(errno == EISDIR){
3339
3339
              dret = unlinkat(dir_fd, direntries[i]->d_name,
3340
3340
                              AT_REMOVEDIR);
3341
 
          }         
 
3341
          }
3342
3342
          if((dret == -1) and (errno == ENOTEMPTY)
3343
3343
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3344
3344
                  == 0) and (level == 0)){
3353
3353
        }
3354
3354
        free(direntries[i]);
3355
3355
      }
3356
 
      
 
3356
 
3357
3357
      /* need to clean even if 0 because man page doesn't specify */
3358
3358
      free(direntries);
3359
3359
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
3365
3365
    }
3366
3366
    close(dir_fd);
3367
3367
  }
3368
 
  
 
3368
 
3369
3369
  /* Removes the GPGME temp directory and all files inside */
3370
3370
  if(tempdir != NULL){
3371
3371
    clean_dir_at(-1, tempdir, 0);
3372
3372
  }
3373
 
  
 
3373
 
3374
3374
  if(quit_now){
3375
3375
    sigemptyset(&old_sigterm_action.sa_mask);
3376
3376
    old_sigterm_action.sa_handler = SIG_DFL;
3389
3389
    }
3390
3390
    TEMP_FAILURE_RETRY(pause());
3391
3391
  }
3392
 
  
 
3392
 
3393
3393
  return exitcode;
3394
3394
}