/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-2019 Teddy Hogeborn
13
 
 * Copyright © 2008-2019 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,
123
117
                                   gnutls_*
124
118
                                   init_gnutls_session(),
125
119
                                   GNUTLS_* */
126
 
#if GNUTLS_VERSION_NUMBER < 0x030600
127
120
#include <gnutls/openpgp.h>
128
121
                         /* gnutls_certificate_set_openpgp_key_file(),
129
122
                            GNUTLS_OPENPGP_FMT_BASE64 */
130
 
#elif GNUTLS_VERSION_NUMBER >= 0x030606
131
 
#include <gnutls/x509.h>        /* gnutls_pkcs_encrypt_flags_t,
132
 
                                 GNUTLS_PKCS_PLAIN,
133
 
                                 GNUTLS_PKCS_NULL_PASSWORD */
134
 
#endif
135
123
 
136
124
/* GPGME */
137
125
#include <gpgme.h>              /* All GPGME types, constants and
145
133
#define PATHDIR "/conf/conf.d/mandos"
146
134
#define SECKEY "seckey.txt"
147
135
#define PUBKEY "pubkey.txt"
148
 
#define TLS_PRIVKEY "tls-privkey.pem"
149
 
#define TLS_PUBKEY "tls-pubkey.pem"
150
136
#define HOOKDIR "/lib/mandos/network-hooks.d"
151
137
 
152
138
bool debug = false;
280
266
  return true;
281
267
}
282
268
 
283
 
/* Set effective uid to 0, return errno */
284
 
__attribute__((warn_unused_result))
285
 
int raise_privileges(void){
286
 
  int old_errno = errno;
287
 
  int ret = 0;
288
 
  if(seteuid(0) == -1){
289
 
    ret = errno;
290
 
  }
291
 
  errno = old_errno;
292
 
  return ret;
293
 
}
294
 
 
295
 
/* Set effective and real user ID to 0.  Return errno. */
296
 
__attribute__((warn_unused_result))
297
 
int raise_privileges_permanently(void){
298
 
  int old_errno = errno;
299
 
  int ret = raise_privileges();
300
 
  if(ret != 0){
301
 
    errno = old_errno;
302
 
    return ret;
303
 
  }
304
 
  if(setuid(0) == -1){
305
 
    ret = errno;
306
 
  }
307
 
  errno = old_errno;
308
 
  return ret;
309
 
}
310
 
 
311
 
/* Set effective user ID to unprivileged saved user ID */
312
 
__attribute__((warn_unused_result))
313
 
int lower_privileges(void){
314
 
  int old_errno = errno;
315
 
  int ret = 0;
316
 
  if(seteuid(uid) == -1){
317
 
    ret = errno;
318
 
  }
319
 
  errno = old_errno;
320
 
  return ret;
321
 
}
322
 
 
323
 
/* Lower privileges permanently */
324
 
__attribute__((warn_unused_result))
325
 
int lower_privileges_permanently(void){
326
 
  int old_errno = errno;
327
 
  int ret = 0;
328
 
  if(setuid(uid) == -1){
329
 
    ret = errno;
330
 
  }
331
 
  errno = old_errno;
332
 
  return ret;
333
 
}
334
 
 
335
269
/* 
336
270
 * Initialize GPGME.
337
271
 */
357
291
      return false;
358
292
    }
359
293
    
360
 
    /* Workaround for systems without a real-time clock; see also
361
 
       Debian bug #894495: <https://bugs.debian.org/894495> */
362
 
    do {
363
 
      {
364
 
        time_t currtime = time(NULL);
365
 
        if(currtime != (time_t)-1){
366
 
          struct tm tm;
367
 
          if(gmtime_r(&currtime, &tm) == NULL) {
368
 
            perror_plus("gmtime_r");
369
 
            break;
370
 
          }
371
 
          if(tm.tm_year != 70 or tm.tm_mon != 0){
372
 
            break;
373
 
          }
374
 
          if(debug){
375
 
            fprintf_plus(stderr, "System clock is January 1970");
376
 
          }
377
 
        } else {
378
 
          if(debug){
379
 
            fprintf_plus(stderr, "System clock is invalid");
380
 
          }
381
 
        }
382
 
      }
383
 
      struct stat keystat;
384
 
      ret = fstat(fd, &keystat);
385
 
      if(ret != 0){
386
 
        perror_plus("fstat");
387
 
        break;
388
 
      }
389
 
      ret = raise_privileges();
390
 
      if(ret != 0){
391
 
        errno = ret;
392
 
        perror_plus("Failed to raise privileges");
393
 
        break;
394
 
      }
395
 
      if(debug){
396
 
        fprintf_plus(stderr,
397
 
                     "Setting system clock to key file mtime");
398
 
      }
399
 
      time_t keytime = keystat.st_mtim.tv_sec;
400
 
      if(stime(&keytime) != 0){
401
 
        perror_plus("stime");
402
 
      }
403
 
      ret = lower_privileges();
404
 
      if(ret != 0){
405
 
        errno = ret;
406
 
        perror_plus("Failed to lower privileges");
407
 
      }
408
 
    } while(false);
