/mandos/release

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

« back to all changes in this revision

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

  • Committer: teddy at recompile
  • Date: 2020-12-03 20:30:45 UTC
  • mto: This revision was merged to the branch mainline in revision 404.
  • Revision ID: teddy@recompile.se-20201203203045-iqd6nq9y5nwalh1x
Minor fix of a test function

In dracut-module/password-agent, the test function
test_send_password_to_socket_EMSGSIZE() (which tests that the
send_password_to_socket() task function aborts properly when getting
EMSGSIZE when writing to the password socket), part of the test code
is supposed to find a message size which definitely does trigger
EMSGSIZE when send()ing to a socket.  Without a "break" in the proper
place, however, the size given is always exactly 1024 bytes too large.

This is very probably not a problem, since a too large message will
still be too large if it is increased by 1024 bytes, and send(2) in
practice checks the size before reading the buffer.  The biggest issue
would be if some version of send(2) would try to look at the last 1024
bytes of the message buffer before checking the message size; this
would then lead to a buffer over-read when running this test function.
(But even then there would be no security implications since the tests
are not run in the normal operation of the program.)

* dracut-module/password-agent.c
  (test_send_password_to_socket_EMSGSIZE): Break out early when ssret
  < 0 and errno == EMSGSIZE; don't allow loop to increase message_size
  again.

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