/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-08-02 09:36:40 UTC
  • Revision ID: teddy@recompile.se-20150802093640-nc0n17rbmqlbaxuf
Add D-Bus annotations on a few properties on the Client object.

The D-Bus property "Secret" on the interface
"se.recompile.Mandos.Client" should have the annotation
"org.freedesktop.DBus.Property.EmitsChangedSignal" set to
"invalidates".  Also, the properties "Created", "Fingerprint", "Name",
and "ObjectPath" should have the same annotation set to "const".

* mandos (ClientDBus.Name_dbus_property): Set annotation
                    "org.freedesktop.DBus.Property.EmitsChangedSignal"
                    to "const".
  (ClientDBus.Fingerprint_dbus_property): - '' -
  (ClientDBus.Created_dbus_property): - '' -
  (ClientDBus.ObjectPath_dbus_property): - '' -
  (ClientDBus.Secret_dbus_property): Set annotation
                    "org.freedesktop.DBus.Property.EmitsChangedSignal"
                    to "invalidates".

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,
272
266
  return true;
273
267
}
274
268
 
275
 
/* Set effective uid to 0, return errno */
276
 
__attribute__((warn_unused_result))
277
 
int raise_privileges(void){
278
 
  int old_errno = errno;
279
 
  int ret = 0;
280
 
  if(seteuid(0) == -1){
281
 
    ret = errno;
282
 
  }
283
 
  errno = old_errno;
284
 
  return ret;
285
 
}
286
 
 
287
 
/* Set effective and real user ID to 0.  Return errno. */
288
 
__attribute__((warn_unused_result))
289
 
int raise_privileges_permanently(void){
290
 
  int old_errno = errno;
291
 
  int ret = raise_privileges();
292
 
  if(ret != 0){
293
 
    errno = old_errno;
294
 
    return ret;
295
 
  }
296
 
  if(setuid(0) == -1){
297
 
    ret = errno;
298
 
  }
299
 
  errno = old_errno;
300
 
  return ret;
301
 
}
302
 
 
303
 
/* Set effective user ID to unprivileged saved user ID */
304
 
__attribute__((warn_unused_result))
305
 
int lower_privileges(void){
306
 
  int old_errno = errno;
307
 
  int ret = 0;
308
 
  if(seteuid(uid) == -1){
309
 
    ret = errno;
310
 
  }
311
 
  errno = old_errno;
312
 
  return ret;
313
 
}
314
 
 
315
 
/* Lower privileges permanently */
316
 
__attribute__((warn_unused_result))
317
 
int lower_privileges_permanently(void){
318
 
  int old_errno = errno;
319
 
  int ret = 0;
320
 
  if(setuid(uid) == -1){
321
 
    ret = errno;
322
 
  }
323
 
  errno = old_errno;
324
 
  return ret;
325
 
}
326
 
 
327
269
/* 
328
270
 * Initialize GPGME.
329
271
 */
349
291
      return false;
350
292
    }
351
293
    
352
 
    /* Workaround for systems without a real-time clock; see also
353
 
       Debian bug #894495: <https://bugs.debian.org/894495> */
354
 
    do {
355
 
      {
356
 
        time_t currtime = time(NULL);
357
 
        if(currtime != (time_t)-1){
358
 
          struct tm tm;
359
 
          if(gmtime_r(&currtime, &tm) == NULL) {
360
 
            perror_plus("gmtime_r");
361
 
            break;
362
 
          }
363
 
          if(tm.tm_year != 70 or tm.tm_mon != 0){
364
 
            break;
365
 
          }
366
 
          if(debug){
367
 
            fprintf_plus(stderr, "System clock is January 1970");
368
 
          }
369
 
        } else {
370
 
          if(debug){
371
 
            fprintf_plus(stderr, "System clock is invalid");
372
 
          }
373
 
        }
374
 
      }
375
 
      struct stat keystat;
376
 
      ret = fstat(fd, &keystat);
377
 
      if(ret != 0){
378
 
        perror_plus("fstat");
379
 
        break;
380
 
      }
381
 
      ret = raise_privileges();
382
 
      if(ret != 0){
383
 
        errno = ret;
384
 
        perror_plus("Failed to raise privileges");
385
 
        break;
386
 
      }
387
 
      if(debug){
388
 
        fprintf_plus(stderr,
389
 
                     "Setting system clock to key file mtime");
390
 
      }
391
 
      time_t keytime = keystat.st_mtim.tv_sec;
392
 
      if(stime(&keytime) != 0){
393
 
        perror_plus("stime");
394
 
      }
395
 
      ret = lower_privileges();
396
 
      if(ret != 0){
397
 
        errno = ret;
398
 
        perror_plus("Failed to lower privileges");
399
 
      }
400
 
    } while(false);