409
 
 
410
294
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
411
295
    if(rc != GPG_ERR_NO_ERROR){
412
296
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
420
304
                   gpgme_strsource(rc), gpgme_strerror(rc));
421
305
      return false;
422
306
    }
423
 
    {
424
 
      gpgme_import_result_t import_result
425
 
        = gpgme_op_import_result(mc->ctx);
426
 
      if((import_result->imported < 1
427
 
          or import_result->not_imported > 0)
428
 
         and import_result->unchanged == 0){
429
 
        fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
430
 
        fprintf_plus(stderr,
431
 
                     "The total number of considered keys: %d\n",
432
 
                     import_result->considered);
433
 
        fprintf_plus(stderr,
434
 
                     "The number of keys without user ID: %d\n",
435
 
                     import_result->no_user_id);
436
 
        fprintf_plus(stderr,
437
 
                     "The total number of imported keys: %d\n",
438
 
                     import_result->imported);
439
 
        fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
440
 
                     import_result->imported_rsa);
441
 
        fprintf_plus(stderr, "The number of unchanged keys: %d\n",
442
 
                     import_result->unchanged);
443
 
        fprintf_plus(stderr, "The number of new user IDs: %d\n",
444
 
                     import_result->new_user_ids);
445
 
        fprintf_plus(stderr, "The number of new sub keys: %d\n",
446
 
                     import_result->new_sub_keys);
447
 
        fprintf_plus(stderr, "The number of new signatures: %d\n",
448
 
                     import_result->new_signatures);
449
 
        fprintf_plus(stderr, "The number of new revocations: %d\n",
450
 
                     import_result->new_revocations);
451
 
        fprintf_plus(stderr,
452
 
                     "The total number of secret keys read: %d\n",
453
 
                     import_result->secret_read);
454
 
        fprintf_plus(stderr,
455
 
                     "The number of imported secret keys: %d\n",
456
 
                     import_result->secret_imported);
457
 
        fprintf_plus(stderr,
458
 
                     "The number of unchanged secret keys: %d\n",
459
 
                     import_result->secret_unchanged);
460
 
        fprintf_plus(stderr, "The number of keys not imported: %d\n",
461
 
                     import_result->not_imported);
462
 
        for(gpgme_import_status_t import_status
463
 
              = import_result->imports;
464
 
            import_status != NULL;
465
 
            import_status = import_status->next){
466
 
          fprintf_plus(stderr, "Import status for key: %s\n",
467
 
                       import_status->fpr);
468
 
          if(import_status->result != GPG_ERR_NO_ERROR){
469
 
            fprintf_plus(stderr, "Import result: %s: %s\n",
470
 
                         gpgme_strsource(import_status->result),
471
 
                         gpgme_strerror(import_status->result));
472
 
          }
473
 
          fprintf_plus(stderr, "Key status:\n");
474
 
          fprintf_plus(stderr,
475
 
                       import_status->status & GPGME_IMPORT_NEW
476
 
                       ? "The key was new.\n"
477
 
                       : "The key was not new.\n");
478
 
          fprintf_plus(stderr,
479
 
                       import_status->status & GPGME_IMPORT_UID
480
 
                       ? "The key contained new user IDs.\n"
481
 
                       : "The key did not contain new user IDs.\n");
482
 
          fprintf_plus(stderr,
483
 
                       import_status->status & GPGME_IMPORT_SIG
484
 
                       ? "The key contained new signatures.\n"
485
 
                       : "The key did not contain new signatures.\n");
486
 
          fprintf_plus(stderr,
487
 
                       import_status->status & GPGME_IMPORT_SUBKEY
488
 
                       ? "The key contained new sub keys.\n"
489
 
                       : "The key did not contain new sub keys.\n");
490
 
          fprintf_plus(stderr,
491
 
                       import_status->status & GPGME_IMPORT_SECRET
492
 
                       ? "The key contained a secret key.\n"
493
 
                       : "The key did not contain a secret key.\n");
494
 
        }
495
 
        return false;
496
 
      }
497
 
    }
498
307
    
499
308
    ret = close(fd);
500
309
    if(ret == -1){
541
350
  /* Create new GPGME "context" */
542
351
  rc = gpgme_new(&(mc->ctx));
543
352
  if(rc != GPG_ERR_NO_ERROR){
544
 
    fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
545
 
                 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));
