/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: 2012-06-16 23:25:46 UTC
  • Revision ID: teddy@recompile.se-20120616232546-dcrkmnhd4yhq6mvd
* plugins.d/mandos-client (mandos_context): Moved to inside "main()".
                                            All users changed.
  (add_server): Take new "current_server" argument; all callers
                changed.
  (init_gpgme, pgp_packet_decrypt, init_gnutls_global,
  init_gnutls_session, start_mandos_communication,
  avahi_loop_with_timeout): Take new "mc" argument"; all callers
                            changed.
  (resolve_callback, browse_callback): Handle void pointer userdata as
                                       "mc", a pointer to
                                       mandos_context.  All callers
                                       changed.
  (main): New "mc" variable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
165
165
 
166
166
/* global so signal handler can reach it*/
167
167
AvahiSimplePoll *simple_poll;
168
 
mandos_context mc = { .server = NULL, .dh_bits = 1024,
169
 
                      .priority = "SECURE256:!CTYPE-X.509:"
170
 
                      "+CTYPE-OPENPGP", .current_server = NULL };
171
168
 
172
169
sig_atomic_t quit_now = 0;
173
170
int signal_received = 0;
210
207
 
211
208
/* Add server to set of servers to retry periodically */
212
209
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
213
 
                int af){
 
210
                int af, server **current_server){
214
211
  int ret;
215
212
  server *new_server = malloc(sizeof(server));
216
213
  if(new_server == NULL){
226
223
    return false;
227
224
  }
228
225
  /* Special case of first server */
229
 
  if (mc.current_server == NULL){
 
226
  if(*current_server == NULL){
230
227
    new_server->next = new_server;
231
228
    new_server->prev = new_server;
232
 
    mc.current_server = new_server;
 
229
    *current_server = new_server;
233
230
  /* Place the new server last in the list */
234
231
  } else {
235
 
    new_server->next = mc.current_server;
236
 
    new_server->prev = mc.current_server->prev;
 
232
    new_server->next = *current_server;
 
233
    new_server->prev = (*current_server)->prev;
237
234
    new_server->prev->next = new_server;
238
 
    mc.current_server->prev = new_server;
 
235
    (*current_server)->prev = new_server;
239
236
  }
240
 
  ret = clock_gettime(CLOCK_MONOTONIC, &mc.current_server->last_seen);
 
237
  ret = clock_gettime(CLOCK_MONOTONIC, &(*current_server)->last_seen);
241
238
  if(ret == -1){
242
239
    perror_plus("clock_gettime");
243
240
    return false;
249
246
 * Initialize GPGME.
250
247
 */
251
248
static bool init_gpgme(const char *seckey, const char *pubkey,
252
 
                       const char *tempdir){
 
249
                       const char *tempdir, mandos_context *mc){
253
250
  gpgme_error_t rc;
254
251
  gpgme_engine_info_t engine_info;
255
252
  
274
271
      return false;
275
272
    }
276
273
    
277
 
    rc = gpgme_op_import(mc.ctx, pgp_data);
 
274
    rc = gpgme_op_import(mc->ctx, pgp_data);
278
275
    if(rc != GPG_ERR_NO_ERROR){
279
276
      fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
280
277
                   gpgme_strsource(rc), gpgme_strerror(rc));
324
321
  }
325
322
  
326
323
  /* Create new GPGME "context" */
327
 
  rc = gpgme_new(&(mc.ctx));
 
324
  rc = gpgme_new(&(mc->ctx));
328
325
  if(rc != GPG_ERR_NO_ERROR){
329
326
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
330
327
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
345
342
 */
346
343
static ssize_t pgp_packet_decrypt(const char *cryptotext,
347
344
                                  size_t crypto_size,
348
 
                                  char **plaintext){
 
345
                                  char **plaintext,
 
346
                                  mandos_context *mc){
349
347
  gpgme_data_t dh_crypto, dh_plain;
350
348
  gpgme_error_t rc;
351
349
  ssize_t ret;
377
375
  
378
376
  /* Decrypt data from the cryptotext data buffer to the plaintext
379
377
     data buffer */
380
 
  rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
 
378
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
381
379
  if(rc != GPG_ERR_NO_ERROR){
382
380
    fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
383
381
                 gpgme_strsource(rc), gpgme_strerror(rc));
384
382
    plaintext_length = -1;
385
383
    if(debug){
386
384
      gpgme_decrypt_result_t result;
387
 
      result = gpgme_op_decrypt_result(mc.ctx);
 
385
      result = gpgme_op_decrypt_result(mc->ctx);
388
386
      if(result == NULL){
389
387
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
390
388
      } else {
481
479
}
482
480
 
483
481
static int init_gnutls_global(const char *pubkeyfilename,
484
 
                              const char *seckeyfilename){
 
482
                              const char *seckeyfilename,
 
483
                              mandos_context *mc){
485
484
  int ret;
486
485
  
487
486
  if(debug){
504
503
  }
505
504
  
506
505
  /* OpenPGP credentials */
507
 
  ret = gnutls_certificate_allocate_credentials(&mc.cred);
 
506
  ret = gnutls_certificate_allocate_credentials(&mc->cred);
508
507
  if(ret != GNUTLS_E_SUCCESS){
509
508
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
510
509
                 safer_gnutls_strerror(ret));
520
519
  }
521
520
  
522
521
  ret = gnutls_certificate_set_openpgp_key_file
523
 
    (mc.cred, pubkeyfilename, seckeyfilename,
 
522
    (mc->cred, pubkeyfilename, seckeyfilename,
524
523
     GNUTLS_OPENPGP_FMT_BASE64);
525
524
  if(ret != GNUTLS_E_SUCCESS){
526
525
    fprintf_plus(stderr,
532
531
  }
533
532
  
534
533
  /* GnuTLS server initialization */
535
 
  ret = gnutls_dh_params_init(&mc.dh_params);
 
534
  ret = gnutls_dh_params_init(&mc->dh_params);
536
535
  if(ret != GNUTLS_E_SUCCESS){
537
536
    fprintf_plus(stderr, "Error in GnuTLS DH parameter"
538
537
                 " initialization: %s\n",
539
538
                 safer_gnutls_strerror(ret));
540
539
    goto globalfail;
541
540
  }
542
 
  ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
 
541
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
543
542
  if(ret != GNUTLS_E_SUCCESS){
544
543
    fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
545
544
                 safer_gnutls_strerror(ret));
546
545
    goto globalfail;
547
546
  }
548
547
  
549
 
  gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
 
548
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
550
549
  
551
550
  return 0;
552
551
  
553
552
 globalfail:
554
553
  
555
 
  gnutls_certificate_free_credentials(mc.cred);
 
554
  gnutls_certificate_free_credentials(mc->cred);
556
555
  gnutls_global_deinit();
557
 
  gnutls_dh_params_deinit(mc.dh_params);
 
556
  gnutls_dh_params_deinit(mc->dh_params);
558
557
  return -1;
559
558
}
560
559
 
561
 
static int init_gnutls_session(gnutls_session_t *session){
 
560
static int init_gnutls_session(gnutls_session_t *session,
 
561
                               mandos_context *mc){
562
562
  int ret;
563
563
  /* GnuTLS session creation */
564
564
  do {
576
576
  {
577
577
    const char *err;
578
578
    do {
579
 
      ret = gnutls_priority_set_direct(*session, mc.priority, &err);
 
579
      ret = gnutls_priority_set_direct(*session, mc->priority, &err);
580
580
      if(quit_now){
581
581
        gnutls_deinit(*session);
582
582
        return -1;
593
593
  
594
594
  do {
595
595
    ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
596
 
                                 mc.cred);
 
596
                                 mc->cred);
597
597
    if(quit_now){
598
598
      gnutls_deinit(*session);
599
599
      return -1;
609
609
  /* ignore client certificate if any. */
610
610
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
611
611
  
612
 
  gnutls_dh_set_prime_bits(*session, mc.dh_bits);
 
612
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
613
613
  
614
614
  return 0;
615
615
}
621
621
/* Called when a Mandos server is found */
622
622
static int start_mandos_communication(const char *ip, in_port_t port,
623
623
                                      AvahiIfIndex if_index,
624
 
                                      int af){
 
624
                                      int af, mandos_context *mc){
625
625
  int ret, tcp_sd = -1;
626
626
  ssize_t sret;
627
627
  union {
657
657
    return -1;
658
658
  }
659
659
  
660
 
  ret = init_gnutls_session(&session);
 
660
  ret = init_gnutls_session(&session, mc);
661
661
  if(ret != 0){
662
662
    return -1;
663
663
  }
935
935
  if(buffer_length > 0){
936
936
    ssize_t decrypted_buffer_size;
937
937
    decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
938
 
                                               &decrypted_buffer);
 
938
                                               &decrypted_buffer, mc);
939
939
    if(decrypted_buffer_size >= 0){
940
940
      
941
941
      written = 0;
1002
1002
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
1003
1003
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
1004
1004
                             flags,
1005
 
                             AVAHI_GCC_UNUSED void* userdata){
 
1005
                             void* mc){
1006
1006
  if(r == NULL){
1007
1007
    return;
1008
1008
  }
1020
1020
    fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1021
1021
                 "'%s' of type '%s' in domain '%s': %s\n", name, type,
1022
1022
                 domain,
1023
 
                 avahi_strerror(avahi_server_errno(mc.server)));
 
1023
                 avahi_strerror(avahi_server_errno
 
1024
                                (((mandos_context*)mc)->server)));
1024
1025
    break;
1025
1026
    
1026
1027
  case AVAHI_RESOLVER_FOUND:
1034
1035
      }
1035
1036
      int ret = start_mandos_communication(ip, (in_port_t)port,
1036
1037
                                           interface,
1037
 
                                           avahi_proto_to_af(proto));
 
1038
                                           avahi_proto_to_af(proto),
 
1039
                                           mc);
1038
1040
      if(ret == 0){
1039
1041
        avahi_simple_poll_quit(simple_poll);
1040
1042
      } else {
1041
1043
        if(not add_server(ip, (in_port_t)port, interface,
1042
 
                          avahi_proto_to_af(proto))){
 
1044
                          avahi_proto_to_af(proto),
 
1045
                          &((mandos_context*)mc)->current_server)){
1043
1046
          fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1044
1047
                       " list\n", name);
1045
1048
        }
1058
1061
                            const char *domain,
1059
1062
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
1060
1063
                            flags,
1061
 
                            AVAHI_GCC_UNUSED void* userdata){
 
1064
                            void* mc){
1062
1065
  if(b == NULL){
1063
1066
    return;
1064
1067
  }
1075
1078
  case AVAHI_BROWSER_FAILURE:
1076
1079
    
1077
1080
    fprintf_plus(stderr, "(Avahi browser) %s\n",
1078
 
                 avahi_strerror(avahi_server_errno(mc.server)));
 
1081
                 avahi_strerror(avahi_server_errno
 
1082
                                (((mandos_context*)mc)->server)));
1079
1083
    avahi_simple_poll_quit(simple_poll);
1080
1084
    return;
1081
1085
    
1085
1089
       the callback function is called the Avahi server will free the
1086
1090
       resolver for us. */
1087
1091
    
1088
 
    if(avahi_s_service_resolver_new(mc.server, interface, protocol,
1089
 
                                    name, type, domain, protocol, 0,
1090
 
                                    resolve_callback, NULL) == NULL)
 
1092
    if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
 
1093
                                    interface, protocol, name, type,
 
1094
                                    domain, protocol, 0,
 
1095
                                    resolve_callback, mc) == NULL)
1091
1096
      fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1092
1097
                   " %s\n", name,
1093
 
                   avahi_strerror(avahi_server_errno(mc.server)));
 
1098
                   avahi_strerror(avahi_server_errno
 
1099
                                  (((mandos_context*)mc)->server)));
1094
1100
    break;
1095
1101
    
1096
1102
  case AVAHI_BROWSER_REMOVE:
1316
1322
  return 1;
1317
1323
}
1318
1324
 