401
 
 
402
294
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
403
295
    if(rc != GPG_ERR_NO_ERROR){
404
296
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
412
304
                   gpgme_strsource(rc), gpgme_strerror(rc));
413
305
      return false;
414
306
    }
415
 
    {
416
 
      gpgme_import_result_t import_result
417
 
        = gpgme_op_import_result(mc->ctx);
418
 
      if((import_result->imported < 1
419
 
          or import_result->not_imported > 0)
420
 
         and import_result->unchanged == 0){
421
 
        fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
422
 
        fprintf_plus(stderr,
423
 
                     "The total number of considered keys: %d\n",
424
 
                     import_result->considered);
425
 
        fprintf_plus(stderr,
426
 
                     "The number of keys without user ID: %d\n",
427
 
                     import_result->no_user_id);
428
 
        fprintf_plus(stderr,
429
 
                     "The total number of imported keys: %d\n",
430
 
                     import_result->imported);
431
 
        fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
432
 
                     import_result->imported_rsa);
433
 
        fprintf_plus(stderr, "The number of unchanged keys: %d\n",
434
 
                     import_result->unchanged);
435
 
        fprintf_plus(stderr, "The number of new user IDs: %d\n",
436
 
                     import_result->new_user_ids);
437
 
        fprintf_plus(stderr, "The number of new sub keys: %d\n",
438
 
                     import_result->new_sub_keys);
439
 
        fprintf_plus(stderr, "The number of new signatures: %d\n",
440
 
                     import_result->new_signatures);
441
 
        fprintf_plus(stderr, "The number of new revocations: %d\n",
442
 
                     import_result->new_revocations);
443
 
        fprintf_plus(stderr,
444
 
                     "The total number of secret keys read: %d\n",
445
 
                     import_result->secret_read);
446
 
        fprintf_plus(stderr,
447
 
                     "The number of imported secret keys: %d\n",
448
 
                     import_result->secret_imported);
449
 
        fprintf_plus(stderr,
450
 
                     "The number of unchanged secret keys: %d\n",
451
 
                     import_result->secret_unchanged);
452
 
        fprintf_plus(stderr, "The number of keys not imported: %d\n",
453
 
                     import_result->not_imported);
454
 
        for(gpgme_import_status_t import_status
455
 
              = import_result->imports;
456
 
            import_status != NULL;
457
 
            import_status = import_status->next){
458
 
          fprintf_plus(stderr, "Import status for key: %s\n",
459
 
                       import_status->fpr);
460
 
          if(import_status->result != GPG_ERR_NO_ERROR){
461
 
            fprintf_plus(stderr, "Import result: %s: %s\n",
462
 
                         gpgme_strsource(import_status->result),
463
 
                         gpgme_strerror(import_status->result));
464
 
          }
465
 
          fprintf_plus(stderr, "Key status:\n");
466
 
          fprintf_plus(stderr,
467
 
                       import_status->status & GPGME_IMPORT_NEW
468
 
                       ? "The key was new.\n"
469
 
                       : "The key was not new.\n");
470
 
          fprintf_plus(stderr,
471
 
                       import_status->status & GPGME_IMPORT_UID
472
 
                       ? "The key contained new user IDs.\n"
473
 
                       : "The key did not contain new user IDs.\n");
474
 
          fprintf_plus(stderr,
475
 
                       import_status->status & GPGME_IMPORT_SIG
476
 
                       ? "The key contained new signatures.\n"
477
 
                       : "The key did not contain new signatures.\n");
478
 
          fprintf_plus(stderr,
479
 
                       import_status->status & GPGME_IMPORT_SUBKEY
480
 
                       ? "The key contained new sub keys.\n"
481
 
                       : "The key did not contain new sub keys.\n");
482
 
          fprintf_plus(stderr,
483
 
                       import_status->status & GPGME_IMPORT_SECRET
484
 
                       ? "The key contained a secret key.\n"
485
 
                       : "The key did not contain a secret key.\n");
486
 
        }
487
 
        return false;
488
 
      }
489
 
    }
490
307
    
491
308
    ret = close(fd);
492
309
    if(ret == -1){
533
350
  /* Create new GPGME "context" */
534
351
  rc = gpgme_new(&(mc->ctx));
535
352
  if(rc != GPG_ERR_NO_ERROR){
536
 
    fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
537
 
                 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));