546
356
    return false;
547
357
  }
548
358
  
584
394
  /* Create new empty GPGME data buffer for the plaintext */
585
395
  rc = gpgme_data_new(&dh_plain);
586
396
  if(rc != GPG_ERR_NO_ERROR){
587
 
    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",
588
399
                 gpgme_strsource(rc), gpgme_strerror(rc));
589
400
    gpgme_data_release(dh_crypto);
590
401
    return -1;
603
414
      if(result == NULL){
604
415
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
605
416
      } else {
606
 
        if(result->unsupported_algorithm != NULL) {
607
 
          fprintf_plus(stderr, "Unsupported algorithm: %s\n",
608
 
                       result->unsupported_algorithm);
609
 
        }
610
 
        fprintf_plus(stderr, "Wrong key usage: %s\n",
611
 
                     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);
612
421
        if(result->file_name != NULL){
613
422
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
614
423
        }
615
 
 
616
 
        for(gpgme_recipient_t r = result->recipients; r != NULL;
617
 
            r = r->next){
 
424
        gpgme_recipient_t recipient;
 
425
        recipient = result->recipients;
 
426
        while(recipient != NULL){
618
427
          fprintf_plus(stderr, "Public key algorithm: %s\n",
619
 
                       gpgme_pubkey_algo_name(r->pubkey_algo));
620
 
          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);
621
431
          fprintf_plus(stderr, "Secret key available: %s\n",
622
 
                       r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
 
432
                       recipient->status == GPG_ERR_NO_SECKEY
 
433
                       ? "No" : "Yes");
 
434
          recipient = recipient->next;
623
435
        }
624
436
      }
625
437
    }
701
513
  fprintf_plus(stderr, "GnuTLS: %s", string);
702
514
}
703
515
 
704
 
__attribute__((nonnull(1, 2, 4), warn_unused_result))
 
516
__attribute__((nonnull, warn_unused_result))
705
517
static int init_gnutls_global(const char *pubkeyfilename,
706
518
                              const char *seckeyfilename,
707
519
                              const char *dhparamsfilename,
708
520
                              mandos_context *mc){
709
521
  int ret;
 
522
  unsigned int uret;
710
523
  
711
524
  if(debug){
712
525
    fprintf_plus(stderr, "Initializing GnuTLS\n");
713
526
  }
714
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
  
715
535
  if(debug){
716
536
    /* "Use a log level over 10 to enable all debugging options."
717
537
     * - GnuTLS manual
725
545
  if(ret != GNUTLS_E_SUCCESS){
726
546
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
727
547
                 safer_gnutls_strerror(ret));
 
548
    gnutls_global_deinit();
728
549
    return -1;
729
550
  }
730
551
  
731
552
  if(debug){
732
 
    fprintf_plus(stderr, "Attempting to use public key %s and"
733
 
                 " private key %s as GnuTLS credentials\n",
 
553
    fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
 
554
                 " secret key %s as GnuTLS credentials\n",
734
555
                 pubkeyfilename,
735
556
                 seckeyfilename);
736
557
  }
737
558
  
738
 
#if GNUTLS_VERSION_NUMBER >= 0x030606
739
 
  ret = gnutls_certificate_set_rawpk_key_file
740
 
    (mc->cred, pubkeyfilename, seckeyfilename,
741
 
     GNUTLS_X509_FMT_PEM,       /* format */
742
 
     NULL,                      /* pass */
743
 
     /* key_usage */
744
 
     GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT,
745
 
     NULL,                      /* names */
746
 
     0,                         /* names_length */
747
 
     /* privkey_flags */
748
 
     GNUTLS_PKCS_PLAIN | GNUTLS_PKCS_NULL_PASSWORD,
749
 
     0);                        /* pkcs11_flags */
750
 
#elif GNUTLS_VERSION_NUMBER < 0x030600
751
559
  ret = gnutls_certificate_set_openpgp_key_file
752
560
    (mc->cred, pubkeyfilename, seckeyfilename,
753
561
     GNUTLS_OPENPGP_FMT_BASE64);
754
 
#else
755
 
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
756
 
#endif
757
562
  if(ret != GNUTLS_E_SUCCESS){
758
563
    fprintf_plus(stderr,
759
 
                 "Error[%d] while reading the key pair ('%s',"
 
564
                 "Error[%d] while reading the OpenPGP key pair ('%s',"
760
565
                 " '%s')\n", ret, pubkeyfilename, seckeyfilename);
761
566
    fprintf_plus(stderr, "The GnuTLS error is: %s\n",
762
567
                 safer_gnutls_strerror(ret));
810
615
        }
811
616
        params.size += (unsigned int)bytes_read;
812
617
      }
