/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: 2015-07-20 03:03:33 UTC
  • Revision ID: teddy@recompile.se-20150720030333-203m2aeblypcsfte
Bug fix for GnuTLS 3: be compatible with old 2048-bit DSA keys.

The mandos-keygen program in Mandos version 1.6.0 and older generated
2048-bit DSA keys, and when GnuTLS uses these it has trouble
connecting using the Mandos default priority string.  This was
previously fixed in Mandos 1.6.2, but the bug reappeared when using
GnuTLS 3, so the default priority string has to change again; this
time also the Mandos client has to change its default, so now the
server and the client should use the same default priority string:

SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256

* mandos (main/server_defaults): Changed default priority string.
* mandos-options.xml (/section/para[id="priority_compat"]): Removed.
  (/section/para[id="priority"]): Changed default priority string.
* mandos.conf ([DEFAULT]/priority): - '' -
* mandos.conf.xml (OPTIONS/priority): Refer to the id "priority"
                                      instead of "priority_compat".
* mandos.xml (OPTIONS/--priority): - '' -
* plugins.d/mandos-client.c (main): Changed default priority string.

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-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
 
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
23
21
 * WITHOUT ANY WARRANTY; without even the implied warranty of
24
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25
23
 * General Public License for more details.
26
24
 * 
27
25
 * You should have received a copy of the GNU General Public License
28
 
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
26
 * along with this program.  If not, see
 
27
 * <http://www.gnu.org/licenses/>.
29
28
 * 
30
29
 * Contact the authors at <mandos@recompile.se>.
31
30
 */
48
47
                                   strtof(), abort() */
49
48
#include <stdbool.h>            /* bool, false, true */
50
49
#include <string.h>             /* strcmp(), strlen(), strerror(),
51
 
                                   asprintf(), strncpy(), strsignal()
52
 
                                */
 
50
                                   asprintf(), strcpy() */
53
51
#include <sys/ioctl.h>          /* ioctl */
54
52
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
55
53
                                   sockaddr_in6, PF_INET6,
59
57
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
60
58
                                   inet_pton(), connect(),
61
59
                                   getnameinfo() */
62
 
#include <fcntl.h>              /* open(), unlinkat(), AT_REMOVEDIR */
 
60
#include <fcntl.h>              /* open(), unlinkat() */
63
61
#include <dirent.h>             /* opendir(), struct dirent, readdir()
64
62
                                 */
65
63
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
66
64
                                   strtoimax() */
67
 
#include <errno.h>              /* perror(), errno, EINTR, EINVAL,
68
 
                                   EAI_SYSTEM, ENETUNREACH,
69
 
                                   EHOSTUNREACH, ECONNREFUSED, EPROTO,
70
 
                                   EIO, ENOENT, ENXIO, ENOMEM, EISDIR,
71
 
                                   ENOTEMPTY,
 
65
#include <errno.h>              /* perror(), errno,
72
66
                                   program_invocation_short_name */
73
67
#include <time.h>               /* nanosleep(), time(), sleep() */
74
68
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
310
304
                   gpgme_strsource(rc), gpgme_strerror(rc));
311
305
      return false;
312
306
    }
