/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: 2018-08-15 09:26:02 UTC
  • Revision ID: teddy@recompile.se-20180815092602-xoyb5s6gf8376i7u
mandos-client: Set system clock if necessary

* plugins.d/mandos-client.c (init_gpgme/import_key): If the system
  clock is not set, or set to january 1970, set the system clock to
  the more plausible value that is the mtime of the key file.  This is
  required by GnuPG to be able to import the keys.  (We can't pass the
  --ignore-time-conflict or the --ignore-valid-from options though
  GPGME.)

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
11
 * Everything else is
12
 
 * Copyright © 2008-2015 Teddy Hogeborn
13
 
 * Copyright © 2008-2015 Björn Påhlsson
14
 
 * 
15
 
 * This program is free software: you can redistribute it and/or
16
 
 * modify it under the terms of the GNU General Public License as
17
 
 * published by the Free Software Foundation, either version 3 of the
18
 
 * License, or (at your option) any later version.
19
 
 * 
20
 
 * This program is distributed in the hope that it will be useful, but
 
12
 * Copyright © 2008-2018 Teddy Hogeborn
 
13
 * Copyright © 2008-2018 Björn Påhlsson
 
14
 * 
 
15
 * This file is part of Mandos.
 
16
 * 
 
17
 * Mandos is free software: you can redistribute it and/or modify it
 
18
 * under the terms of the GNU General Public License as published by
 
19
 * the Free Software Foundation, either version 3 of the License, or
 
20
 * (at your option) any later version.
 
21
 * 
 
22
 * Mandos is distributed in the hope that it will be useful, but
21
23
 * WITHOUT ANY WARRANTY; without even the implied warranty of
22
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23
25
 * General Public License for more details.
24
26
 * 
25
27
 * You should have received a copy of the GNU General Public License
26
 
 * along with this program.  If not, see
27
 
 * <http://www.gnu.org/licenses/>.
 
28
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
28
29
 * 
29
30
 * Contact the authors at <mandos@recompile.se>.
30
31
 */
46
47
#include <stdlib.h>             /* free(), EXIT_SUCCESS, srand(),
47
48
                                   strtof(), abort() */
48
49
#include <stdbool.h>            /* bool, false, true */
49
 
#include <string.h>             /* memset(), strcmp(), strlen(),
50
 
                                   strerror(), asprintf(), strcpy() */
 
50
#include <string.h>             /* strcmp(), strlen(), strerror(),
 
51
                                   asprintf(), strncpy(), strsignal()
 
52
                                */
51
53
#include <sys/ioctl.h>          /* ioctl */
52
54
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
53
55
                                   sockaddr_in6, PF_INET6,
57
59
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
58
60
                                   inet_pton(), connect(),
59
61
                                   getnameinfo() */
60
 
#include <fcntl.h>              /* open(), unlinkat() */
 
62
#include <fcntl.h>              /* open(), unlinkat(), AT_REMOVEDIR */
61
63
#include <dirent.h>             /* opendir(), struct dirent, readdir()
62
64
                                 */
63
65
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
64
66
                                   strtoimax() */
65
 
#include <errno.h>              /* perror(), errno,
 
67
#include <errno.h>              /* perror(), errno, EINTR, EINVAL,
 
68
                                   EAI_SYSTEM, ENETUNREACH,
 
69
                                   EHOSTUNREACH, ECONNREFUSED, EPROTO,
 
70
                                   EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
 
71
                                   ENOTEMPTY,
66
72
                                   program_invocation_short_name */
67
73
#include <time.h>               /* nanosleep(), time(), sleep() */
68
74
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
266
272
  return true;
267
273
}
268
274
 
 
275
/* Set effective uid to 0, return errno */
 
276
__attribute__((warn_unused_result))
 
277
int raise_privileges(void){
 
278
  int old_errno = errno;
 
279
  int ret = 0;
 
280
  if(seteuid(0) == -1){
 
281
    ret = errno;
 
282
  }
 
283
  errno = old_errno;
 
284
  return ret;
 
285
}
 
286
 
 
287
/* Set effective and real user ID to 0.  Return errno. */
 
288
__attribute__((warn_unused_result))
 
289
int raise_privileges_permanently(void){
 
290
  int old_errno = errno;
 
291
  int ret = raise_privileges();
 
292
  if(ret != 0){
 
293
    errno = old_errno;
 
294
    return ret;
 
295
  }
 
296
  if(setuid(0) == -1){
 
297
    ret = errno;
 
298
  }
 
299
  errno = old_errno;
 
300
  return ret;
 
301
}
 
302
 
 
303
/* Set effective user ID to unprivileged saved user ID */
 
304
__attribute__((warn_unused_result))
 
305
int lower_privileges(void){
 
306
  int old_errno = errno;
 
307
  int ret = 0;
 
308
  if(seteuid(uid) == -1){
 
309
    ret = errno;
 
310
  }
 
311
  errno = old_errno;
 
312
  return ret;
 
313
}
 
314
 
 
315
/* Lower privileges permanently */
 
