/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 at bsnet
  • Date: 2010-08-10 19:08:24 UTC
  • mto: This revision was merged to the branch mainline in revision 419.
  • Revision ID: teddy@fukt.bsnet.se-20100810190824-5yquozxy4kh6py3f
* plugins.d/mandos-client.c: An empty interface name now means to
                             autodetect an interface; to specify no
                             particular interface, use "none".
  (sys_class_net): New global variable for the "/sys/class/net" path.
  (good_interface): New function to determine the suitability of an
                    interface.  Used by a scandir() call in main().
  (main): Changed default value for "interface" to the empty string.
          Moved "connect_to" to be a global variable.  Only take down
          and up interface if its name is not "none".
* plugins.d/mandos-client.xml (OPTIONS): Update documentation for the
                                         "--interface" option.

Show diffs side-by-side

added added

removed removed

Lines of Context:
44
44
#include <stdint.h>             /* uint16_t, uint32_t */
45
45
#include <stddef.h>             /* NULL, size_t, ssize_t */
46
46
#include <stdlib.h>             /* free(), EXIT_SUCCESS, EXIT_FAILURE,
47
 
                                   srand(), strtof() */
 
47
                                   srand(), strtof(), abort() */
48
48
#include <stdbool.h>            /* bool, false, true */
49
49
#include <string.h>             /* memset(), strcmp(), strlen(),
50
50
                                   strerror(), asprintf(), strcpy() */
72
72
                                */
73
73
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
74
74
                                   getuid(), getgid(), seteuid(),
75
 
                                   setgid() */
 
75
                                   setgid(), pause() */
76
76
#include <arpa/inet.h>          /* inet_pton(), htons */
77
77
#include <iso646.h>             /* not, or, and */
78
78
#include <argp.h>               /* struct argp_option, error_t, struct
125
125
static const char mandos_protocol_version[] = "1";
126
126
const char *argp_program_version = "mandos-client " VERSION;
127
127
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
128
static const char sys_class_net[] = "/sys/class/net";
 
129
char *connect_to = NULL;
128
130
 
129
131
/* Used for passing in values through the Avahi callback functions */
130
132
typedef struct {
142
144
                      .dh_bits = 1024, .priority = "SECURE256"
143
145
                      ":!CTYPE-X.509:+CTYPE-OPENPGP" };
144
146
 
 
147
sig_atomic_t quit_now = 0;
 
148
int signal_received = 0;
 
149
 
145
150
/*
146
151
 * Make additional room in "buffer" for at least BUFFER_SIZE more
147
152
 * bytes. "buffer_capacity" is how much is currently allocated,
476
481
  /* GnuTLS session creation */
477
482
  do {
478
483
    ret = gnutls_init(session, GNUTLS_SERVER);
 
484
    if(quit_now){
 
485
      return -1;
 
486
    }
479
487
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
480
488
  if(ret != GNUTLS_E_SUCCESS){
481
489
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
486
494
    const char *err;
487
495
    do {
488
496
      ret = gnutls_priority_set_direct(*session, mc.priority, &err);
 
497
      if(quit_now){
 
498
        gnutls_deinit(*session);
 
499
        return -1;
 
500
      }
489
501
    } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
490
502
    if(ret != GNUTLS_E_SUCCESS){
491
503
      fprintf(stderr, "Syntax error at: %s\n", err);
499
511
  do {
500
512
    ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
501
513
                                 mc.cred);
 
514
    if(quit_now){
 
515
      gnutls_deinit(*session);
 
516
      return -1;
 
517
    }
502
518
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
503
519
  if(ret != GNUTLS_E_SUCCESS){
504
520
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
519
535
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
520
536
                      __attribute__((unused)) const char *txt){}
521
537
 
522
 
sig_atomic_t quit_now = 0;
523
 
int signal_received = 0;
524
 
 
525
538
/* Called when a Mandos server is found */
526
539
static int start_mandos_communication(const char *ip, uint16_t port,
527
540
                                      AvahiIfIndex if_index,
533
546
    struct sockaddr_in6 in6;
534
547
  } to;
535
548
  char *buffer = NULL;
536
 
  char *decrypted_buffer;
 
549
  char *decrypted_buffer = NULL;
537
550
  size_t buffer_length = 0;
538
551
  size_t buffer_capacity = 0;
539
552
  size_t written;
540
 
  int retval = 0;
 
553
  int retval = -1;
541
554
  gnutls_session_t session;
542
555
  int pf;                       /* Protocol family */
543
556
  
570
583
  tcp_sd = socket(pf, SOCK_STREAM, 0);
571
584
  if(tcp_sd < 0){
572
585
    perror("socket");
573
 
    retval = -1;
574
586
    goto mandos_end;
575
587
  }
576
588
  
588
600
  }
589
601
  if(ret < 0 ){
590
602
    perror("inet_pton");
591
 
    retval = -1;
592
603
    goto mandos_end;
593
604
  }
594
605
  if(ret == 0){
595
606
    fprintf(stderr, "Bad address: %s\n", ip);
596
 
    retval = -1;
597
607
    goto mandos_end;
598
608
  }
599
609
  if(af == AF_INET6){
607
617
      if(if_index == AVAHI_IF_UNSPEC){
608
618
        fprintf(stderr, "An IPv6 link-local address is incomplete"
609
619
                " without a network interface\n");
610
 
        retval = -1;
611
620
        goto mandos_end;
612
621
      }
613
622
      /* Set the network interface number as scope */
666
675
  }
667
676
  if(ret < 0){
668
677
    perror("connect");
669
 
    retval = -1;
670
678
    goto mandos_end;
671
679
  }
672
680
  
682
690
                                   out_size - written));