538
356
    return false;
539
357
  }
540
358
  
576
394
  /* Create new empty GPGME data buffer for the plaintext */
577
395
  rc = gpgme_data_new(&dh_plain);
578
396
  if(rc != GPG_ERR_NO_ERROR){
579
 
    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",
580
399
                 gpgme_strsource(rc), gpgme_strerror(rc));
581
400
    gpgme_data_release(dh_crypto);
582
401
    return -1;
595
414
      if(result == NULL){
596
415
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
597
416
      } else {
598
 
        if(result->unsupported_algorithm != NULL) {
599
 
          fprintf_plus(stderr, "Unsupported algorithm: %s\n",
600
 
                       result->unsupported_algorithm);
601
 
        }
602
 
        fprintf_plus(stderr, "Wrong key usage: %s\n",
603
 
                     result->wrong_key_usage ? "Yes" : "No");
 
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);
604
421
        if(result->file_name != NULL){
605
422
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
606
423
        }
607
 
 
608
 
        for(gpgme_recipient_t r = result->recipients; r != NULL;
609
 
            r = r->next){
 
424
        gpgme_recipient_t recipient;
 
425
        recipient = result->recipients;
 
426
        while(recipient != NULL){
610
427
          fprintf_plus(stderr, "Public key algorithm: %s\n",
611
 
                       gpgme_pubkey_algo_name(r->pubkey_algo));
612
 
          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);
613
431
          fprintf_plus(stderr, "Secret key available: %s\n",
614
 
                       r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
 
432
                       recipient->status == GPG_ERR_NO_SECKEY
 
433
                       ? "No" : "Yes");
 
434
          recipient = recipient->next;
615
435
        }
616
436
      }
617
437
    }
693
513
  fprintf_plus(stderr, "GnuTLS: %s", string);
694
514
}
695
515
 
696
 
__attribute__((nonnull(1, 2, 4), warn_unused_result))
 
516
__attribute__((nonnull, warn_unused_result))
697
517
static int init_gnutls_global(const char *pubkeyfilename,
698
518
                              const char *seckeyfilename,
699
519
                              const char *dhparamsfilename,
705
525
    fprintf_plus(stderr, "Initializing GnuTLS\n");
706
526
  }
707
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
  