316
__attribute__((warn_unused_result))
 
317
int lower_privileges_permanently(void){
 
318
  int old_errno = errno;
 
319
  int ret = 0;
 
320
  if(setuid(uid) == -1){
 
321
    ret = errno;
 
322
  }
 
323
  errno = old_errno;
 
324
  return ret;
 
325
}
 
326
 
269
327
/* 
270
328
 * Initialize GPGME.
271
329
 */
291
349
      return false;
292
350
    }
293
351
    
 
352
    /* Workaround for systems without a real-time clock; see also
 
353
       Debian bug #894495: <https://bugs.debian.org/894495> */
 
354
    do {
 
355
      {
 
356
        time_t currtime = time(NULL);
 
357
        if(currtime != (time_t)-1){
 
358
          struct tm tm;
 
359
          if(gmtime_r(&currtime, &tm) == NULL) {
 
360
            perror_plus("gmtime_r");
 
361
            break;
 
362
          }
 
363
          if(tm.tm_year != 70 or tm.tm_mon != 0){
 
364
            break;
 
365
          }
 
366
          if(debug){
 
367
            fprintf_plus(stderr, "System clock is January 1970");
 
368
          }
 
369
        } else {
 
370
          if(debug){
 
371
            fprintf_plus(stderr, "System clock is invalid");
 
372
          }
 
373
        }
 
374
      }
 
375
      struct stat keystat;
 
376
      ret = fstat(fd, &keystat);
 
377
      if(ret != 0){
 
378
        perror_plus("fstat");
 
379
        break;
 
380
      }
 
381
      ret = raise_privileges();
 
382
      if(ret != 0){
 
383
        errno = ret;
 
384
        perror_plus("Failed to raise privileges");
 
385
        break;
 
386
      }
 
387
      if(debug){
 
388
        fprintf_plus(stderr,
 
389
                     "Setting system clock to key file mtime");
 
390
      }
 
391
      time_t keytime = keystat.st_mtim.tv_sec;
 
392
      if(stime(&keytime) != 0){
 
393
        perror_plus("stime");
 
394
      }
 
395
      ret = lower_privileges();
 
396
      if(ret != 0){
 
397
        errno = ret;
 
398
        perror_plus("Failed to lower privileges");
 
399
      }
 
400
    } while(false);
 
401
 
294
402
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
295
403
    if(rc != GPG_ERR_NO_ERROR){
296
404
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
304
412
                   gpgme_strsource(rc), gpgme_strerror(rc));
305
413
      return false;
306
414
    }
 
415
    {
 
416
      gpgme_import_result_t import_result
 
417
        = gpgme_op_import_result(mc->ctx);
 
418
      if((import_result->imported < 1
 
419
          or import_result->not_imported > 0)
 
420
         and import_result->unchanged == 0){
 
421
        fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
 
422
        fprintf_plus(stderr,
 
423
                     "The total number of considered keys: %d\n",
 
424
                     import_result->considered);
 
425
        fprintf_plus(stderr,
 
426
                     "The number of keys without user ID: %d\n",
 
427
                     import_result->no_user_id);
 
428
        fprintf_plus(stderr,
 
429
                     "The total number of imported keys: %d\n",
 
430
                     import_result->imported);
 
431
        fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
 
432
                     import_result->imported_rsa);
 
433
        fprintf_plus(stderr, "The number of unchanged keys: %d\n",
 
434
                     import_result->unchanged);
 
435
        fprintf_plus(stderr, "The number of new user IDs: %d\n",
 
436
                     import_result->new_user_ids);
 
437
        fprintf_plus(stderr, "The number of new sub keys: %d\n",
 
438
                     import_result->new_sub_keys);
 
439
        fprintf_plus(stderr, "The number of new signatures: %d\n",
 
440
                     import_result->new_signatures);
 
441
        fprintf_plus(stderr, "The number of new revocations: %d\n",
 
442
                     import_result->new_revocations);
 
443
        fprintf_plus(stderr,
 
444
                     "The total number of secret keys read: %d\n",
 
445
                     import_result->secret_read);
 
446
        fprintf_plus(stderr,
 
447
                     "The number of imported secret keys: %d\n",
 
448
                     import_result->secret_imported);
 
449
        fprintf_plus(stderr,
 
450
                     "The number of unchanged secret keys: %d\n",
 
451
                     import_result->secret_unchanged);
 
452
        fprintf_plus(stderr, "The number of keys not imported: %d\n",
 
453
                     import_result->not_imported);
 
454
        for(gpgme_import_status_t import_status
 
455
              = import_result->imports;
 
456
            import_status != NULL;
 
457
            import_status = import_status->next){
 
458
          fprintf_plus(stderr, "Import status for key: %s\n",
 
459
                       import_status->fpr);
 
460
          if(import_status->result != GPG_ERR_NO_ERROR){
 
461
            fprintf_plus(stderr, "Import result: %s: %s\n",
 
462
                         gpgme_strsource(import_status->result),
 
463
                         gpgme_strerror(import_status->result));
 
464
          }
 
465
          fprintf_plus(stderr, "Key status:\n");
 
466
          fprintf_plus(stderr,
 
467
                       import_status->status & GPGME_IMPORT_NEW
 
468
                       ? "The key was new.\n"
 
469
                       : "The key was not new.\n");
 
470
          fprintf_plus(stderr,
 
471
                       import_status->status & GPGME_IMPORT_UID
 
472
                       ? "The key contained new user IDs.\n"
 
473
                       : "The key did not contain new user IDs.\n");
 
474
          fprintf_plus(stderr,
 
475
                       import_status->status & GPGME_IMPORT_SIG
 
476
                       ? "The key contained new signatures.\n"
 
477
                       : "The key did not contain new signatures.\n");
 
478
          fprintf_plus(stderr,
 
479
                       import_status->status & GPGME_IMPORT_SUBKEY
 
480
                       ? "The key contained new sub keys.\n"
 
481
                       : "The key did not contain new sub keys.\n");
 
482
          fprintf_plus(stderr,
 
483
                       import_status->status & GPGME_IMPORT_SECRET
 
484
                       ? "The key contained a secret key.\n"
 
485
                       : "The key did not contain a secret key.\n");
 
486
        }
 
487
        return false;
 
488
      }
 