683
691
    if(ret == -1){
684
692
      perror("write");
685
 
      retval = -1;
686
693
      goto mandos_end;
687
694
    }
688
695
    written += (size_t)ret;
728
735
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
729
736
      gnutls_perror(ret);
730
737
    }
731
 
    retval = -1;
732
738
    goto mandos_end;
733
739
  }
734
740
  
749
755
                                   buffer_capacity);
750
756
    if(buffer_capacity == 0){
751
757
      perror("incbuffer");
752
 
      retval = -1;
753
758
      goto mandos_end;
754
759
    }
755
760
    
778
783
        if(ret < 0){
779
784
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
780
785
          gnutls_perror(ret);
781
 
          retval = -1;
782
786
          goto mandos_end;
783
787
        }
784
788
        break;
785
789
      default:
786
790
        fprintf(stderr, "Unknown error while reading data from"
787
791
                " encrypted session with Mandos server\n");
788
 
        retval = -1;
789
792
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
790
793
        goto mandos_end;
791
794
      }
802
805
    goto mandos_end;
803
806
  }
804
807
  
805
 
  gnutls_bye(session, GNUTLS_SHUT_RDWR);
806
 
  
807
 
  if(quit_now){
808
 
    goto mandos_end;
809
 
  }
 
808
  do {
 
809
    ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
810
    if(quit_now){
 
811
      goto mandos_end;
 
812
    }
 
813
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
810
814
  
811
815
  if(buffer_length > 0){
812
816
    ssize_t decrypted_buffer_size;
829
833
            fprintf(stderr, "Error writing encrypted data: %s\n",
830
834
                    strerror(errno));
831
835
          }
832
 
          retval = -1;
833
 
          break;
 
836
          goto mandos_end;
834
837
        }
835
838
        written += (size_t)ret;
836
839
      }
837
 
      free(decrypted_buffer);
838
 
    } else {
839
 
      retval = -1;
 
840
      retval = 0;
840
841
    }
841
 
  } else {
842
 
    retval = -1;
843
842
  }
844
843
  
845
844
  /* Shutdown procedure */
846
845
  
847
846
 mandos_end:
 
847
  free(decrypted_buffer);
848
848
  free(buffer);
849
849
  if(tcp_sd >= 0){
850
850
    ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
976
976
  errno = old_errno;
977
977
}
978
978
 
 
979
/* 
 
980
 * This function determines if a directory entry in /sys/class/net
 
981
 * corresponds to an acceptable network device.
 
982
 * (This function is passed to scandir(3) as a filter function.)
 
983
 */
 