313
 
    {
314
 
      gpgme_import_result_t import_result
315
 
        = gpgme_op_import_result(mc->ctx);
316
 
      if((import_result->imported < 1
317
 
          or import_result->not_imported > 0)
318
 
         and import_result->unchanged == 0){
319
 
        fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
320
 
        fprintf_plus(stderr,
321
 
                     "The total number of considered keys: %d\n",
322
 
                     import_result->considered);
323
 
        fprintf_plus(stderr,
324
 
                     "The number of keys without user ID: %d\n",
325
 
                     import_result->no_user_id);
326
 
        fprintf_plus(stderr,
327
 
                     "The total number of imported keys: %d\n",
328
 
                     import_result->imported);
329
 
        fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
330
 
                     import_result->imported_rsa);
331
 
        fprintf_plus(stderr, "The number of unchanged keys: %d\n",
332
 
                     import_result->unchanged);
333
 
        fprintf_plus(stderr, "The number of new user IDs: %d\n",
334
 
                     import_result->new_user_ids);
335
 
        fprintf_plus(stderr, "The number of new sub keys: %d\n",
336
 
                     import_result->new_sub_keys);
337
 
        fprintf_plus(stderr, "The number of new signatures: %d\n",
338
 
                     import_result->new_signatures);
339
 
        fprintf_plus(stderr, "The number of new revocations: %d\n",
340
 
                     import_result->new_revocations);
341
 
        fprintf_plus(stderr,
342
 
                     "The total number of secret keys read: %d\n",
343
 
                     import_result->secret_read);
344
 
        fprintf_plus(stderr,
345
 
                     "The number of imported secret keys: %d\n",
346
 
                     import_result->secret_imported);
347
 
        fprintf_plus(stderr,
348
 
                     "The number of unchanged secret keys: %d\n",
349
 
                     import_result->secret_unchanged);
350
 
        fprintf_plus(stderr, "The number of keys not imported: %d\n",
351
 
                     import_result->not_imported);
352
 
        for(gpgme_import_status_t import_status
353
 
              = import_result->imports;
354
 
            import_status != NULL;
355
 
            import_status = import_status->next){
356
 
          fprintf_plus(stderr, "Import status for key: %s\n",
357
 
                       import_status->fpr);
358
 
          if(import_status->result != GPG_ERR_NO_ERROR){
359
 
            fprintf_plus(stderr, "Import result: %s: %s\n",
360
 
                         gpgme_strsource(import_status->result),
361
 
                         gpgme_strerror(import_status->result));
362
 
          }
363
 
          fprintf_plus(stderr, "Key status:\n");
364
 
          fprintf_plus(stderr,
365
 
                       import_status->status & GPGME_IMPORT_NEW
366
 
                       ? "The key was new.\n"
367
 
                       : "The key was not new.\n");
368
 
          fprintf_plus(stderr,
369
 
                       import_status->status & GPGME_IMPORT_UID
370
 
                       ? "The key contained new user IDs.\n"
371
 
                       : "The key did not contain new user IDs.\n");
372
 
          fprintf_plus(stderr,
373
 
                       import_status->status & GPGME_IMPORT_SIG
374
 
                       ? "The key contained new signatures.\n"
375
 
                       : "The key did not contain new signatures.\n");
376
 
          fprintf_plus(stderr,
377
 
                       import_status->status & GPGME_IMPORT_SUBKEY
378
 
                       ? "The key contained new sub keys.\n"
379
 
                       : "The key did not contain new sub keys.\n");
380
 
          fprintf_plus(stderr,
381
 
                       import_status->status & GPGME_IMPORT_SECRET
382
 
                       ? "The key contained a secret key.\n"
383
 
                       : "The key did not contain a secret key.\n");
384
 
        }
385
 
        return false;
386
 
      }
387
 
    }
388
307
    
389
308
    ret = close(fd);
390
309
    if(ret == -1){
431
350
  /* Create new GPGME "context" */
432
351
  rc = gpgme_new(&(mc->ctx));
433
352
  if(rc != GPG_ERR_NO_ERROR){
434
 
    fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
435
 
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
353
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
 
354
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
 
355
                 gpgme_strerror(rc));
436
356
    return false;
437
357
  }
438
358
  
474
394
  /* Create new empty GPGME data buffer for the plaintext */
475
395
  rc = gpgme_data_new(&dh_plain);
476
396
  if(rc != GPG_ERR_NO_ERROR){
477
 
    fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
 
397
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
 
398
                 "bad gpgme_data_new: %s: %s\n",
478
399
                 gpgme_strsource(rc), gpgme_strerror(rc));
479
400
    gpgme_data_release(dh_crypto);
480
401
    return -1;
493
414
      if(result == NULL){
494
415
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
495
416
      } else {
496
 
        if(result->unsupported_algorithm != NULL) {
497
 
          fprintf_plus(stderr, "Unsupported algorithm: %s\n",
498
 
                       result->unsupported_algorithm);
499
 
        }
500
 
        fprintf_plus(stderr, "Wrong key usage: %s\n",
501
 
                     result->wrong_key_usage ? "Yes" : "No");
 
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);
502
421
        if(result->file_name != NULL){
503
422
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
504
423
        }