489
    }
307
490
    
308
 
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
491
    ret = close(fd);
309
492
    if(ret == -1){
310
493
      perror_plus("close");
311
494
    }
350
533
  /* Create new GPGME "context" */
351
534
  rc = gpgme_new(&(mc->ctx));
352
535
  if(rc != GPG_ERR_NO_ERROR){
353
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
354
 
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
355
 
                 gpgme_strerror(rc));
 
536
    fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
 
537
                 gpgme_strsource(rc), gpgme_strerror(rc));
356
538
    return false;
357
539
  }
358
540
  
394
576
  /* Create new empty GPGME data buffer for the plaintext */
395
577
  rc = gpgme_data_new(&dh_plain);
396
578
  if(rc != GPG_ERR_NO_ERROR){
397
 
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
398
 
                 "bad gpgme_data_new: %s: %s\n",
 
579
    fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
399
580
                 gpgme_strsource(rc), gpgme_strerror(rc));
400
581
    gpgme_data_release(dh_crypto);
401
582
    return -1;
414
595
      if(result == NULL){
415
596
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
416
597
      } else {
417
 
        fprintf_plus(stderr, "Unsupported algorithm: %s\n",
418
 
                     result->unsupported_algorithm);
419
 
        fprintf_plus(stderr, "Wrong key usage: %u\n",
420
 
                     result->wrong_key_usage);
 
598
        if(result->unsupported_algorithm != NULL) {
 
599
          fprintf_plus(stderr, "Unsupported algorithm: %s\n",
 
600
                       result->unsupported_algorithm);
 
601
        }
 
602
        fprintf_plus(stderr, "Wrong key usage: %s\n",
 
603
                     result->wrong_key_usage ? "Yes" : "No");
421
604
        if(result->file_name != NULL){
422
605
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
423
606
        }
424
 
        gpgme_recipient_t recipient;
425
 
        recipient = result->recipients;
426
 
        while(recipient != NULL){
 
607
 
 
608
        for(gpgme_recipient_t r = result->recipients; r != NULL;
 
609
            r = r->next){
427
610
          fprintf_plus(stderr, "Public key algorithm: %s\n",
428
 
                       gpgme_pubkey_algo_name
429
 
                       (recipient->pubkey_algo));
430
 
          fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
 
611
                       gpgme_pubkey_algo_name(r->pubkey_algo));
 
612
          fprintf_plus(stderr, "Key ID: %s\n", r->keyid);
431
613
          fprintf_plus(stderr, "Secret key available: %s\n",
432
 
                       recipient->status == GPG_ERR_NO_SECKEY
433
 
                       ? "No" : "Yes");
434
 
          recipient = recipient->next;
 
614
                       r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
435
615
        }
436
616
      }
437
617
    }
513
693
  fprintf_plus(stderr, "GnuTLS: %s", string);
514
694
}
515
695
 
516
 
__attribute__((nonnull, warn_unused_result))
 
696
__attribute__((nonnull(1, 2, 4), warn_unused_result))
517
697
static int init_gnutls_global(const char *pubkeyfilename,
518
698
                              const char *seckeyfilename,
519
699
                              const char *dhparamsfilename,
525
705
    fprintf_plus(stderr, "Initializing GnuTLS\n");
526
706
  }
527
707
  
528
 
  ret = gnutls_global_init();
529
 
  if(ret != GNUTLS_E_SUCCESS){
530
 
    fprintf_plus(stderr, "GnuTLS global_init: %s\n",
531
 
                 safer_gnutls_strerror(ret));
532
 
    return -1;
533
 
  }
