/mandos/release

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

« back to all changes in this revision

Viewing changes to plugins.d/mandos-client.c

merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
36
36
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
37
37
 
38
38
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
39
 
                                   stdout, ferror(), remove() */
 
39
                                   stdout, ferror(), sscanf(),
 
40
                                   remove() */
40
41
#include <stdint.h>             /* uint16_t, uint32_t */
41
42
#include <stddef.h>             /* NULL, size_t, ssize_t */
42
43
#include <stdlib.h>             /* free(), EXIT_SUCCESS, EXIT_FAILURE,
43
 
                                   srand(), strtof() */
 
44
                                   srand() */
44
45
#include <stdbool.h>            /* bool, false, true */
45
46
#include <string.h>             /* memset(), strcmp(), strlen(),
46
47
                                   strerror(), asprintf(), strcpy() */
55
56
#include <fcntl.h>              /* open() */
56
57
#include <dirent.h>             /* opendir(), struct dirent, readdir()
57
58
                                 */
58
 
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
59
 
                                   strtoimax() */
 
59
#include <inttypes.h>           /* PRIu16, intmax_t, SCNdMAX */
60
60
#include <assert.h>             /* assert() */
61
61
#include <errno.h>              /* perror(), errno */
62
62
#include <time.h>               /* nanosleep(), time() */
75
75
                                   argp_state, struct argp,
76
76
                                   argp_parse(), ARGP_KEY_ARG,
77
77
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
78
 
#include <signal.h>             /* sigemptyset(), sigaddset(),
79
 
                                   sigaction(), SIGTERM, sigaction,
80
 
                                   sig_atomic_t */
81
 
 
82
78
#ifdef __linux__
83
79
#include <sys/klog.h>           /* klogctl() */
84
 
#endif  /* __linux__ */
 
80
#endif
85
81
 
86
82
/* Avahi */
87
83
/* All Avahi types, constants and functions
133
129
  gpgme_ctx_t ctx;
134
130
} mandos_context;
135
131
 
136
 
/* global context so signal handler can reach it*/
137
 
mandos_context mc = { .simple_poll = NULL, .server = NULL,
138
 
                      .dh_bits = 1024, .priority = "SECURE256"
139
 
                      ":!CTYPE-X.509:+CTYPE-OPENPGP" };
140
 
 
141
132
/*
142
 
 * Make additional room in "buffer" for at least BUFFER_SIZE more
143
 
 * bytes. "buffer_capacity" is how much is currently allocated,
144
 
 * "buffer_length" is how much is already used.
 
133
 * Make additional room in "buffer" for at least BUFFER_SIZE
 
134
 * additional bytes. "buffer_capacity" is how much is currently
 
135
 * allocated, "buffer_length" is how much is already used.
145
136
 */
146
137
size_t incbuffer(char **buffer, size_t buffer_length,
147
138
                  size_t buffer_capacity){
158
149
/* 
159
150
 * Initialize GPGME.
160
151
 */
161
 
static bool init_gpgme(const char *seckey,
 
152
static bool init_gpgme(mandos_context *mc, const char *seckey,
162
153
                       const char *pubkey, const char *tempdir){
163
154
  int ret;
164
155
  gpgme_error_t rc;
185
176
      return false;
186
177
    }
187
178
    
188
 
    rc = gpgme_op_import(mc.ctx, pgp_data);
 
179
    rc = gpgme_op_import(mc->ctx, pgp_data);
189
180
    if(rc != GPG_ERR_NO_ERROR){
190
181
      fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
191
182
              gpgme_strsource(rc), gpgme_strerror(rc));
201
192
  }
202
193
  
203
194
  if(debug){
204
 
    fprintf(stderr, "Initializing GPGME\n");
 
195
    fprintf(stderr, "Initialize gpgme\n");
205
196
  }
206
197
  
207
198
  /* Init GPGME */
234
225
  }
235
226
  
236
227
  /* Create new GPGME "context" */
237
 
  rc = gpgme_new(&(mc.ctx));
 
228
  rc = gpgme_new(&(mc->ctx));
238
229
  if(rc != GPG_ERR_NO_ERROR){
239
230
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
240
231
            gpgme_strsource(rc), gpgme_strerror(rc));
252
243
 * Decrypt OpenPGP data.
253
244
 * Returns -1 on error
254
245
 */
255
 