505
 
 
506
 
        for(gpgme_recipient_t r = result->recipients; r != NULL;
507
 
            r = r->next){
 
424
        gpgme_recipient_t recipient;
 
425
        recipient = result->recipients;
 
426
        while(recipient != NULL){
508
427
          fprintf_plus(stderr, "Public key algorithm: %s\n",
509
 
                       gpgme_pubkey_algo_name(r->pubkey_algo));
510
 
          fprintf_plus(stderr, "Key ID: %s\n", r->keyid);
 
428
                       gpgme_pubkey_algo_name
 
429
                       (recipient->pubkey_algo));
 
430
          fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
511
431
          fprintf_plus(stderr, "Secret key available: %s\n",
512
 
                       r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
 
432
                       recipient->status == GPG_ERR_NO_SECKEY
 
433
                       ? "No" : "Yes");
 
434
          recipient = recipient->next;
513
435
        }
514
436
      }
515
437
    }
591
513
  fprintf_plus(stderr, "GnuTLS: %s", string);
592
514
}
593
515
 
594
 
__attribute__((nonnull(1, 2, 4), warn_unused_result))
 
516
__attribute__((nonnull, warn_unused_result))
595
517
static int init_gnutls_global(const char *pubkeyfilename,
596
518
                              const char *seckeyfilename,
597
519
                              const char *dhparamsfilename,
603
525
    fprintf_plus(stderr, "Initializing GnuTLS\n");
604
526
  }
605
527
  
 
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
  