534
 
  
535
708
  if(debug){
536
709
    /* "Use a log level over 10 to enable all debugging options."
537
710
     * - GnuTLS manual
545
718
  if(ret != GNUTLS_E_SUCCESS){
546
719
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
547
720
                 safer_gnutls_strerror(ret));
548
 
    gnutls_global_deinit();
549
721
    return -1;
550
722
  }
551
723
  
615
787
        }
616
788
        params.size += (unsigned int)bytes_read;
617
789
      }
 
790
      ret = close(dhpfile);
 
791
      if(ret == -1){
 
792
        perror_plus("close");
 
793
      }
618
794
      if(params.data == NULL){
619
795
        dhparamsfilename = NULL;
620
796
      }
629
805
                     safer_gnutls_strerror(ret));
630
806
        dhparamsfilename = NULL;
631
807
      }
 
808
      free(params.data);
632
809
    } while(false);
633
810
  }
634
811
  if(dhparamsfilename == NULL){
755
932
 globalfail:
756
933
  
757
934
  gnutls_certificate_free_credentials(mc->cred);
758
 
  gnutls_global_deinit();
759
935
  gnutls_dh_params_deinit(mc->dh_params);
760
936
  return -1;
761
937
}
820
996
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
821
997
                      __attribute__((unused)) const char *txt){}
822
998
 
823
 
/* Set effective uid to 0, return errno */
824
 
__attribute__((warn_unused_result))
825
 
error_t raise_privileges(void){
826
 
  error_t old_errno = errno;
827
 
  error_t ret_errno = 0;
828
 
  if(seteuid(0) == -1){
829
 
    ret_errno = errno;
830
 
  }
831
 
  errno = old_errno;
832
 
  return ret_errno;
833
 
}
834
 
 
835
 
/* Set effective and real user ID to 0.  Return errno. */
836
 
__attribute__((warn_unused_result))
837
 
error_t raise_privileges_permanently(void){
838
 
  error_t old_errno = errno;
839
 
  error_t ret_errno = raise_privileges();
840
 
  if(ret_errno != 0){
841
 
    errno = old_errno;
842
 
    return ret_errno;
843
 
  }
844
 
  if(setuid(0) == -1){
845
 
    ret_errno = errno;
846
 
  }
847
 
  errno = old_errno;
848
 
  return ret_errno;
849
 
}
850
 
 
851
 
/* Set effective user ID to unprivileged saved user ID */
852
 
__attribute__((warn_unused_result))
853
 
error_t lower_privileges(void){
854
 
  error_t old_errno = errno;
855
 
  error_t ret_errno = 0;
856
 
  if(seteuid(uid) == -1){
857
 
    ret_errno = errno;
858
 
  }
859
 
  errno = old_errno;
860
 
  return ret_errno;
861
 
}
862
 
 
863
 
/* Lower privileges permanently */
864
 
__attribute__((warn_unused_result))
865
 
error_t lower_privileges_permanently(void){
866
 
  error_t old_errno = errno;
867
 
  error_t ret_errno = 0;
868
 
  if(setuid(uid) == -1){
869
 
    ret_errno = errno;
870
 
  }
871
 
  errno = old_errno;
872
 
  return ret_errno;
873
 
}
874
 
 
875
999
/* Helper function to add_local_route() and delete_local_route() */
876
1000
__attribute__((nonnull, warn_unused_result))
877
1001
static bool add_delete_local_route(const bool add,
931
1055
      perror_plus("dup2(devnull, STDIN_FILENO)");
932
1056
      _exit(EX_OSERR);
933
1057
    }
934
 
    ret = (int)TEMP_FAILURE_RETRY(close(devnull));
 
1058
    ret = close(devnull);
935
1059
    if(ret == -1){
936
1060
      perror_plus("close");
937
1061
      _exit(EX_OSERR);
954
1078
                                                   helper, O_RDONLY));
955
1079
    if(helper_fd == -1){
956
1080
      perror_plus("openat");
 
1081
      close(helperdir_fd);
957
1082
      _exit(EX_UNAVAILABLE);
958
1083
    }
959
 
    TEMP_FAILURE_RETRY(close(helperdir_fd));
 
1084
    close(helperdir_fd);
960
1085
#ifdef __GNUC__
961
1086
#pragma GCC diagnostic push
962
1087
#pragma GCC diagnostic ignored "-Wcast-qual"
1081
1206
    bool match = false;