813
 
      ret = close(dhpfile);
814
 
      if(ret == -1){
815
 
        perror_plus("close");
816
 
      }
817
618
      if(params.data == NULL){
818
619
        dhparamsfilename = NULL;
819
620
      }
828
629
                     safer_gnutls_strerror(ret));
829
630
        dhparamsfilename = NULL;
830
631
      }
831
 
      free(params.data);
832
632
    } while(false);
833
633
  }
834
634
  if(dhparamsfilename == NULL){
835
635
    if(mc->dh_bits == 0){
836
 
#if GNUTLS_VERSION_NUMBER < 0x030600
837
636
      /* Find out the optimal number of DH bits */
838
637
      /* Try to read the private key file */
839
638
      gnutls_datum_t buffer = { .data = NULL, .size = 0 };
919
718
          }
920
719
        }
921
720
      }
922
 
      unsigned int uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
 
721
      uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
923
722
      if(uret != 0){
924
723
        mc->dh_bits = uret;
925
724
        if(debug){
937
736
                     safer_gnutls_strerror(ret));
938
737
        goto globalfail;
939
738
      }
940
 
#endif
941
 
    } else {                    /* dh_bits != 0 */
942
 
      if(debug){
943
 
        fprintf_plus(stderr, "DH bits explicitly set to %u\n",
944
 
                     mc->dh_bits);
945
 
      }
946
 
      ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
947
 
      if(ret != GNUTLS_E_SUCCESS){
948
 
        fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
949
 
                     " bits): %s\n", mc->dh_bits,
950
 
                     safer_gnutls_strerror(ret));
951
 
        goto globalfail;
952
 
      }
953
 
      gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
 
739
    } else if(debug){
 
740
      fprintf_plus(stderr, "DH bits explicitly set to %u\n",
 
741
                   mc->dh_bits);
 
742
    }
 
743
    ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
 
744
    if(ret != GNUTLS_E_SUCCESS){
 
745
      fprintf_plus(stderr, "Error in GnuTLS prime generation (%u"
 
746
                   " bits): %s\n", mc->dh_bits,
 
747
                   safer_gnutls_strerror(ret));
 
748
      goto globalfail;
954
749
    }
955
750
  }
 
751
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
956
752
  
957
753
  return 0;
958
754
  
959
755
 globalfail:
960
756
  
961
757
  gnutls_certificate_free_credentials(mc->cred);
 
758
  gnutls_global_deinit();
962
759
  gnutls_dh_params_deinit(mc->dh_params);
963
760
  return -1;
964
761
}
969
766
  int ret;
970
767
  /* GnuTLS session creation */
971
768
  do {
972
 
    ret = gnutls_init(session, (GNUTLS_SERVER
973
 
#if GNUTLS_VERSION_NUMBER >= 0x030506
974
 
                                | GNUTLS_NO_TICKETS
975
 
#endif
976
 
#if GNUTLS_VERSION_NUMBER >= 0x030606
977
 
                                | GNUTLS_ENABLE_RAWPK
978
 
#endif
979
 
                                ));
 
769
    ret = gnutls_init(session, GNUTLS_SERVER);
980
770
    if(quit_now){
981
771
      return -1;
982
772
    }
1030
820
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
1031
821
                      __attribute__((unused)) const char *txt){}
1032
822
 
 
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
 
1033
875
/* Helper function to add_local_route() and delete_local_route() */
1034
876
__attribute__((nonnull, warn_unused_result))
1035
877
static bool add_delete_local_route(const bool add,
1074
916
      ret = setgid(0);
1075
917
      if(ret == -1){
1076
918
        perror_plus("setgid");
1077
 
        close(devnull);
1078
919
        _exit(EX_NOPERM);
1079
920
      }
1080
921
      /* Reset supplementary groups */
1082
923
      ret = setgroups(0, NULL);
1083
924
      if(ret == -1){
1084
925
        perror_plus("setgroups");
1085
 
        close(devnull);
1086
926
        _exit(EX_NOPERM);
1087
927
      }
1088
928
    }
1089
929
    ret = dup2(devnull, STDIN_FILENO);
1090
930
    if(ret == -1){
1091
931
      perror_plus("dup2(devnull, STDIN_FILENO)");
1092
 
      close(devnull);
1093
932
      _exit(EX_OSERR);
1094
933
    }
1095
934
    ret = close(devnull);
1096
935
    if(ret == -1){
1097
936
      perror_plus("close");
 
937
      _exit(EX_OSERR);
1098
938
    }
1099
939
    ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1100