1319
 
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval){
 
1325
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
 
1326
                            mandos_context *mc){
1320
1327
  int ret;
1321
1328
  struct timespec now;
1322
1329
  struct timespec waited_time;
1323
1330
  intmax_t block_time;
1324
1331
  
1325
1332
  while(true){
1326
 
    if(mc.current_server == NULL){
 
1333
    if(mc->current_server == NULL){
1327
1334
      if (debug){
1328
1335
        fprintf_plus(stderr, "Wait until first server is found."
1329
1336
                     " No timeout!\n");
1343
1350
      /* Calculating in ms how long time between now and server
1344
1351
         who we visted longest time ago. Now - last seen.  */
1345
1352
      waited_time.tv_sec = (now.tv_sec
1346
 
                            - mc.current_server->last_seen.tv_sec);
 
1353
                            - mc->current_server->last_seen.tv_sec);
1347
1354
      waited_time.tv_nsec = (now.tv_nsec
1348
 
                             - mc.current_server->last_seen.tv_nsec);
 
1355
                             - mc->current_server->last_seen.tv_nsec);
1349
1356
      /* total time is 10s/10,000ms.
1350
1357
         Converting to s from ms by dividing by 1,000,
1351
1358
         and ns to ms by dividing by 1,000,000. */
1359
1366
      }
1360
1367
      
1361
1368
      if(block_time <= 0){
1362
 
        ret = start_mandos_communication(mc.current_server->ip,
1363
 
                                         mc.current_server->port,
1364
 
                                         mc.current_server->if_index,
1365
 
                                         mc.current_server->af);
 
1369
        ret = start_mandos_communication(mc->current_server->ip,
 
1370
                                         mc->current_server->port,
 
1371
                                         mc->current_server->if_index,
 
1372
                                         mc->current_server->af, mc);
1366
1373
        if(ret == 0){
1367
 
          avahi_simple_poll_quit(simple_poll);
 
1374
          avahi_simple_poll_quit(s);
1368
1375
          return 0;
1369
1376
        }
1370
1377
        ret = clock_gettime(CLOCK_MONOTONIC,
1371
 
                            &mc.current_server->last_seen);
 
1378
                            &mc->current_server->last_seen);
1372
1379
        if(ret == -1){
1373
1380
          perror_plus("clock_gettime");
1374
1381
          return -1;
1375
1382
        }
1376
 
        mc.current_server = mc.current_server->next;
 
1383
        mc->current_server = mc->current_server->next;
1377
1384
        block_time = 0;         /* Call avahi to find new Mandos
1378
1385
                                   servers, but don't block */
1379
1386
      }
1746
1753
}
1747
1754
 