984
int good_interface(const struct dirent *if_entry){
 
985
  ssize_t ssret;
 
986
  char *flagname = NULL;
 
987
  int ret = asprintf(&flagname, "%s/%s/flags", sys_class_net,
 
988
                     if_entry->d_name);
 
989
  if(ret < 0){
 
990
    perror("asprintf");
 
991
    return 0;
 
992
  }
 
993
  if(if_entry->d_name[0] == '.'){
 
994
    return 0;
 
995
  }
 
996
  int flags_fd = (int)TEMP_FAILURE_RETRY(open(flagname, O_RDONLY));
 
997
  if(flags_fd == -1){
 
998
    perror("open");
 
999
    return 0;
 
1000
  }
 
1001
  typedef short ifreq_flags;    /* ifreq.ifr_flags in netdevice(7) */
 
1002
  /* read line from flags_fd */
 
1003
  ssize_t to_read = (sizeof(ifreq_flags)*2)+3; /* "0x1003\n" */
 
1004
  char *flagstring = malloc((size_t)to_read);
 
1005
  if(flagstring == NULL){
 
1006
    perror("malloc");
 
1007
    close(flags_fd);
 
1008
    return 0;
 
1009
  }
 
1010
  while(to_read > 0){
 
1011
    ssret = (ssize_t)TEMP_FAILURE_RETRY(read(flags_fd, flagstring,
 
1012
                                             (size_t)to_read));
 
1013
    if(ssret == -1){
 
1014
      perror("read");
 
1015
      free(flagstring);
 
1016
      close(flags_fd);
 
1017
      return 0;
 
1018
    }
 
1019
    to_read -= ssret;
 
1020
    if(ssret == 0){
 
1021
      break;
 
1022
    }
 
1023
  }
 
1024
  close(flags_fd);
 
1025
  intmax_t tmpmax;
 
1026
  char *tmp;
 
1027
  errno = 0;
 
1028
  tmpmax = strtoimax(flagstring, &tmp, 0);
 
1029
  if(errno != 0 or tmp == flagstring or (*tmp != '\0'
 
1030
                                         and not (isspace(*tmp)))
 
1031
     or tmpmax != (ifreq_flags)tmpmax){
 
1032
    free(flagstring);
 
1033
    return 0;
 
1034
  }
 
1035
  free(flagstring);
 
1036
  ifreq_flags flags = (ifreq_flags)tmpmax;
 
1037
  /* Reject the loopback device */
 
1038
  if(flags & IFF_LOOPBACK){
 
1039
    return 0;
 
1040
  }
 
1041
  /* Accept point-to-point devices only if connect_to is specified */
 
1042
  if(connect_to != NULL and (flags & IFF_POINTOPOINT)){
 
1043
    return 1;
 
1044
  }
 
1045
  /* Otherwise, reject non-broadcast-capable devices */
 
1046
  if(not (flags & IFF_BROADCAST)){
 
1047
    return 0;
 
1048
  }
 
1049
  /* Accept this device */
 
1050
  return 1;
 
1051
}
 
1052
 
979
1053
int main(int argc, char *argv[]){
980
1054
  AvahiSServiceBrowser *sb = NULL;
981
1055
  int error;
983
1057
  intmax_t tmpmax;
984
1058
  char *tmp;
985
1059
  int exitcode = EXIT_SUCCESS;
986
 
  const char *interface = "eth0";
 
1060
  const char *interface = "";
987
1061
  struct ifreq network;
988
1062
  int sd = -1;
989
1063
  bool take_down_interface = false;
990
1064
  uid_t uid;
991
1065
  gid_t gid;
992
 
  char *connect_to = NULL;
993
1066
  char tempdir[] = "/tmp/mandosXXXXXX";
994
1067
  bool tempdir_created = false;
995
1068
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1125
1198
  if(not debug){
1126
1199
    avahi_set_log_function(empty_log);
1127
1200
  }
 
1201
 
 
1202
  if(interface[0] == '\0'){
 
1203
    struct dirent **direntries;
 
1204
    ret = scandir(sys_class_net, &direntries, good_interface,
 
1205
                  alphasort);
 
1206
    if(ret >= 1){
 
1207
      /* Pick the first good interface */
 
1208
      interface = strdup(direntries[0]->d_name);
 
1209
      if(interface == NULL){
 
1210
        perror("malloc");
 
1211
        free(direntries);
 
1212
        exitcode = EXIT_FAILURE;
 
1213
        goto end;
 
1214
      }
 
1215
      free(direntries);
 
1216
    } else {
 
1217
      free(direntries);
 
1218
      fprintf(stderr, "Could not find a network interface\n");
 
1219
      exitcode = EXIT_FAILURE;
 
1220
      goto end;
 
1221
    }
 
1222
  }
1128
1223
  
1129
1224
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
1130
1225
     from the signal handler */
1201
1296
  }
1202
1297
  
1203
1298
  /* If the interface is down, bring it up */
1204
 
  if(interface[0] != '\0'){
 
1299
  if(strcmp(interface, "none") != 0){
1205
1300
    if_index = (AvahiIfIndex) if_nametoindex(interface);
1206
1301
    if(if_index == 0){
1207
1302
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
1598
1693
  if(quit_now){
1599
1694
    sigemptyset(&old_sigterm_action.sa_mask);
1600
1695
    old_sigterm_action.sa_handler = SIG_DFL;
1601
 
    ret = sigaction(signal_received, &old_sigterm_action, NULL);
 
1696
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
 
1697
                                            &old_sigterm_action,
 
1698
                                            NULL));
1602
1699
    if(ret == -1){
1603
1700
      perror("sigaction");
1604
1701
    }
1605
 
    raise(signal_received);
 
1702
    do {
 
1703
      ret = raise(signal_received);
 
1704
    } while(ret != 0 and errno == EINTR);
 
1705
    if(ret != 0){
 
1706
      perror("raise");
 
1707
      abort();
 
1708
    }
 
1709
    TEMP_FAILURE_RETRY(pause());
1606
1710
  }
1607
1711
  
1608
1712
  return exitcode;