1082
1207
    {
1083
1208
      char *interface = NULL;
1084
 
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
1085
 
                                 interface))){
 
1209
      while((interface = argz_next(mc->interfaces,
 
1210
                                   mc->interfaces_size,
 
1211
                                   interface))){
1086
1212
        if(if_nametoindex(interface) == (unsigned int)if_index){
1087
1213
          match = true;
1088
1214
          break;
1130
1256
    goto mandos_end;
1131
1257
  }
1132
1258
  
1133
 
  memset(&to, 0, sizeof(to));
1134
1259
  if(af == AF_INET6){
1135
 
    ((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
1136
 
    ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
 
1260
    struct sockaddr_in6 *to6 = (struct sockaddr_in6 *)&to;
 
1261
    *to6 = (struct sockaddr_in6){ .sin6_family = (sa_family_t)af };
 
1262
    ret = inet_pton(af, ip, &to6->sin6_addr);
1137
1263
  } else {                      /* IPv4 */
1138
 
    ((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
1139
 
    ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
 
1264
    struct sockaddr_in *to4 = (struct sockaddr_in *)&to;
 
1265
    *to4 = (struct sockaddr_in){ .sin_family = (sa_family_t)af };
 
1266
    ret = inet_pton(af, ip, &to4->sin_addr);
1140
1267
  }
1141
1268
  if(ret < 0 ){
1142
1269
    int e = errno;
1221
1348
                    sizeof(struct sockaddr_in));
1222
1349
    }
1223
1350
    if(ret < 0){
1224
 
      if(errno == ENETUNREACH
 
1351
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
1225
1352
         and if_index != AVAHI_IF_UNSPEC
1226
1353
         and connect_to == NULL
1227
1354
         and not route_added and
1240
1367
           with an explicit route added with the server's address.
1241
1368
           
1242
1369
           Avahi bug reference:
1243
 
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1370
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1244
1371
           https://bugs.debian.org/587961
1245
1372
        */
1246
1373
        if(debug){
1426
1553
                                               &decrypted_buffer, mc);
1427
1554
    if(decrypted_buffer_size >= 0){
1428
1555
      
 
1556
      clearerr(stdout);
1429
1557
      written = 0;
1430
1558
      while(written < (size_t) decrypted_buffer_size){
1431
1559
        if(quit_now){
1447
1575
        }
1448
1576
        written += (size_t)ret;
1449
1577
      }
 
1578
      ret = fflush(stdout);
 
1579
      if(ret != 0){
 
1580
        int e = errno;
 
1581
        if(debug){
 
1582
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
 
1583
                       strerror(errno));
 
1584
        }
 
1585
        errno = e;
 
1586
        goto mandos_end;
 
1587
      }
1450
1588
      retval = 0;
1451
1589
    }
1452
1590
  }
1465
1603
    free(decrypted_buffer);
1466
1604
    free(buffer);
1467
1605
    if(tcp_sd >= 0){
1468
 
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
1606
      ret = close(tcp_sd);
1469
1607
    }
1470
1608
    if(ret == -1){
1471
1609
      if(e == 0){
1483
1621
  return retval;
1484
1622
}
1485
1623
 
1486
 
__attribute__((nonnull))
1487
1624
static void resolve_callback(AvahiSServiceResolver *r,
1488
1625
                             AvahiIfIndex interface,
1489
1626
                             AvahiProtocol proto,
1626
1763
__attribute__((nonnull, warn_unused_result))
1627
1764
bool get_flags(const char *ifname, struct ifreq *ifr){
1628
1765
  int ret;
1629
 
  error_t ret_errno;
 
1766
  int old_errno;
1630
1767
  
1631
1768
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1632
1769
  if(s < 0){
1633
 
    ret_errno = errno;
 
1770
    old_errno = errno;
1634
1771
    perror_plus("socket");
1635
 
    errno = ret_errno;
 
1772
    errno = old_errno;
1636
1773
    return false;
1637
1774
  }
1638
 
  strcpy(ifr->ifr_name, ifname);
 
1775
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
 
1776
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
1639
1777
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1640
1778
  if(ret == -1){
1641
1779
    if(debug){
1642
 
      ret_errno = errno;
 
1780
      old_errno = errno;
1643
1781
      perror_plus("ioctl SIOCGIFFLAGS");
1644
 
      errno = ret_errno;
 
1782
      errno = old_errno;
 
1783
    }
 
1784
    if((close(s) == -1) and debug){
 
1785
      old_errno = errno;
 
1786
      perror_plus("close");
 
1787
      errno = old_errno;
1645
1788
    }
1646
1789
    return false;
1647
1790
  }
 
1791
  if((close(s) == -1) and debug){
 
1792
    old_errno = errno;
 
1793
    perror_plus("close");
 
1794
    errno = old_errno;
 
1795
  }
1648
1796
  return true;
1649
1797
}
1650
1798
 
1911
2059
      return;
1912
2060
    }
1913
2061
  }
1914
 
#ifdef __GLIBC__
1915
 
#if __GLIBC_PREREQ(2, 15)
 
2062
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
2063
  if(devnull == -1){
 
2064
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
2065
    return;
 
2066
  }
1916
2067
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
1917
2068
                           runnable_hook, alphasort);
1918
 
#else  /* not __GLIBC_PREREQ(2, 15) */
1919
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1920
 
                         alphasort);
1921
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
1922
 
#else   /* not __GLIBC__ */
1923
 
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
1924
 
                         alphasort);
1925
 
#endif  /* not __GLIBC__ */
1926
2069
  if(numhooks == -1){
1927
2070
    perror_plus("scandir");
 
2071
    close(devnull);
1928
2072
    return;
1929
2073
  }
1930
2074
  struct dirent *direntry;
1931
2075
  int ret;
1932
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
1933
 
  if(devnull == -1){
1934
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
1935
 
    return;
1936
 
  }
1937
2076
  for(int i = 0; i < numhooks; i++){
1938
2077
    direntry = direntries[i];
1939
2078
    if(debug){
2010
2149
        perror_plus("openat");
2011
2150
        _exit(EXIT_FAILURE);
2012
2151
      }
2013
 
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
2152
      if(close(hookdir_fd) == -1){
2014
2153
        perror_plus("close");
2015
2154
        _exit(EXIT_FAILURE);
2016
2155
      }
2019
2158
        perror_plus("dup2(devnull, STDIN_FILENO)");
2020
2159
        _exit(EX_OSERR);
2021
2160
      }
2022
 
      ret = (int)TEMP_FAILURE_RETRY(close(devnull));
 
2161
      ret = close(devnull);
2023
2162
      if(ret == -1){
2024
2163
        perror_plus("close");
2025
2164
        _exit(EX_OSERR);
2074
2213
    free(direntry);
2075
2214
  }
2076
2215
  free(direntries);
2077
 
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
2216
  if(close(hookdir_fd) == -1){
2078
2217
    perror_plus("close");
2079
2218
  } else {
2080
2219
    hookdir_fd = -1;
2083
2222
}
2084
2223
 
2085
2224
__attribute__((nonnull, warn_unused_result))
2086
 
error_t bring_up_interface(const char *const interface,
2087
 
                           const float delay){
2088
 
  error_t old_errno = errno;
 
2225
int bring_up_interface(const char *const interface,
 
2226
                       const float delay){
 
2227
  int old_errno = errno;
2089
2228
  int ret;
2090
2229
  struct ifreq network;
2091
2230
  unsigned int if_index = if_nametoindex(interface);
2101
2240
  }
2102
2241
  
2103
2242
  if(not interface_is_up(interface)){
2104
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2243
    int ret_errno = 0;
 
2244
    int ioctl_errno = 0;
2105
2245
    if(not get_flags(interface, &network)){
2106
2246
      ret_errno = errno;
2107
2247
      fprintf_plus(stderr, "Failed to get flags for interface "
2120
2260
    }
2121
2261
    
2122
2262
    if(quit_now){
2123
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2263
      ret = close(sd);
2124
2264
      if(ret == -1){
2125
2265
        perror_plus("close");
2126
2266
      }
2176
2316
    }
2177
2317
    
2178
2318
    /* Close the socket */
2179
 
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2319
    ret = close(sd);
2180
2320
    if(ret == -1){
2181
2321
      perror_plus("close");
2182
2322
    }
2194
2334
  
2195
2335
  /* Sleep checking until interface is running.
2196
2336
     Check every 0.25s, up to total time of delay */
2197
 
  for(int i=0; i < delay * 4; i++){
 
2337
  for(int i = 0; i < delay * 4; i++){
2198
2338
    if(interface_is_running(interface)){
2199
2339
      break;
2200
2340
    }
2210
2350
}
2211
2351
 
2212
2352
__attribute__((nonnull, warn_unused_result))
2213
 
error_t take_down_interface(const char *const interface){
2214
 
  error_t old_errno = errno;
 
2353
int take_down_interface(const char *const interface){
 
2354
  int old_errno = errno;
2215
2355
  struct ifreq network;
2216
2356
  unsigned int if_index = if_nametoindex(interface);
2217
2357
  if(if_index == 0){
2220
2360
    return ENXIO;
2221
2361
  }
2222
2362
  if(interface_is_up(interface)){
2223
 
    error_t ret_errno = 0, ioctl_errno = 0;
 
2363
    int ret_errno = 0;
 
2364
    int ioctl_errno = 0;
2224
2365
    if(not get_flags(interface, &network) and debug){
2225
2366
      ret_errno = errno;
2226
2367
      fprintf_plus(stderr, "Failed to get flags for interface "
2264
2405
    }
2265
2406
    
2266
2407
    /* Close the socket */
2267
 
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
2408
    int ret = close(sd);
2268
2409
    if(ret == -1){
2269
2410
      perror_plus("close");
2270
2411
    }
2286
2427
 
2287
2428
int main(int argc, char *argv[]){
2288
2429
  mandos_context mc = { .server = NULL, .dh_bits = 0,
2289
 
                        .priority = "SECURE256:!CTYPE-X.509:"
2290
 
                        "+CTYPE-OPENPGP:!RSA", .current_server = NULL,
2291
 
                        .interfaces = NULL, .interfaces_size = 0 };
 
2430
                        .priority = "SECURE256:!CTYPE-X.509"
 
2431
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
 
2432
                        .current_server = NULL, .interfaces = NULL,
 
2433
                        .interfaces_size = 0 };
2292
2434
  AvahiSServiceBrowser *sb = NULL;
2293
2435
  error_t ret_errno;
2294
2436
  int ret;
2475
2617
                         .args_doc = "",
2476
2618
                         .doc = "Mandos client -- Get and decrypt"
2477
2619
                         " passwords from a Mandos server" };
2478
 
    ret = argp_parse(&argp, argc, argv,
2479
 
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2480
 
    switch(ret){
 
2620
    ret_errno = argp_parse(&argp, argc, argv,
 
2621
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2622
    switch(ret_errno){
2481
2623
    case 0:
2482
2624
      break;
2483
2625
    case ENOMEM:
2484
2626
    default:
2485
 
      errno = ret;
 
2627
      errno = ret_errno;
2486
2628
      perror_plus("argp_parse");
2487
2629
      exitcode = EX_OSERR;
2488
2630
      goto end;
2494
2636
  
2495
2637
  {
2496
2638
    /* Work around Debian bug #633582:
2497
 
       <http://bugs.debian.org/633582> */
 
2639
       <https://bugs.debian.org/633582> */
2498
2640
    
2499
2641
    /* Re-raise privileges */
2500
 
    ret_errno = raise_privileges();
2501
 
    if(ret_errno != 0){
2502
 
      errno = ret_errno;
 
2642
    ret = raise_privileges();
 
2643
    if(ret != 0){
 
2644
      errno = ret;
2503
2645
      perror_plus("Failed to raise privileges");
2504
2646
    } else {
2505
2647
      struct stat st;
2521
2663
              }
2522
2664
            }
2523
2665
          }
2524
 
          TEMP_FAILURE_RETRY(close(seckey_fd));
 
2666
          close(seckey_fd);
2525
2667
        }
2526
2668
      }
2527
2669
      
2542
2684
              }
2543
2685
            }
2544
2686
          }
2545
 
          TEMP_FAILURE_RETRY(close(pubkey_fd));
 
2687
          close(pubkey_fd);
2546
2688
        }
2547
2689
      }
2548
2690
      
2549
 
      if(strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
 
2691
      if(dh_params_file != NULL
 
2692
         and strcmp(dh_params_file, PATHDIR "/dhparams.pem" ) == 0){
2550
2693
        int dhparams_fd = open(dh_params_file, O_RDONLY);
2551
2694
        if(dhparams_fd == -1){
2552
2695
          perror_plus("open");
2563
2706
              }
2564
2707
            }
2565
2708
          }
2566
 
          TEMP_FAILURE_RETRY(close(dhparams_fd));
 
2709
          close(dhparams_fd);
2567
2710
        }
2568
2711
      }
2569
2712
      
2570
2713
      /* Lower privileges */
2571
 
      ret_errno = lower_privileges();
2572
 
      if(ret_errno != 0){
2573
 
        errno = ret_errno;
 
2714
      ret = lower_privileges();
 
2715
      if(ret != 0){
 
2716
        errno = ret;
2574
2717
        perror_plus("Failed to lower privileges");
2575
2718
      }
2576
2719
    }
2904
3047
    
2905
3048
    /* Allocate a new server */
2906
3049
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2907
 
                                 &config, NULL, NULL, &ret_errno);
 
3050
                                 &config, NULL, NULL, &ret);
2908
3051
    
2909
3052
    /* Free the Avahi configuration data */
2910
3053
    avahi_server_config_free(&config);
2913
3056
  /* Check if creating the Avahi server object succeeded */
2914
3057
  if(mc.server == NULL){
2915
3058
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2916
 
                 avahi_strerror(ret_errno));
 
3059
                 avahi_strerror(ret));
2917
3060
    exitcode = EX_UNAVAILABLE;
2918
3061
    goto end;
2919
3062
  }