1748
1755
int main(int argc, char *argv[]){
 
1756
  mandos_context mc = { .server = NULL, .dh_bits = 1024,
 
1757
                        .priority = "SECURE256:!CTYPE-X.509:"
 
1758
                        "+CTYPE-OPENPGP", .current_server = NULL };
1749
1759
  AvahiSServiceBrowser *sb = NULL;
1750
1760
  error_t ret_errno;
1751
1761
  int ret;
2185
2195
    goto end;
2186
2196
  }
2187
2197
  
2188
 
  ret = init_gnutls_global(pubkey, seckey);
 
2198
  ret = init_gnutls_global(pubkey, seckey, &mc);
2189
2199
  if(ret == -1){
2190
2200
    fprintf_plus(stderr, "init_gnutls_global failed\n");
2191
2201
    exitcode = EX_UNAVAILABLE;
2208
2218
    goto end;
2209
2219
  }
2210
2220
  
2211
 
  if(not init_gpgme(pubkey, seckey, tempdir)){
 
2221
  if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2212
2222
    fprintf_plus(stderr, "init_gpgme failed\n");
2213
2223
    exitcode = EX_UNAVAILABLE;
2214
2224
    goto end;
2271
2281
    }
2272
2282
    
2273
2283
    while(not quit_now){
2274
 
      ret = start_mandos_communication(address, port, if_index, af);
 
2284
      ret = start_mandos_communication(address, port, if_index, af,
 
2285
                                       &mc);
2275
2286
      if(quit_now or ret == 0){
2276
2287
        break;
2277
2288
      }
2325
2336
  /* Create the Avahi service browser */
2326
2337
  sb = avahi_s_service_browser_new(mc.server, if_index,
2327
2338
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2328
 
                                   NULL, 0, browse_callback, NULL);
 
2339
                                   NULL, 0, browse_callback,
 
2340
                                   (void *)&mc);
2329
2341
  if(sb == NULL){
2330
2342
    fprintf_plus(stderr, "Failed to create service browser: %s\n",
2331
2343
                 avahi_strerror(avahi_server_errno(mc.server)));
2344
2356
  }
2345
2357
 
2346
2358
  ret = avahi_loop_with_timeout(simple_poll,
2347
 
                                (int)(retry_interval * 1000));
 
2359
                                (int)(retry_interval * 1000), &mc);
2348
2360
  if(debug){
2349
2361
    fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2350
2362
                 (ret == 0) ? "successfully" : "with error");