708
535
  if(debug){
709
536
    /* "Use a log level over 10 to enable all debugging options."
710
537
     * - GnuTLS manual
718
545
  if(ret != GNUTLS_E_SUCCESS){
719
546
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
720
547
                 safer_gnutls_strerror(ret));
 
548
    gnutls_global_deinit();
721
549
    return -1;
722
550
  }
723
551
  
787
615
        }
788
616
        params.size += (unsigned int)bytes_read;
789
617
      }
790
 
      ret = close(dhpfile);
791
 
      if(ret == -1){
792
 
        perror_plus("close");
793
 
      }
794
618
      if(params.data == NULL){
795
619
        dhparamsfilename = NULL;
796
620
      }
805
629
                     safer_gnutls_strerror(ret));
806
630
        dhparamsfilename = NULL;
807
631
      }
808
 
      free(params.data);
809
632
    } while(false);
810
633
  }
811
634
  if(dhparamsfilename == NULL){
932
755
 globalfail:
933
756
  
934
757
  gnutls_certificate_free_credentials(mc->cred);
 
758
  gnutls_global_deinit();
935
759
  gnutls_dh_params_deinit(mc->dh_params);
936
760
  return -1;
937
761
}
996
820
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
997
821
                      __attribute__((unused)) const char *txt){}
998
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
 
999
875
/* Helper function to add_local_route() and delete_local_route() */
1000
876
__attribute__((nonnull, warn_unused_result))
1001
877
static bool add_delete_local_route(const bool add,
1206
1082
    bool match = false;
1207
1083
    {
1208
1084
      char *interface = NULL;
1209
 
      while((interface = argz_next(mc->interfaces,
1210
 
                                   mc->interfaces_size,
1211
 
                                   interface))){
 
1085
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
 
1086
                                 interface))){
1212
1087
        if(if_nametoindex(interface) == (unsigned int)if_index){
1213
1088
          match = true;
1214
1089
          break;
1348
1223
                    sizeof(struct sockaddr_in));
1349
1224
    }
1350
1225
    if(ret < 0){
1351
 
      if(((errno == ENETUNREACH) or (errno == EHOSTUNREACH))
 
1226
      if(errno == ENETUNREACH
1352
1227
         and if_index != AVAHI_IF_UNSPEC
1353
1228
         and connect_to == NULL
1354
1229
         and not route_added and
1367
1242
           with an explicit route added with the server's address.
1368
1243
           
1369
1244
           Avahi bug reference:
1370
 
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1245
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1371
1246
           https://bugs.debian.org/587961
1372
1247
        */
1373
1248
        if(debug){
1553
1428
                                               &decrypted_buffer, mc);
1554
1429
    if(decrypted_buffer_size >= 0){
1555
1430
      
1556
 
      clearerr(stdout);
1557
1431
      written = 0;
1558
1432
      while(written < (size_t) decrypted_buffer_size){
1559
1433
        if(quit_now){
1575
1449
        }
1576
1450
        written += (size_t)ret;
1577
1451
      }
1578
 
      ret = fflush(stdout);
1579
 
      if(ret != 0){
1580
 
        int e = errno;
1581
 
        if(debug){
1582
 
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1583
 
                       strerror(errno));
1584
 
        }
1585
 
        errno = e;
1586
 
        goto mandos_end;
1587
 
      }
1588
1452
      retval = 0;
1589
1453
    }
1590
1454
  }
1621
1485
  return retval;
1622
1486
}
1623
1487
 
 
1488
__attribute__((nonnull))
1624
1489
static void resolve_callback(AvahiSServiceResolver *r,
1625
1490
                             AvahiIfIndex interface,
1626
1491
                             AvahiProtocol proto,
1763
1628
__attribute__((nonnull, warn_unused_result))
1764
1629
bool get_flags(const char *ifname, struct ifreq *ifr){
1765
1630
  int ret;
1766
 
  int old_errno;
 
1631
  error_t ret_errno;
1767
1632
  
1768
1633
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1769
1634
  if(s < 0){
1770
 
    old_errno = errno;
 
1635
    ret_errno = errno;
1771
1636
    perror_plus("socket");
1772
 
    errno = old_errno;
 
1637
    errno = ret_errno;
1773
1638
    return false;
1774
1639
  }
1775
 
  strncpy(ifr->ifr_name, ifname, IF_NAMESIZE);
1776
 
  ifr->ifr_name[IF_NAMESIZE-1] = '\0'; /* NUL terminate */
 
1640
  strcpy(ifr->ifr_name, ifname);
1777
1641
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1778
1642
  if(ret == -1){
1779
1643
    if(debug){
1780
 
      old_errno = errno;
 
1644
      ret_errno = errno;
1781
1645
      perror_plus("ioctl SIOCGIFFLAGS");
1782
 
      errno = old_errno;
1783
 
    }
1784
 
    if((close(s) == -1) and debug){
1785
 
      old_errno = errno;
1786
 
      perror_plus("close");
1787
 
      errno = old_errno;
 
1646
      errno = ret_errno;
1788
1647
    }
1789
1648
    return false;
1790
1649
  }
1791
 
  if((close(s) == -1) and debug){
1792
 
    old_errno = errno;
1793
 
    perror_plus("close");
1794
 
    errno = old_errno;
1795
 
  }
1796
1650
  return true;
1797
1651
}
1798
1652
 
2059
1913
      return;
2060
1914
    }
2061
1915
  }
2062
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
2063
 
  if(devnull == -1){
2064
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
2065
 
    return;
2066
 
  }
 
1916
#ifdef __GLIBC__
 
1917
#if __GLIBC_PREREQ(2, 15)
2067
1918
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
2068
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__ */
2069
1928
  if(numhooks == -1){
2070
1929
    perror_plus("scandir");
2071
 
    close(devnull);
2072
1930
    return;
2073
1931
  }
2074
1932
  struct dirent *direntry;
2075
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
  }