606
535
  if(debug){
607
536
    /* "Use a log level over 10 to enable all debugging options."
608
537
     * - GnuTLS manual
616
545
  if(ret != GNUTLS_E_SUCCESS){
617
546
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
618
547
                 safer_gnutls_strerror(ret));
 
548
    gnutls_global_deinit();
619
549
    return -1;
620
550
  }
621
551
  
685
615
        }
686
616
        params.size += (unsigned int)bytes_read;
687
617
      }
688
 
      ret = close(dhpfile);
689
 
      if(ret == -1){
690
 
        perror_plus("close");
691
 
      }
692
618
      if(params.data == NULL){
693
619
        dhparamsfilename = NULL;
694
620
      }
703
629
                     safer_gnutls_strerror(ret));
704
630
        dhparamsfilename = NULL;
705
631
      }
706
 
      free(params.data);
707
632
    } while(false);
708
633
  }
709
634
  if(dhparamsfilename == NULL){
830
755
 globalfail:
831
756
  
832
757
  gnutls_certificate_free_credentials(mc->cred);
 
758
  gnutls_global_deinit();
833
759
  gnutls_dh_params_deinit(mc->dh_params);
834
760
  return -1;
835
761
}
896
822
 
897
823
/* Set effective uid to 0, return errno */
898
824
__attribute__((warn_unused_result))
899
 
int raise_privileges(void){
900
 
  int old_errno = errno;
901
 
  int ret = 0;
 
825
error_t raise_privileges(void){
 
826
  error_t old_errno = errno;
 
827
  error_t ret_errno = 0;
902
828
  if(seteuid(0) == -1){
903
 
    ret = errno;
 
829
    ret_errno = errno;
904
830
  }
905
831
  errno = old_errno;
906
 
  return ret;
 
832
  return ret_errno;
907
833
}
908
834
 
909
835
/* Set effective and real user ID to 0.  Return errno. */
910
836
__attribute__((warn_unused_result))
911
 
int raise_privileges_permanently(void){
912
 
  int old_errno = errno;
913
 
  int ret = raise_privileges();
914
 
  if(ret != 0){
 
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){
915
841
    errno = old_errno;
916
 
    return ret;
 
842
    return ret_errno;
917
843
  }
918
844
  if(setuid(0) == -1){
919
 
    ret = errno;
 
845
    ret_errno = errno;
920
846
  }
921
847
  errno = old_errno;
922
 
  return ret;
 
848
  return ret_errno;
923
849
}
924
850
 
925
851
/* Set effective user ID to unprivileged saved user ID */
926
852
__attribute__((warn_unused_result))
927
 
int lower_privileges(void){
928
 
  int old_errno = errno;
929
 
  int ret = 0;
 
853
error_t lower_privileges(void){
 
854
  error_t old_errno = errno;
 
855
  error_t ret_errno = 0;
930
856
  if(seteuid(uid) == -1){
931
 
    ret = errno;
 
857
    ret_errno = errno;
932
858
  }
933
859
  errno = old_errno;
934
 
  return ret;
 
860
  return ret_errno;
935
861
}
936
862
 
937
863
/* Lower privileges permanently */
938
864
__attribute__((warn_unused_result))
939
 
int lower_privileges_permanently(void){
940
 
  int old_errno = errno;
941
 
  int ret = 0;
 
865
error_t lower_privileges_permanently(void){
 
866
  error_t old_errno = errno;
 
867
  error_t ret_errno = 0;
942
868
  if(setuid(uid) == -1){
943
 
    ret = errno;
 
869
    ret_errno = errno;
944
870
  }
945
871
  errno = old_errno;
946
 
  return ret;
 
872
  return ret_errno;
947
873
}
948
874
 
949
875
/* Helper function to add_local_route() and delete_local_route() */
1156
1082
    bool match = false;
1157
1083
    {
1158
1084
      char *interface = NULL;
1159
 
      while((interface = argz_next(mc->interfaces,
1160
 
                                   mc->interfaces_size,
1161
 
                                   interface))){
 
1085
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
 
1086
                                 interface))){
1162
1087
        if(if_nametoindex(interface) == (unsigned int)if_index){
1163
1088
          match = true;
1164
1089
          break;
1298
1223
                    sizeof(struct sockaddr_in));
1299
1224
    }
1300
1225
    if(ret < 0){
1301
 
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
 
1226
      if(errno == ENETUNREACH
1302
1227
         and if_index != AVAHI_IF_UNSPEC
1303
1228
         and connect_to == NULL
1304
1229
         and not route_added and
1317
1242
           with an explicit route added with the server's address.
1318
1243
           
1319
1244
           Avahi bug reference:
1320
 
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1245
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1321
1246
           https://bugs.debian.org/587961
1322
1247
        */
1323
1248
        if(debug){
1503
1428
                                               &decrypted_buffer, mc);
1504
1429
    if(decrypted_buffer_size >= 0){
1505
1430
      
1506
 
      clearerr(stdout);
1507
1431
      written = 0;
1508
1432
      while(written < (size_t) decrypted_buffer_size){
1509
1433
        if(quit_now){
1525
1449
        }
1526
1450
        written += (size_t)ret;
1527
1451
      }
1528
 
      ret = fflush(stdout);
1529
 
      if(ret != 0){
1530
 
        int e = errno;
1531
 
        if(debug){
1532
 
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1533
 
                       strerror(errno));
1534
 
        }
1535
 
        errno = e;
1536
 
        goto mandos_end;
1537
 
      }
1538
1452
      retval = 0;
1539
1453
    }
1540
1454
  }
1571
1485
  return retval;
1572
1486
}
1573
1487
 
 
1488
__attribute__((nonnull))
1574
1489
static void resolve_callback(AvahiSServiceResolver *r,
1575
1490
                             AvahiIfIndex interface,
1576
1491
                             AvahiProtocol proto,
1713
1628
__attribute__((nonnull, warn_unused_result))
1714
1629
bool get_flags(const char *ifname, struct ifreq *ifr){
1715
1630
  int ret;
1716
 
  int old_errno;
 
1631
  error_t ret_errno;
1717
1632
  
1718
1633
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1719
1634
  if(s < 0){
1720
 
    old_errno = errno;
 
1635
    ret_errno = errno;
1721
1636
    perror_plus("socket");
1722
 
    errno = old_errno;
 
1637
    errno = ret_errno;
1723
1638
    return false;
1724
1639
  }
1725
 
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1726
 
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
 
1640
  strcpy(ifr->ifr_name, ifname);
1727
1641
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1728
1642
  if(ret == -1){
1729
1643
    if(debug){
1730
 
      old_errno = errno;
 
1644
      ret_errno = errno;
1731
1645
      perror_plus("ioctl SIOCGIFFLAGS");
1732
 
      errno = old_errno;
1733
 
    }
1734
 
    if((close(s) == -1) and debug){
1735
 
      old_errno = errno;
1736
 
      perror_plus("close");
1737
 
      errno = old_errno;
 
1646
      errno = ret_errno;
1738
1647
    }
1739
1648
    return false;
1740
1649
  }
1741
 
  if((close(s) == -1) and debug){
1742
 
    old_errno = errno;
1743
 
    perror_plus("close");
1744
 
    errno = old_errno;
1745
 
  }
1746
1650
  return true;
1747
1651
}
1748
1652
 
2009
1913
      return;
2010
1914
    }
2011
1915
  }
2012
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
2013
 
  if(devnull == -1){
2014
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
2015
 
    return;
2016
 
  }
 
1916
#ifdef __GLIBC__
 
1917
#if __GLIBC_PREREQ(2, 15)
2017
1918
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
2018
1919
                           runnable_hook, alphasort);
 