static ssize_t pgp_packet_decrypt(const char *cryptotext,
 
246
static ssize_t pgp_packet_decrypt(const mandos_context *mc,
 
247
                                  const char *cryptotext,
256
248
                                  size_t crypto_size,
257
249
                                  char **plaintext){
258
250
  gpgme_data_t dh_crypto, dh_plain;
285
277
  
286
278
  /* Decrypt data from the cryptotext data buffer to the plaintext
287
279
     data buffer */
288
 
  rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
 
280
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
289
281
  if(rc != GPG_ERR_NO_ERROR){
290
282
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
291
283
            gpgme_strsource(rc), gpgme_strerror(rc));
292
284
    plaintext_length = -1;
293
285
    if(debug){
294
286
      gpgme_decrypt_result_t result;
295
 
      result = gpgme_op_decrypt_result(mc.ctx);
 
287
      result = gpgme_op_decrypt_result(mc->ctx);
296
288
      if(result == NULL){
297
289
        fprintf(stderr, "gpgme_op_decrypt_result failed\n");
298
290
      } else {
390
382
  fprintf(stderr, "GnuTLS: %s", string);
391
383
}
392
384
 
393
 
static int init_gnutls_global(const char *pubkeyfilename,
 
385
static int init_gnutls_global(mandos_context *mc,
 
386
                              const char *pubkeyfilename,
394
387
                              const char *seckeyfilename){
395
388
  int ret;
396
389
  
414
407
  }
415
408
  
416
409
  /* OpenPGP credentials */
417
 
  gnutls_certificate_allocate_credentials(&mc.cred);
 
410
  gnutls_certificate_allocate_credentials(&mc->cred);
418
411
  if(ret != GNUTLS_E_SUCCESS){
419
412
    fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
420
413
                                                    from
432
425
  }
433
426
  
434
427
  ret = gnutls_certificate_set_openpgp_key_file
435
 
    (mc.cred, pubkeyfilename, seckeyfilename,
 
428
    (mc->cred, pubkeyfilename, seckeyfilename,
436
429
     GNUTLS_OPENPGP_FMT_BASE64);
437
430
  if(ret != GNUTLS_E_SUCCESS){
438
431
    fprintf(stderr,
444
437
  }
445
438
  
446
439
  /* GnuTLS server initialization */
447
 
  ret = gnutls_dh_params_init(&mc.dh_params);
 
440
  ret = gnutls_dh_params_init(&mc->dh_params);
448
441
  if(ret != GNUTLS_E_SUCCESS){
449
442
    fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
450
443
            " %s\n", safer_gnutls_strerror(ret));
451
444
    goto globalfail;
452
445
  }
453
 
  ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
 
446
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
454
447
  if(ret != GNUTLS_E_SUCCESS){
455
448
    fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
456
449
            safer_gnutls_strerror(ret));
457
450
    goto globalfail;
458
451
  }
459
452
  
460
 
  gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
 
453
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
461
454
  
462
455
  return 0;
463
456
  
464
457
 globalfail:
465
458
  
466
 
  gnutls_certificate_free_credentials(mc.cred);
 
459
  gnutls_certificate_free_credentials(mc->cred);
467
460
  gnutls_global_deinit();
468
 
  gnutls_dh_params_deinit(mc.dh_params);
 
461
  gnutls_dh_params_deinit(mc->dh_params);
469
462
  return -1;
470
463
}
471
464
 
472
 
static int init_gnutls_session(gnutls_session_t *session){
 
465
static int init_gnutls_session(mandos_context *mc,
 
466
                               gnutls_session_t *session){
473
467
  int ret;
474
468
  /* GnuTLS session creation */
475
469
  ret = gnutls_init(session, GNUTLS_SERVER);
480
474
  
481
475
  {
482
476
    const char *err;
483
 
    ret = gnutls_priority_set_direct(*session, mc.priority, &err);
 
477
    ret = gnutls_priority_set_direct(*session, mc->priority, &err);
484
478
    if(ret != GNUTLS_E_SUCCESS){
485
479
      fprintf(stderr, "Syntax error at: %s\n", err);
486
480
      fprintf(stderr, "GnuTLS error: %s\n",
491
485
  }
492
486
  
493
487
  ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
494
 
                               mc.cred);
 
488
                               mc->cred);
495
489
  if(ret != GNUTLS_E_SUCCESS){
496
490
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
497
491
            safer_gnutls_strerror(ret));
503
497
  gnutls_certificate_server_set_request(*session,
504
498
                                        GNUTLS_CERT_IGNORE);
505
499
  
506
 
  gnutls_dh_set_prime_bits(*session, mc.dh_bits);
 
500
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
507
501
  
508
502
  return 0;
509
503
}
515
509
/* Called when a Mandos server is found */
516
510
static int start_mandos_communication(const char *ip, uint16_t port,
517
511
                                      AvahiIfIndex if_index,
518
 
                                      int af){
 
512
                                      mandos_context *mc, int af){
519
513
  int ret, tcp_sd;
520
514
  ssize_t sret;
521
515
  union {
544
538
    return -1;
545
539
  }
546
540
  
547
 
  ret = init_gnutls_session(&session);
 
541
  ret = init_gnutls_session(mc, &session);
548
542
  if(ret != 0){
549
543
    return -1;
550
544
  }
739
733
  gnutls_bye(session, GNUTLS_SHUT_RDWR);
740
734
  
741
735
  if(buffer_length > 0){
742
 
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
 
736
    decrypted_buffer_size = pgp_packet_decrypt(mc, buffer,
743
737
                                               buffer_length,
744
738
                                               &decrypted_buffer);
745
739
    if(decrypted_buffer_size >= 0){
791
785
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
792
786
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
793
787
                             flags,
794
 
                             AVAHI_GCC_UNUSED void* userdata){
 
788
                             void* userdata){
 
789
  mandos_context *mc = userdata;
795
790
  assert(r);
796
791
  
797
792
  /* Called whenever a service has been resolved successfully or
802
797
  case AVAHI_RESOLVER_FAILURE:
803
798
    fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
804
799
            " of type '%s' in domain '%s': %s\n", name, type, domain,
805
 
            avahi_strerror(avahi_server_errno(mc.server)));
 
800
            avahi_strerror(avahi_server_errno(mc->server)));
806
801
    break;
807
802
    
808
803
  case AVAHI_RESOLVER_FOUND:
814
809
                PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
815
810
                ip, (intmax_t)interface, port);
816
811
      }
817
 
      int ret = start_mandos_communication(ip, port, interface,
 
812
      int ret = start_mandos_communication(ip, port, interface, mc,
818
813
                                           avahi_proto_to_af(proto));
819
814
      if(ret == 0){
820
 
        avahi_simple_poll_quit(mc.simple_poll);
 
815
        avahi_simple_poll_quit(mc->simple_poll);
821
816
      }
822
817
    }
823
818
  }
833
828
                            const char *domain,
834
829
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
835
830
                            flags,
836
 
                            AVAHI_GCC_UNUSED void* userdata){
 
831
                            void* userdata){
 
832
  mandos_context *mc = userdata;
837
833
  assert(b);
838
834
  
839
835
  /* Called whenever a new services becomes available on the LAN or
844
840
  case AVAHI_BROWSER_FAILURE:
845
841
    
846
842
    fprintf(stderr, "(Avahi browser) %s\n",
847
 
            avahi_strerror(avahi_server_errno(mc.server)));
848
 
    avahi_simple_poll_quit(mc.simple_poll);
 
843
            avahi_strerror(avahi_server_errno(mc->server)));
 
844
    avahi_simple_poll_quit(mc->simple_poll);
849
845
    return;
850
846
    
851
847
  case AVAHI_BROWSER_NEW:
854
850
       the callback function is called the Avahi server will free the
855
851
       resolver for us. */
856
852
    
857
 
    if(avahi_s_service_resolver_new(mc.server, interface, protocol,
858
 
                                    name, type, domain, protocol, 0,
859
 
                                    resolve_callback, NULL) == NULL)
 
853
    if(!(avahi_s_service_resolver_new(mc->server, interface,
 
854
                                       protocol, name, type, domain,
 
855
                                       AVAHI_PROTO_INET6, 0,
 
856
                                       resolve_callback, mc)))
860
857
      fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
861
 
              name, avahi_strerror(avahi_server_errno(mc.server)));
 
858
              name, avahi_strerror(avahi_server_errno(mc->server)));
862
859
    break;
863
860
    
864
861
  case AVAHI_BROWSER_REMOVE:
873
870
  }
874
871
}
875
872
 
876
 
sig_atomic_t quit_now = 0;
877
 
 
878
 
/* stop main loop after sigterm has been called */
879
 
static void handle_sigterm(__attribute__((unused)) int sig){
880
 
  if(quit_now){
881
 
    return;
882
 
  }
883
 
  quit_now = 1;
884
 
  int old_errno = errno;
885
 
  if(mc.simple_poll != NULL){
886
 
    avahi_simple_poll_quit(mc.simple_poll);
887
 
  }
888
 
  errno = old_errno;
889
 
}
890
 
 
891
873
int main(int argc, char *argv[]){
892
874
  AvahiSServiceBrowser *sb = NULL;
893
875
  int error;
894
876
  int ret;
895
877
  intmax_t tmpmax;
896
 
  char *tmp;
 
878
  int numchars;
897
879
  int exitcode = EXIT_SUCCESS;
898
880
  const char *interface = "eth0";
899
881
  struct ifreq network;
907
889
  const char *seckey = PATHDIR "/" SECKEY;
908
890
  const char *pubkey = PATHDIR "/" PUBKEY;
909
891
  
 
892
  mandos_context mc = { .simple_poll = NULL, .server = NULL,
 
893
                        .dh_bits = 1024, .priority = "SECURE256"
 
894
                        ":!CTYPE-X.509:+CTYPE-OPENPGP" };
910
895
  bool gnutls_initialized = false;
911
896
  bool gpgme_initialized = false;
912
 
  float delay = 2.5f;
913
 
  
914
 
  struct sigaction old_sigterm_action;
915
 
  struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
 
897
  double delay = 2.5;
916
898
  
917
899
  {
918
900
    struct argp_option options[] = {
970
952
        pubkey = arg;
971
953
        break;
972
954
      case 129:                 /* --dh-bits */
973
 
        errno = 0;
974
 
        tmpmax = strtoimax(arg, &tmp, 10);
975
 
        if(errno != 0 or tmp == arg or *tmp != '\0'
976
 
           or tmpmax != (typeof(mc.dh_bits))tmpmax){
 
955
        ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars);
 
956
        if(ret < 1 or tmpmax != (typeof(mc.dh_bits))tmpmax
 
957
           or arg[numchars] != '\0'){
977
958
          fprintf(stderr, "Bad number of DH bits\n");
978
959
          exit(EXIT_FAILURE);
979
960
        }
983
964
        mc.priority = arg;
984
965
        break;
985
966
      case 131:                 /* --delay */
986
 
        errno = 0;
987
 
        delay = strtof(arg, &tmp);
988
 
        if(errno != 0 or tmp == arg or *tmp != '\0'){
 
967
        ret = sscanf(arg, "%lf%n", &delay, &numchars);
 
968
        if(ret < 1 or arg[numchars] != '\0'){
989
969
          fprintf(stderr, "Bad delay\n");
990
970
          exit(EXIT_FAILURE);
991
971
        }
1012
992
    }
1013
993
  }
1014
994
  
1015
 
  if(not debug){
1016
 
    avahi_set_log_function(empty_log);
1017
 
  }
1018
 
  
1019
 
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
1020
 
     from the signal handler */
1021
 
  /* Initialize the pseudo-RNG for Avahi */
1022
 
  srand((unsigned int) time(NULL));
1023
 
  mc.simple_poll = avahi_simple_poll_new();
1024
 
  if(mc.simple_poll == NULL){
1025
 
    fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1026
 
    exitcode = EXIT_FAILURE;
1027
 
    goto end;
1028
 
  }
1029
 
  
1030
 
  sigemptyset(&sigterm_action.sa_mask);
1031
 
  ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1032
 
  if(ret == -1){
1033
 
    perror("sigaddset");
1034
 
    exitcode = EXIT_FAILURE;
1035
 
    goto end;
1036
 
  }
1037
 
  ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1038
 
  if(ret == -1){
1039
 
    perror("sigaddset");
1040
 
    exitcode = EXIT_FAILURE;
1041
 
    goto end;
1042
 
  }
1043
 
  ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1044
 
  if(ret == -1){
1045
 
    perror("sigaddset");
1046
 
    exitcode = EXIT_FAILURE;
1047
 
    goto end;
1048
 
  }
1049
 
  ret = sigaction(SIGTERM, &sigterm_action, &old_sigterm_action);
1050
 
  if(ret == -1){
1051
 
    perror("sigaction");
1052
 
    exitcode = EXIT_FAILURE;
1053
 
    goto end;
1054
 
  }  
1055
 
  
1056
995
  /* If the interface is down, bring it up */
1057
996
  if(interface[0] != '\0'){
1058
997
#ifdef __linux__
1064
1003
      restore_loglevel = false;
1065
1004
      perror("klogctl");
1066
1005
    }
1067
 
#endif  /* __linux__ */
 
1006
#endif
1068
1007
    
1069
1008
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1070
1009
    if(sd < 0){
1077
1016
          perror("klogctl");
1078
1017
        }
1079
1018
      }
1080
 
#endif  /* __linux__ */
 
1019
#endif
1081
1020
      goto end;
1082
1021
    }
1083
1022
    strcpy(network.ifr_name, interface);
1091
1030
          perror("klogctl");
1092
1031
        }
1093
1032
      }
1094
 
#endif  /* __linux__ */
 
1033
#endif
1095
1034
      exitcode = EXIT_FAILURE;
1096
1035
      goto end;
1097
1036
    }