2954
3097
 end:
2955
3098
  
2956
3099
  if(debug){
2957
 
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3100
    if(signal_received){
 
3101
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
 
3102
                   argv[0], signal_received,
 
3103
                   strsignal(signal_received));
 
3104
    } else {
 
3105
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
 
3106
    }
2958
3107
  }
2959
3108
  
2960
3109
  /* Cleanup things */
2971
3120
  
2972
3121
  if(gnutls_initialized){
2973
3122
    gnutls_certificate_free_credentials(mc.cred);
2974
 
    gnutls_global_deinit();
2975
3123
    gnutls_dh_params_deinit(mc.dh_params);
2976
3124
  }
2977
3125
  
3000
3148
  
3001
3149
  /* Re-raise privileges */
3002
3150
  {
3003
 
    ret_errno = raise_privileges();
3004
 
    if(ret_errno != 0){
3005
 
      errno = ret_errno;
 
3151
    ret = raise_privileges();
 
3152
    if(ret != 0){
 
3153
      errno = ret;
3006
3154
      perror_plus("Failed to raise privileges");
3007
3155
    } else {
3008
3156
      
3013
3161
      /* Take down the network interfaces which were brought up */
3014
3162
      {
3015
3163
        char *interface = NULL;
3016
 
        while((interface=argz_next(interfaces_to_take_down,
3017
 
                                   interfaces_to_take_down_size,
3018
 
                                   interface))){
3019
 
          ret_errno = take_down_interface(interface);
3020
 
          if(ret_errno != 0){
3021
 
            errno = ret_errno;
 
3164
        while((interface = argz_next(interfaces_to_take_down,
 
3165
                                     interfaces_to_take_down_size,
 
3166
                                     interface))){
 
3167
          ret = take_down_interface(interface);
 
3168
          if(ret != 0){
 
3169
            errno = ret;
3022
3170
            perror_plus("Failed to take down interface");
3023
3171
          }
3024
3172
        }
3029
3177
      }
3030
3178
    }
3031
3179
    
3032
 
    ret_errno = lower_privileges_permanently();
3033
 
    if(ret_errno != 0){
3034
 
      errno = ret_errno;
 
3180
    ret = lower_privileges_permanently();
 
3181
    if(ret != 0){
 
3182
      errno = ret;
3035
3183
      perror_plus("Failed to lower privileges permanently");
3036
3184
    }
3037
3185
  }
3039
3187
  free(interfaces_to_take_down);
3040
3188
  free(interfaces_hooks);
3041
3189
  
 
3190
  void clean_dir_at(int base, const char * const dirname,
 
3191
                    uintmax_t level){
 
3192
    struct dirent **direntries = NULL;
 
3193
    int dret;
 
3194
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
 
3195
                                                O_RDONLY
 
3196
                                                | O_NOFOLLOW
 
3197
                                                | O_DIRECTORY
 
3198
                                                | O_PATH));
 
3199
    if(dir_fd == -1){
 
3200
      perror_plus("open");
 
3201
      return;
 
3202
    }
 
3203
    int numentries = scandirat(dir_fd, ".", &direntries,
 
3204
                               notdotentries, alphasort);
 
3205
    if(numentries >= 0){
 
3206
      for(int i = 0; i < numentries; i++){
 
3207
        if(debug){
 
3208
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
 
3209
                       dirname, direntries[i]->d_name);
 
3210
        }
 
3211
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
 
3212
        if(dret == -1){
 
3213
          if(errno == EISDIR){
 
3214
              dret = unlinkat(dir_fd, direntries[i]->d_name,
 
3215
                              AT_REMOVEDIR);
 
3216
          }         
 
3217
          if((dret == -1) and (errno == ENOTEMPTY)
 
3218
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
 
3219
                  == 0) and (level == 0)){
 
3220
            /* Recurse only in this special case */
 
3221
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
 
3222
            dret = 0;
 
3223
          }
 
3224
          if((dret == -1) and (errno != ENOENT)){
 
3225
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
 
3226
                         direntries[i]->d_name, strerror(errno));
 
3227
          }
 
3228
        }
 
3229
        free(direntries[i]);
 
3230
      }
 
3231
      
 
3232
      /* need to clean even if 0 because man page doesn't specify */
 
3233
      free(direntries);
 
3234
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
 
3235
      if(dret == -1 and errno != ENOENT){
 
3236
        perror_plus("rmdir");
 
3237
      }
 
3238
    } else {
 
3239
      perror_plus("scandirat");
 
3240
    }
 