940
    if(ret == -1){
1135
975
  }
1136
976
  if(pid == -1){
1137
977
    perror_plus("fork");
1138
 
    close(devnull);
1139
978
    return false;
1140
979
  }
1141
 
  ret = close(devnull);
1142
 
  if(ret == -1){
1143
 
    perror_plus("close");
1144
 
  }
1145
980
  int status;
1146
981
  pid_t pret = -1;
1147
982
  errno = 0;
1247
1082
    bool match = false;
1248
1083
    {
1249
1084
      char *interface = NULL;
1250
 
      while((interface = argz_next(mc->interfaces,
1251
 
                                   mc->interfaces_size,
1252
 
                                   interface))){
 
1085
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
 
1086
                                 interface))){
1253
1087
        if(if_nametoindex(interface) == (unsigned int)if_index){
1254
1088
          match = true;
1255
1089
          break;
1389
1223
                    sizeof(struct sockaddr_in));
1390
1224
    }
1391
1225
    if(ret < 0){
1392
 
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
 
1226
      if(errno == ENETUNREACH
1393
1227
         and if_index != AVAHI_IF_UNSPEC
1394
1228
         and connect_to == NULL
1395
1229
         and not route_added and
1408
1242
           with an explicit route added with the server's address.
1409
1243
           
1410
1244
           Avahi bug reference:
1411
 
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1245
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1412
1246
           https://bugs.debian.org/587961
1413
1247
        */
1414
1248
        if(debug){
1594
1428
                                               &decrypted_buffer, mc);
1595
1429
    if(decrypted_buffer_size >= 0){
1596
1430
      
1597
 
      clearerr(stdout);
1598
1431
      written = 0;
1599
1432
      while(written < (size_t) decrypted_buffer_size){
1600
1433
        if(quit_now){
1616
1449
        }
1617
1450
        written += (size_t)ret;
1618
1451
      }
1619
 
      ret = fflush(stdout);
1620
 
      if(ret != 0){
1621
 
        int e = errno;
1622
 
        if(debug){
1623
 
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1624
 
                       strerror(errno));
1625
 
        }
1626
 
        errno = e;
1627
 
        goto mandos_end;
1628
 
      }
1629
1452
      retval = 0;
1630
1453
    }
1631
1454
  }
1662
1485
  return retval;
1663
1486
}
1664
1487
 
 
1488
__attribute__((nonnull))
1665
1489
static void resolve_callback(AvahiSServiceResolver *r,
1666
1490
                             AvahiIfIndex interface,
1667
1491
                             AvahiProtocol proto,
1804
1628
__attribute__((nonnull, warn_unused_result))
1805
1629
bool get_flags(const char *ifname, struct ifreq *ifr){
1806
1630
  int ret;
1807
 
  int old_errno;
 
1631
  error_t ret_errno;
1808
1632
  
1809
1633
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1810
1634
  if(s < 0){
1811
 
    old_errno = errno;
 
1635
    ret_errno = errno;
1812
1636
    perror_plus("socket");
1813
 
    errno = old_errno;
 
1637
    errno = ret_errno;
1814
1638
    return false;
1815
1639
  }
1816
 
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1817
 
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
 
1640
  strcpy(ifr->ifr_name, ifname);
1818
1641
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1819
1642
  if(ret == -1){
1820
1643
    if(debug){
1821
 
      old_errno = errno;
 
1644
      ret_errno = errno;
1822
1645
      perror_plus("ioctl SIOCGIFFLAGS");
1823
 
      errno = old_errno;
1824
 
    }
1825
 
    if((close(s) == -1) and debug){
1826
 
      old_errno = errno;
1827
 
      perror_plus("close");
1828
 
      errno = old_errno;
 
1646
      errno = ret_errno;
1829
1647
    }
1830
1648
    return false;
1831
1649
  }
1832
 
  if((close(s) == -1) and debug){
1833
 
    old_errno = errno;
1834
 
    perror_plus("close");
1835
 
    errno = old_errno;
1836
 
  }
1837
1650
  return true;
1838
1651
}
1839
1652
 
2100
1913
      return;
2101
1914
    }
2102
1915
  }
2103
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
2104
 
  if(devnull == -1){
2105
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
2106
 
    return;
2107
 
  }
 
1916
#ifdef __GLIBC__
 
1917
#if __GLIBC_PREREQ(2, 15)
2108
1918
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
2109
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__ */
2110
1928
  if(numhooks == -1){
2111
1929
    perror_plus("scandir");
2112
 
    close(devnull);
2113
1930
    return;
2114
1931
  }
2115
1932
  struct dirent *direntry;
2116
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
  }