1108
1047
            perror("klogctl");
1109
1048
          }
1110
1049
        }
1111
 
#endif  /* __linux__ */
 
1050
#endif
1112
1051
        goto end;
1113
1052
      }
1114
1053
    }
1138
1077
        perror("klogctl");
1139
1078
      }
1140
1079
    }
1141
 
#endif  /* __linux__ */
 
1080
#endif
1142
1081
  }
1143
1082
  
1144
1083
  uid = getuid();
1155
1094
    perror("setuid");
1156
1095
  }
1157
1096
  
1158
 
  ret = init_gnutls_global(pubkey, seckey);
 
1097
  ret = init_gnutls_global(&mc, pubkey, seckey);
1159
1098
  if(ret == -1){
1160
1099
    fprintf(stderr, "init_gnutls_global failed\n");
1161
1100
    exitcode = EXIT_FAILURE;
1170
1109
  }
1171
1110
  tempdir_created = true;
1172
1111
  
1173
 
  if(not init_gpgme(pubkey, seckey, tempdir)){
 
1112
  if(not init_gpgme(&mc, pubkey, seckey, tempdir)){
1174
1113
    fprintf(stderr, "init_gpgme failed\n");
1175
1114
    exitcode = EXIT_FAILURE;
1176
1115
    goto end;
1197
1136
      goto end;
1198
1137
    }
1199
1138
    uint16_t port;
1200
 
    errno = 0;
1201
 
    tmpmax = strtoimax(address+1, &tmp, 10);
1202
 
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
1203
 
       or tmpmax != (uint16_t)tmpmax){
 
1139
    ret = sscanf(address+1, "%" SCNdMAX "%n", &tmpmax, &numchars);
 
1140
    if(ret < 1 or tmpmax != (uint16_t)tmpmax
 
1141
       or address[numchars+1] != '\0'){
1204
1142
      fprintf(stderr, "Bad port number\n");
1205
1143
      exitcode = EXIT_FAILURE;
1206
1144
      goto end;
1215
1153
    } else {
1216
1154
      af = AF_INET;
1217
1155
    }
1218
 
    ret = start_mandos_communication(address, port, if_index, af);
 
1156
    ret = start_mandos_communication(address, port, if_index, &mc,
 
1157
                                     af);
1219
1158
    if(ret < 0){
1220
1159
      exitcode = EXIT_FAILURE;
1221
1160
    } else {
1223
1162
    }
1224
1163
    goto end;
1225
1164
  }