1920
#else  /* not __GLIBC_PREREQ(2, 15) */
 
1921
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1922
                         alphasort);
 
1923
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
1924
#else   /* not __GLIBC__ */
 
1925
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1926
                         alphasort);
 
1927
#endif  /* not __GLIBC__ */
2019
1928
  if(numhooks == -1){
2020
1929
    perror_plus("scandir");
2021
 
    close(devnull);
2022
1930
    return;
2023
1931
  }
2024
1932
  struct dirent *direntry;
2025
1933
  int ret;
 
1934
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
1935
  if(devnull == -1){
 
1936
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
1937
    return;
 
1938
  }
2026
1939
  for(int i = 0; i < numhooks; i++){
2027
1940
    direntry = direntries[i];
2028
1941
    if(debug){
2172
2085
}
2173
2086
 
2174
2087
__attribute__((nonnull, warn_unused_result))
2175
 
int bring_up_interface(const char *const interface,
2176
 
                       const float delay){
2177
 
  int old_errno = errno;
 
2088
error_t bring_up_interface(const char *const interface,
 
2089
                           const float delay){
 
2090
  error_t old_errno = errno;
2178
2091
  int ret;
2179
2092
  struct ifreq network;
2180
2093
  unsigned int if_index = if_nametoindex(interface);
2190
2103
  }
2191
2104
  
2192
2105
  if(not interface_is_up(interface)){
2193
 
    int ret_errno = 0;
2194
 
    int ioctl_errno = 0;
 
2106
    error_t ret_errno = 0, ioctl_errno = 0;
2195
2107
    if(not get_flags(interface, &network)){
2196
2108
      ret_errno = errno;
2197
2109
      fprintf_plus(stderr, "Failed to get flags for interface "
2284
2196
  
2285
2197
  /* Sleep checking until interface is running.
2286
2198
     Check every 0.25s, up to total time of delay */
2287
 
  for(int i = 0; i < delay * 4; i++){
 
2199
  for(int i=0; i < delay * 4; i++){
2288
2200
    if(interface_is_running(interface)){
2289
2201
      break;
2290
2202
    }
2300
2212
}
2301
2213
 
2302
2214
__attribute__((nonnull, warn_unused_result))
2303
 
int take_down_interface(const char *const interface){
2304
 
  int old_errno = errno;
 
2215
error_t take_down_interface(const char *const interface){
 
2216
  error_t old_errno = errno;
2305
2217
  struct ifreq network;
2306
2218
  unsigned int if_index = if_nametoindex(interface);
2307
2219
  if(if_index == 0){
2310
2222
    return ENXIO;
2311
2223
  }
2312
2224
  if(interface_is_up(interface)){
2313
 
    int ret_errno = 0;
2314
 
    int ioctl_errno = 0;
 
2225
    error_t ret_errno = 0, ioctl_errno = 0;
2315
2226
    if(not get_flags(interface, &network) and debug){
2316
2227
      ret_errno = errno;
2317
2228
      fprintf_plus(stderr, "Failed to get flags for interface "
2567
2478
                         .args_doc = "",
2568
2479
                         .doc = "Mandos client -- Get and decrypt"
2569
2480
                         " passwords from a Mandos server" };
2570
 
    ret_errno = argp_parse(&argp, argc, argv,
2571
 
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2572
 
    switch(ret_errno){
 
2481
    ret = argp_parse(&argp, argc, argv,
 
2482
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2483
    switch(ret){
2573
2484
    case 0:
2574
2485
      break;
2575
2486
    case ENOMEM:
2576
2487
    default:
2577
 
      errno = ret_errno;
 
2488
      errno = ret;
2578
2489
      perror_plus("argp_parse");
2579
2490
      exitcode = EX_OSERR;
2580
2491
      goto end;
2586
2497
  
2587
2498
  {
2588
2499
    /* Work around Debian bug #633582:
2589
 
       <https://bugs.debian.org/633582> */
 
2500
       <http://bugs.debian.org/633582> */
2590
2501
    
2591
2502
    /* Re-raise privileges */
2592
 
    ret = raise_privileges();
2593
 
    if(ret != 0){
2594
 
      errno = ret;
 
2503
    ret_errno = raise_privileges();
 
2504
    if(ret_errno != 0){
 
2505
      errno = ret_errno;
2595
2506
      perror_plus("Failed to raise privileges");
2596
2507
    } else {
2597
2508
      struct stat st;
2661
2572
      }
2662
2573
      
2663
2574
      /* Lower privileges */
2664
 
      ret = lower_privileges();
2665
 
      if(ret != 0){
2666
 
        errno = ret;
 
2575
      ret_errno = lower_privileges();
 
2576
      if(ret_errno != 0){
 
2577
        errno = ret_errno;
2667
2578
        perror_plus("Failed to lower privileges");
2668
2579
      }
2669
2580
    }
2997
2908
    
2998
2909
    /* Allocate a new server */
2999
2910
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
3000
 
                                 &config, NULL, NULL, &ret);
 
2911
                                 &config, NULL, NULL, &ret_errno);
3001
2912
    
3002
2913
    /* Free the Avahi configuration data */
3003
2914
    avahi_server_config_free(&config);
3006
2917
  /* Check if creating the Avahi server object succeeded */
3007
2918
  if(mc.server == NULL){
3008
2919
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
3009
 
                 avahi_strerror(ret));
 
2920
                 avahi_strerror(ret_errno));
3010
2921
    exitcode = EX_UNAVAILABLE;
3011
2922
    goto end;
3012
2923
  }
3047
2958
 end:
3048
2959
  
3049
2960
  if(debug){
3050
 
    if(signal_received){
3051
 
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
3052
 
                   argv[0], signal_received,
3053
 
                   strsignal(signal_received));
3054
 
    } else {
3055
 
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
3056
 
    }
 
2961
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
3057
2962
  }
3058
2963
  
3059
2964
  /* Cleanup things */
3070
2975
  
3071
2976
  if(gnutls_initialized){
3072
2977
    gnutls_certificate_free_credentials(mc.cred);
 
2978
    gnutls_global_deinit();
3073
2979
    gnutls_dh_params_deinit(mc.dh_params);
3074
2980
  }
3075
2981
  
3098
3004
  
3099
3005
  /* Re-raise privileges */
3100
3006
  {
3101
 
    ret = raise_privileges();
3102
 
    if(ret != 0){
3103
 
      errno = ret;
 
3007
    ret_errno = raise_privileges();
 
3008
    if(ret_errno != 0){
 
3009
      errno = ret_errno;
3104
3010
      perror_plus("Failed to raise privileges");
3105
3011
    } else {
3106
3012
      
3111
3017
      /* Take down the network interfaces which were brought up */
3112
3018
      {
3113
3019
        char *interface = NULL;
3114
 
        while((interface = argz_next(interfaces_to_take_down,
3115
 
                                     interfaces_to_take_down_size,
3116
 
                                     interface))){
3117
 
          ret = take_down_interface(interface);
3118
 
          if(ret != 0){
3119
 
            errno = ret;
 
3020
        while((interface=argz_next(interfaces_to_take_down,
 
3021
                                   interfaces_to_take_down_size,
 
3022
                                   interface))){
 
3023
          ret_errno = take_down_interface(interface);
 
3024
          if(ret_errno != 0){
 
3025
            errno = ret_errno;
3120
3026
            perror_plus("Failed to take down interface");
3121
3027
          }
3122
3028
        }
3127
3033
      }
3128
3034
    }
3129
3035
    
3130
 
    ret = lower_privileges_permanently();
3131
 
    if(ret != 0){
3132
 
      errno = ret;
 
3036
    ret_errno = lower_privileges_permanently();
 
3037
    if(ret_errno != 0){
 
3038
      errno = ret_errno;
3133
3039
      perror_plus("Failed to lower privileges permanently");
3134
3040
    }
3135
3041
  }
3137
3043
  free(interfaces_to_take_down);
3138
3044
  free(interfaces_hooks);
3139
3045
  
3140
 
  void clean_dir_at(int base, const char * const dirname,
3141
 
                    uintmax_t level){
3142
 
    struct dirent **direntries = NULL;
3143
 
    int dret;
3144
 
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3145
 
                                                O_RDONLY
3146
 
                                                | O_NOFOLLOW
3147
 
                                                | O_DIRECTORY
3148
 
                                                | O_PATH));
3149
 
    if(dir_fd == -1){
3150
 
      perror_plus("open");
3151
 
      return;
3152
 
    }
3153
 
    int numentries = scandirat(dir_fd, ".", &direntries,
3154
 
                               notdotentries, alphasort);
3155
 
    if(numentries >= 0){
3156
 
      for(int i = 0; i < numentries; i++){
3157
 
        if(debug){
3158
 
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3159
 
                       dirname, direntries[i]->d_name);
3160
 
        }
3161
 
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3162
 
        if(dret == -1){
3163
 
          if(errno == EISDIR){
3164
 
              dret = unlinkat(dir_fd, direntries[i]->d_name,
3165
 
                              AT_REMOVEDIR);
3166
 
          }         
3167
 
          if((dret == -1) and (errno == ENOTEMPTY)
3168
 
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3169
 
                  == 0) and (level == 0)){
3170
 
            /* Recurse only in this special case */
3171
 
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3172
 
            dret = 0;
3173
 
          }
3174
 
          if((dret == -1) and (errno != ENOENT)){
3175
 
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3176
 
                         direntries[i]->d_name, strerror(errno));
3177
 
          }
3178
 
        }
3179
 
        free(direntries[i]);
3180
 
      }
3181
 
      
3182
 
      /* need to clean even if 0 because man page doesn't specify */
3183
 
      free(direntries);
3184
 
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
3185
 
      if(dret == -1 and errno != ENOENT){
3186
 
        perror_plus("rmdir");
3187
 
      }
3188
 
    } else {
3189
 
      perror_plus("scandirat");
3190
 
    }
3191
 
    close(dir_fd);
3192
 
  }
3193
 
  
3194
3046
  /* Removes the GPGME temp directory and all files inside */
3195
3047
  if(tempdir != NULL){
3196
 
    clean_dir_at(-1, tempdir, 0);
 
3048
    struct dirent **direntries = NULL;
 
3049
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY
 
3050
                                                  | O_NOFOLLOW
 
3051
                                                  | O_DIRECTORY
 
3052
                                                  | O_PATH));
 
3053
    if(tempdir_fd == -1){
 
3054
      perror_plus("open");
 
3055
    } else {
 
3056
#ifdef __GLIBC__
 
3057
#if __GLIBC_PREREQ(2, 15)
 
3058
      int numentries = scandirat(tempdir_fd, ".", &direntries,
 
3059
                                 notdotentries, alphasort);
 
3060
#else  /* not __GLIBC_PREREQ(2, 15) */
 
3061
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
3062
                               alphasort);
 
3063
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
3064
#else   /* not __GLIBC__ */
 
3065
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
3066
                               alphasort);
 
3067
#endif  /* not __GLIBC__ */
 
3068
      if(numentries >= 0){
 
3069
        for(int i = 0; i < numentries; i++){
 
3070
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
 
3071
          if(ret == -1){
 
3072
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
 
3073
                         " \"%s\", 0): %s\n", tempdir,
 
3074
                         direntries[i]->d_name, strerror(errno));
 
3075
          }
 
3076
          free(direntries[i]);
 
3077
        }
 
3078
        
 
3079
        /* need to clean even if 0 because man page doesn't specify */
 
3080
        free(direntries);
 
3081
        if(numentries == -1){
 
3082
          perror_plus("scandir");
 
3083
        }
 
3084
        ret = rmdir(tempdir);
 
3085
        if(ret == -1 and errno != ENOENT){
 
3086
          perror_plus("rmdir");
 
3087
        }
 
3088
      }
 
3089
      close(tempdir_fd);
 
3090
    }
3197
3091
  }
3198
3092
  
3199
3093
  if(quit_now){