2076
1939
  for(int i = 0; i < numhooks; i++){
2077
1940
    direntry = direntries[i];
2078
1941
    if(debug){
2222
2085
}
2223
2086
 
2224
2087
__attribute__((nonnull, warn_unused_result))
2225
 
int bring_up_interface(const char *const interface,
2226
 
                       const float delay){
2227
 
  int old_errno = errno;
 
2088
error_t bring_up_interface(const char *const interface,
 
2089
                           const float delay){
 
2090
  error_t old_errno = errno;
2228
2091
  int ret;
2229
2092
  struct ifreq network;
2230
2093
  unsigned int if_index = if_nametoindex(interface);
2240
2103
  }
2241
2104
  
2242
2105
  if(not interface_is_up(interface)){
2243
 
    int ret_errno = 0;
2244
 
    int ioctl_errno = 0;
 
2106
    error_t ret_errno = 0, ioctl_errno = 0;
2245
2107
    if(not get_flags(interface, &network)){
2246
2108
      ret_errno = errno;
2247
2109
      fprintf_plus(stderr, "Failed to get flags for interface "
2334
2196
  
2335
2197
  /* Sleep checking until interface is running.
2336
2198
     Check every 0.25s, up to total time of delay */
2337
 
  for(int i = 0; i < delay * 4; i++){
 
2199
  for(int i=0; i < delay * 4; i++){
2338
2200
    if(interface_is_running(interface)){
2339
2201
      break;
2340
2202
    }
2350
2212
}
2351
2213
 
2352
2214
__attribute__((nonnull, warn_unused_result))
2353
 
int take_down_interface(const char *const interface){
2354
 
  int old_errno = errno;
 
2215
error_t take_down_interface(const char *const interface){
 
2216
  error_t old_errno = errno;
2355
2217
  struct ifreq network;
2356
2218
  unsigned int if_index = if_nametoindex(interface);
2357
2219
  if(if_index == 0){
2360
2222
    return ENXIO;
2361
2223
  }
2362
2224
  if(interface_is_up(interface)){
2363
 
    int ret_errno = 0;
2364
 
    int ioctl_errno = 0;
 
2225
    error_t ret_errno = 0, ioctl_errno = 0;
2365
2226
    if(not get_flags(interface, &network) and debug){
2366
2227
      ret_errno = errno;
2367
2228
      fprintf_plus(stderr, "Failed to get flags for interface "
2617
2478
                         .args_doc = "",
2618
2479
                         .doc = "Mandos client -- Get and decrypt"
2619
2480
                         " passwords from a Mandos server" };
2620
 
    ret_errno = argp_parse(&argp, argc, argv,
2621
 
                           ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2622
 
    switch(ret_errno){
 
2481
    ret = argp_parse(&argp, argc, argv,
 
2482
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2483
    switch(ret){
2623
2484
    case 0:
2624
2485
      break;
2625
2486
    case ENOMEM:
2626
2487
    default:
2627
 
      errno = ret_errno;
 
2488
      errno = ret;
2628
2489
      perror_plus("argp_parse");
2629
2490
      exitcode = EX_OSERR;
2630
2491
      goto end;
2636
2497
  
2637
2498
  {
2638
2499
    /* Work around Debian bug #633582:
2639
 
       <https://bugs.debian.org/633582> */
 
2500
       <http://bugs.debian.org/633582> */
2640
2501
    
2641
2502
    /* Re-raise privileges */
2642
 
    ret = raise_privileges();
2643
 
    if(ret != 0){
2644
 
      errno = ret;
 
2503
    ret_errno = raise_privileges();
 
2504
    if(ret_errno != 0){
 
2505
      errno = ret_errno;
2645
2506
      perror_plus("Failed to raise privileges");
2646
2507
    } else {
2647
2508
      struct stat st;
2711
2572
      }
2712
2573
      
2713
2574
      /* Lower privileges */
2714
 
      ret = lower_privileges();
2715
 
      if(ret != 0){
2716
 
        errno = ret;
 
2575
      ret_errno = lower_privileges();
 
2576
      if(ret_errno != 0){
 
2577
        errno = ret_errno;
2717
2578
        perror_plus("Failed to lower privileges");
2718
2579
      }
2719
2580
    }
3047
2908
    
3048
2909
    /* Allocate a new server */
3049
2910
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
3050
 
                                 &config, NULL, NULL, &ret);
 
2911
                                 &config, NULL, NULL, &ret_errno);
3051
2912
    
3052
2913
    /* Free the Avahi configuration data */
3053
2914
    avahi_server_config_free(&config);
3056
2917
  /* Check if creating the Avahi server object succeeded */
3057
2918
  if(mc.server == NULL){
3058
2919
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
3059
 
                 avahi_strerror(ret));
 
2920
                 avahi_strerror(ret_errno));
3060
2921
    exitcode = EX_UNAVAILABLE;
3061
2922
    goto end;
3062
2923
  }
3097
2958
 end:
3098
2959
  
3099
2960
  if(debug){
3100
 
    if(signal_received){
3101
 
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
3102
 
                   argv[0], signal_received,
3103
 
                   strsignal(signal_received));
3104
 
    } else {
3105
 
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
3106
 
    }
 
2961
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
3107
2962
  }
3108
2963
  
3109
2964
  /* Cleanup things */
3120
2975
  
3121
2976
  if(gnutls_initialized){
3122
2977
    gnutls_certificate_free_credentials(mc.cred);
 
2978
    gnutls_global_deinit();
3123
2979
    gnutls_dh_params_deinit(mc.dh_params);
3124
2980
  }
3125
2981
  
3148
3004
  
3149
3005
  /* Re-raise privileges */
3150
3006
  {
3151
 
    ret = raise_privileges();
3152
 
    if(ret != 0){
3153
 
      errno = ret;
 
3007
    ret_errno = raise_privileges();
 
3008
    if(ret_errno != 0){
 
3009
      errno = ret_errno;
3154
3010
      perror_plus("Failed to raise privileges");
3155
3011
    } else {
3156
3012
      
3161
3017
      /* Take down the network interfaces which were brought up */
3162
3018
      {
3163
3019
        char *interface = NULL;
3164
 
        while((interface = argz_next(interfaces_to_take_down,
3165
 
                                     interfaces_to_take_down_size,
3166
 
                                     interface))){
3167
 
          ret = take_down_interface(interface);
3168
 
          if(ret != 0){
3169
 
            errno = ret;
 
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;
3170
3026
            perror_plus("Failed to take down interface");
3171
3027
          }
3172
3028
        }
3177
3033
      }
3178
3034
    }
3179
3035
    
3180
 
    ret = lower_privileges_permanently();
3181
 
    if(ret != 0){
3182
 
      errno = ret;
 
3036
    ret_errno = lower_privileges_permanently();
 
3037
    if(ret_errno != 0){
 
3038
      errno = ret_errno;
3183
3039
      perror_plus("Failed to lower privileges permanently");
3184
3040
    }
3185
3041
  }
3187
3043
  free(interfaces_to_take_down);
3188
3044
  free(interfaces_hooks);
3189
3045
  
3190
 
  void clean_dir_at(int base, const char * const dirname,
3191
 
                    uintmax_t level){
3192
 
    struct dirent **direntries = NULL;
3193
 
    int dret;
3194
 
    int dir_fd = (int)TEMP_FAILURE_RETRY(openat(base, dirname,
3195
 
                                                O_RDONLY
3196
 
                                                | O_NOFOLLOW
3197
 
                                                | O_DIRECTORY
3198
 
                                                | O_PATH));
3199
 
    if(dir_fd == -1){
3200
 
      perror_plus("open");
3201
 
      return;
3202
 
    }
3203
 
    int numentries = scandirat(dir_fd, ".", &direntries,
3204
 
                               notdotentries, alphasort);
3205
 
    if(numentries >= 0){
3206
 
      for(int i = 0; i < numentries; i++){
3207
 
        if(debug){
3208
 
          fprintf_plus(stderr, "Unlinking \"%s/%s\"\n",
3209
 
                       dirname, direntries[i]->d_name);
3210
 
        }
3211
 
        dret = unlinkat(dir_fd, direntries[i]->d_name, 0);
3212
 
        if(dret == -1){
3213
 
          if(errno == EISDIR){
3214
 
              dret = unlinkat(dir_fd, direntries[i]->d_name,
3215
 
                              AT_REMOVEDIR);
3216
 
          }         
3217
 
          if((dret == -1) and (errno == ENOTEMPTY)
3218
 
             and (strcmp(direntries[i]->d_name, "private-keys-v1.d")
3219
 
                  == 0) and (level == 0)){
3220
 
            /* Recurse only in this special case */
3221
 
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3222
 
            dret = 0;
3223
 
          }
3224
 
          if((dret == -1) and (errno != ENOENT)){
3225
 
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3226
 
                         direntries[i]->d_name, strerror(errno));
3227
 
          }
3228
 
        }
3229
 
        free(direntries[i]);
3230
 
      }
3231
 
      
3232
 
      /* need to clean even if 0 because man page doesn't specify */
3233
 
      free(direntries);
3234
 
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
3235
 
      if(dret == -1 and errno != ENOENT){
3236
 
        perror_plus("rmdir");
3237
 
      }
3238
 
    } else {
3239
 
      perror_plus("scandirat");
3240
 
    }
3241
 
    close(dir_fd);
3242
 
  }
3243
 
  
3244
3046
  /* Removes the GPGME temp directory and all files inside */
3245
3047
  if(tempdir != NULL){
3246
 
    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
    }
3247
3091
  }
3248
3092
  
3249
3093
  if(quit_now){