2117
1939
  for(int i = 0; i < numhooks; i++){
2118
1940
    direntry = direntries[i];
2119
1941
    if(debug){
2263
2085
}
2264
2086
 
2265
2087
__attribute__((nonnull, warn_unused_result))
2266
 
int bring_up_interface(const char *const interface,
2267
 
                       const float delay){
2268
 
  int old_errno = errno;
 
2088
error_t bring_up_interface(const char *const interface,
 
2089
                           const float delay){
 
2090
  error_t old_errno = errno;
2269
2091
  int ret;
2270
2092
  struct ifreq network;
2271
2093
  unsigned int if_index = if_nametoindex(interface);
2281
2103
  }
2282
2104
  
2283
2105
  if(not interface_is_up(interface)){
2284
 
    int ret_errno = 0;
2285
 
    int ioctl_errno = 0;
 
2106
    error_t ret_errno = 0, ioctl_errno = 0;
2286
2107
    if(not get_flags(interface, &network)){
2287
2108
      ret_errno = errno;
2288
2109
      fprintf_plus(stderr, "Failed to get flags for interface "
2375
2196
  
2376
2197
  /* Sleep checking until interface is running.
2377
2198
     Check every 0.25s, up to total time of delay */
2378
 
  for(int i = 0; i < delay * 4; i++){
 
2199
  for(int i=0; i < delay * 4; i++){
2379
2200
    if(interface_is_running(interface)){
2380
2201
      break;
2381
2202
    }
2391
2212
}
2392
2213
 
2393
2214
__attribute__((nonnull, warn_unused_result))
2394
 
int take_down_interface(const char *const interface){
2395
 
  int old_errno = errno;
 
2215
error_t take_down_interface(const char *const interface){
 
2216
  error_t old_errno = errno;
2396
2217
  struct ifreq network;
2397
2218
  unsigned int if_index = if_nametoindex(interface);
2398
2219
  if(if_index == 0){
2401
2222
    return ENXIO;
2402
2223
  }
2403
2224
  if(interface_is_up(interface)){
2404
 
    int ret_errno = 0;
2405
 
    int ioctl_errno = 0;
 
2225
    error_t ret_errno = 0, ioctl_errno = 0;
2406
2226
    if(not get_flags(interface, &network) and debug){
2407
2227
      ret_errno = errno;
2408
2228
      fprintf_plus(stderr, "Failed to get flags for interface "
2468
2288
 
2469
2289
int main(int argc, char *argv[]){
2470
2290
  mandos_context mc = { .server = NULL, .dh_bits = 0,
2471
 
#if GNUTLS_VERSION_NUMBER >= 0x030606
2472
 
                        .priority = "SECURE128:!CTYPE-X.509"
2473
 
                        ":+CTYPE-RAWPK:!RSA:!VERS-ALL:+VERS-TLS1.3"
2474
 
                        ":%PROFILE_ULTRA",
2475
 
#elif GNUTLS_VERSION_NUMBER < 0x030600
2476
2291
                        .priority = "SECURE256:!CTYPE-X.509"
2477
2292
                        ":+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256",
2478
 
#else
2479
 
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
2480
 
#endif
2481
2293
                        .current_server = NULL, .interfaces = NULL,
2482
2294
                        .interfaces_size = 0 };
2483
2295
  AvahiSServiceBrowser *sb = NULL;
2494
2306
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
2495
2307
  const char *seckey = PATHDIR "/" SECKEY;
2496
2308
  const char *pubkey = PATHDIR "/" PUBKEY;
2497
 
#if GNUTLS_VERSION_NUMBER >= 0x030606
2498
 
  const char *tls_privkey = PATHDIR "/" TLS_PRIVKEY;
2499
 
  const char *tls_pubkey = PATHDIR "/" TLS_PUBKEY;
2500
 
#endif
2501
2309
  const char *dh_params_file = NULL;
2502
2310
  char *interfaces_hooks = NULL;
2503
2311
  
2551
2359
      { .name = "pubkey", .key = 'p',
2552
2360
        .arg = "FILE",
2553
2361
        .doc = "OpenPGP public key file base name",
2554
 
        .group = 1 },
2555
 
      { .name = "tls-privkey", .key = 't',
2556
 
        .arg = "FILE",
2557
 
#if GNUTLS_VERSION_NUMBER >= 0x030606
2558
 
        .doc = "TLS private key file base name",
2559
 
#else
2560
 
        .doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
2561
 
#endif
2562
 
        .group = 1 },
2563
 
      { .name = "tls-pubkey", .key = 'T',
2564
 
        .arg = "FILE",
2565
 
#if GNUTLS_VERSION_NUMBER >= 0x030606
2566
 
        .doc = "TLS public key file base name",
2567
 
#else
2568
 
        .doc = "Dummy; ignored (requires GnuTLS 3.6.6)",
2569
 
#endif
2570
 
        .group = 1 },
 
2362
        .group = 2 },
2571
2363
      { .name = "dh-bits", .key = 129,
2572
2364
        .arg = "BITS",
2573
2365
        .doc = "Bit length of the prime number used in the"
2629
2421
      case 'p':                 /* --pubkey */
2630
2422
        pubkey = arg;
2631
2423
        break;
2632
 
      case 't':                 /* --tls-privkey */
2633
 
#if GNUTLS_VERSION_NUMBER >= 0x030606
2634
 
        tls_privkey = arg;
2635
 
#endif
2636
 
        break;
2637
 
      case 'T':                 /* --tls-pubkey */
2638
 
#if GNUTLS_VERSION_NUMBER >= 0x030606
2639
 
        tls_pubkey = arg;
2640
 
#endif
2641
 
        break;
2642
2424
      case 129:                 /* --dh-bits */
2643
2425
        errno = 0;
2644
2426
        tmpmax = strtoimax(arg, &tmp, 10);
2679
2461
        argp_state_help(state, state->out_stream,
2680
2462
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2681
2463
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
2682
 
        __builtin_unreachable();
2683
2464
      case -3:                  /* --usage */
2684
2465
        argp_state_help(state, state->out_stream,
2685
2466
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2686
 
        __builtin_unreachable();
2687
2467
      case 'V':                 /* --version */
2688
2468
        fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2689
2469
        exit(argp_err_exit_status);
2698
2478
                         .args_doc = "",
2699
2479
                         .doc = "Mandos client -- Get and decrypt"
2700
2480
                         " passwords from a Mandos server" };
2701
 
    ret_errno = argp_parse(&argp, argc, argv,
2702
 
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2703
 
    switch(ret_errno){
 
2481
    ret = argp_parse(&argp, argc, argv,
 
2482
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2483
    switch(ret){
2704
2484
    case 0:
2705
2485
      break;
2706
2486
    case ENOMEM:
2707
2487
    default:
2708
 
      errno = ret_errno;
 
2488
      errno = ret;
2709
2489
      perror_plus("argp_parse");
2710
2490
      exitcode = EX_OSERR;
2711
2491
      goto end;
2717
2497
  
2718
2498
  {
2719
2499
    /* Work around Debian bug #633582:
2720
 
       <https://bugs.debian.org/633582> */
 
2500
       <http://bugs.debian.org/633582> */
2721
2501
    
2722
2502
    /* Re-raise privileges */
2723
 
    ret = raise_privileges();
2724
 
    if(ret != 0){
2725
 
      errno = ret;
 
2503
    ret_errno = raise_privileges();
 
2504
    if(ret_errno != 0){
 
2505
      errno = ret_errno;
2726
2506
      perror_plus("Failed to raise privileges");
2727
2507
    } else {
2728
2508
      struct stat st;
2792
2572
      }
2793
2573
      
2794
2574
      /* Lower privileges */
2795
 
      ret = lower_privileges();
2796
 
      if(ret != 0){
2797
 
        errno = ret;
 
2575
      ret_errno = lower_privileges();
 
2576
      if(ret_errno != 0){
 
2577
        errno = ret_errno;
2798
2578
        perror_plus("Failed to lower privileges");
2799
2579
      }
2800
2580
    }
3000
2780
    goto end;
3001
2781
  }
3002
2782
  
3003
 
#if GNUTLS_VERSION_NUMBER >= 0x030606
3004
 
  ret = init_gnutls_global(tls_pubkey, tls_privkey, dh_params_file, &mc);
3005
 
#elif GNUTLS_VERSION_NUMBER < 0x030600
3006
2783
  ret = init_gnutls_global(pubkey, seckey, dh_params_file, &mc);
3007
 
#else
3008
 
#error "Needs GnuTLS 3.6.6 or later, or before 3.6.0"
3009
 
#endif
3010
2784
  if(ret == -1){
3011
2785
    fprintf_plus(stderr, "init_gnutls_global failed\n");
3012
2786
    exitcode = EX_UNAVAILABLE;
3134
2908
    
3135
2909
    /* Allocate a new server */
3136
2910
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
3137
 
                                 &config, NULL, NULL, &ret);
 
2911
                                 &config, NULL, NULL, &ret_errno);
3138
2912
    
3139
2913
    /* Free the Avahi configuration data */
3140
2914
    avahi_server_config_free(&config);
3143
2917
  /* Check if creating the Avahi server object succeeded */
3144
2918
  if(mc.server == NULL){
3145
2919
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
3146
 
                 avahi_strerror(ret));
 
2920
                 avahi_strerror(ret_errno));
3147
2921
    exitcode = EX_UNAVAILABLE;
3148
2922
    goto end;
3149
2923
  }
3184
2958
 end:
3185
2959
  
3186
2960
  if(debug){
3187
 
    if(signal_received){
3188
 
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
3189
 
                   argv[0], signal_received,
3190
 
                   strsignal(signal_received));
3191
 
    } else {
3192
 
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
3193
 
    }
 
2961
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
3194
2962
  }
3195
2963
  
3196
2964
  /* Cleanup things */
3207
2975
  
3208
2976
  if(gnutls_initialized){
3209
2977
    gnutls_certificate_free_credentials(mc.cred);
 
2978
    gnutls_global_deinit();
3210
2979
    gnutls_dh_params_deinit(mc.dh_params);
3211
2980
  }
3212
2981
  
3235
3004
  
3236
3005
  /* Re-raise privileges */
3237
3006
  {
3238
 
    ret = raise_privileges();
3239
 
    if(ret != 0){
3240
 
      errno = ret;
 
3007
    ret_errno = raise_privileges();
 
3008
    if(ret_errno != 0){
 
3009
      errno = ret_errno;
3241
3010
      perror_plus("Failed to raise privileges");
3242
3011
    } else {
3243
3012
      
3248
3017
      /* Take down the network interfaces which were brought up */
3249
3018
      {
3250
3019
        char *interface = NULL;
3251
 
        while((interface = argz_next(interfaces_to_take_down,
3252
 
                                     interfaces_to_take_down_size,
3253
 
                                     interface))){
3254
 
          ret = take_down_interface(interface);
3255
 
          if(ret != 0){
3256
 
            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;
3257
3026
            perror_plus("Failed to take down interface");
3258
3027
          }
3259
3028
        }
3264
3033
      }
3265
3034
    }
3266
3035
    
3267
 
    ret = lower_privileges_permanently();
3268
 
    if(ret != 0){
3269
 
      errno = ret;
 
3036
    ret_errno = lower_privileges_permanently();
 
3037
    if(ret_errno != 0){
 
3038
      errno = ret_errno;
3270
3039
      perror_plus("Failed to lower privileges permanently");
3271
3040
    }
3272
3041
  }
3274
3043
  free(interfaces_to_take_down);
3275
3044
  free(interfaces_hooks);
3276
3045
  
3277
 
  void clean_dir_at(int base, const char * const dirname,
3278
 
                    uintmax_t level){
3279
 
    struct dirent **direntries = NULL;
3280
 
    int dret;
3281
 
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3282
 
                                                O_RDONLY
3283
 
                                                | O_NOFOLLOW
3284
 
                                                | O_DIRECTORY
3285
 
                                                | O_PATH));
3286
 
    if(dir_fd == -1){
3287
 
      perror_plus("open");
3288
 
      return;
3289
 
    }
3290
 
    int numentries = scandirat(dir_fd, ".", &direntries,
3291
 
                               notdotentries, alphasort);
3292
 
    if(numentries >= 0){
3293
 
      for(int i = 0; i < numentries; i++){
3294
 
        if(debug){
3295
 
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3296
 
                       dirname, direntries[i]->d_name);
3297
 
        }
3298
 
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3299
 
        if(dret == -1){
3300
 
          if(errno == EISDIR){
3301
 
              dret = unlinkat(dir_fd, direntries[i]->d_name,
3302
 
                              AT_REMOVEDIR);
3303
 
          }         
3304
 
          if((dret == -1) and (errno == ENOTEMPTY)
3305
 
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3306
 
                  == 0) and (level == 0)){
3307
 
            /* Recurse only in this special case */
3308
 
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3309
 
            dret = 0;
3310
 
          }
3311
 
          if((dret == -1) and (errno != ENOENT)){
3312
 
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3313
 
                         direntries[i]->d_name, strerror(errno));
3314
 
          }
3315
 
        }
3316
 
        free(direntries[i]);
3317
 
      }
3318
 
      
3319
 
      /* need to clean even if 0 because man page doesn't specify */
3320
 
      free(direntries);
3321
 
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
3322
 
      if(dret == -1 and errno != ENOENT){
3323
 
        perror_plus("rmdir");
3324
 
      }
3325
 
    } else {
3326
 
      perror_plus("scandirat");
3327
 
    }
3328
 
    close(dir_fd);
3329
 
  }
3330
 
  
3331
3046
  /* Removes the GPGME temp directory and all files inside */
3332
3047
  if(tempdir != NULL){
3333
 
    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
    }
3334
3091
  }
3335
3092
  
3336
3093
  if(quit_now){