1226
 
    
 
1165
  
 
1166
  if(not debug){
 
1167
    avahi_set_log_function(empty_log);
 
1168
  }
 
1169
  
 
1170
  /* Initialize the pseudo-RNG for Avahi */
 
1171
  srand((unsigned int) time(NULL));
 
1172
  
 
1173
  /* Allocate main Avahi loop object */
 
1174
  mc.simple_poll = avahi_simple_poll_new();
 
1175
  if(mc.simple_poll == NULL){
 
1176
    fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
 
1177
    exitcode = EXIT_FAILURE;
 
1178
    goto end;
 
1179
  }
 
1180
  
1227
1181
  {
1228
1182
    AvahiServerConfig config;
1229
1183
    /* Do not publish any local Zeroconf records */
1252
1206
  
1253
1207
  /* Create the Avahi service browser */
1254
1208
  sb = avahi_s_service_browser_new(mc.server, if_index,
1255
 
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
1256
 
                                   NULL, 0, browse_callback, NULL);
 
1209
                                   AVAHI_PROTO_INET6, "_mandos._tcp",
 
1210
                                   NULL, 0, browse_callback, &mc);
1257
1211
  if(sb == NULL){
1258
1212
    fprintf(stderr, "Failed to create service browser: %s\n",
1259
1213
            avahi_strerror(avahi_server_errno(mc.server)));