3241
    close(dir_fd);
 
3242
  }
 
3243
  
3042
3244
  /* Removes the GPGME temp directory and all files inside */
3043
3245
  if(tempdir != NULL){
3044
 
    struct dirent **direntries = NULL;
3045
 
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
3046
 
                                                  | O_NOFOLLOW
3047
 
                                                  | O_DIRECTORY
3048
 
                                                  | O_PATH));
3049
 
    if(tempdir_fd == -1){
3050
 
      perror_plus("open");
3051
 
    } else {
3052
 
#ifdef __GLIBC__
3053
 
#if __GLIBC_PREREQ(2, 15)
3054
 
      int numentries = scandirat(tempdir_fd, ".", &direntries,
3055
 
                                 notdotentries, alphasort);
3056
 
#else  /* not __GLIBC_PREREQ(2, 15) */
3057
 
      int numentries = scandir(tempdir, &direntries, notdotentries,
3058
 
                               alphasort);
3059
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
3060
 
#else   /* not __GLIBC__ */
3061
 
      int numentries = scandir(tempdir, &direntries, notdotentries,
3062
 
                               alphasort);
3063
 
#endif  /* not __GLIBC__ */
3064
 
      if(numentries >= 0){
3065
 
        for(int i = 0; i < numentries; i++){
3066
 
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
3067
 
          if(ret == -1){
3068
 
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
3069
 
                         " \"%s\", 0): %s\n", tempdir,
3070
 
                         direntries[i]->d_name, strerror(errno));
3071
 
          }
3072
 
          free(direntries[i]);
3073
 
        }
3074
 
        
3075
 
        /* need to clean even if 0 because man page doesn't specify */
3076
 
        free(direntries);
3077
 
        if(numentries == -1){
3078
 
          perror_plus("scandir");
3079
 
        }
3080
 
        ret = rmdir(tempdir);
3081
 
        if(ret == -1 and errno != ENOENT){
3082
 
          perror_plus("rmdir");
3083
 
        }
3084
 
      }
3085
 
      TEMP_FAILURE_RETRY(close(tempdir_fd));
3086
 
    }
 
3246
    clean_dir_at(-1, tempdir, 0);
3087
3247
  }
3088
3248
  
3089
3249
  if(quit_now){