1
/* -*- coding: utf-8 -*- */
3
* Mandos-client - get and decrypt data from a Mandos server
5
* This program is partly derived from an example program for an Avahi
6
* service browser, downloaded from
7
* <http://avahi.org/browser/examples/core-browse-services.c>. This
8
* includes the following functions: "resolve_callback",
9
* "browse_callback", and parts of "main".
12
* Copyright © 2008-2011 Teddy Hogeborn
13
* Copyright © 2008-2011 Björn Påhlsson
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.
20
* This program is distributed in the hope that it will be useful, but
21
* WITHOUT ANY WARRANTY; without even the implied warranty of
22
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23
* General Public License for more details.
25
* 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/>.
29
* Contact the authors at <mandos@fukt.bsnet.se>.
32
/* Needed by GPGME, specifically gpgme_data_seek() */
33
#ifndef _LARGEFILE_SOURCE
34
#define _LARGEFILE_SOURCE
36
#ifndef _FILE_OFFSET_BITS
37
#define _FILE_OFFSET_BITS 64
40
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
42
#include <stdio.h> /* fprintf(), stderr, fwrite(),
43
stdout, ferror(), remove() */
44
#include <stdint.h> /* uint16_t, uint32_t */
45
#include <stddef.h> /* NULL, size_t, ssize_t */
46
#include <stdlib.h> /* free(), EXIT_SUCCESS, srand(),
48
#include <stdbool.h> /* bool, false, true */
49
#include <string.h> /* memset(), strcmp(), strlen(),
50
strerror(), asprintf(), strcpy() */
51
#include <sys/ioctl.h> /* ioctl */
52
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
53
sockaddr_in6, PF_INET6,
54
SOCK_STREAM, uid_t, gid_t, open(),
56
#include <sys/stat.h> /* open() */
57
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
58
inet_pton(), connect() */
59
#include <fcntl.h> /* open() */
60
#include <dirent.h> /* opendir(), struct dirent, readdir()
62
#include <inttypes.h> /* PRIu16, PRIdMAX, intmax_t,
64
#include <assert.h> /* assert() */
65
#include <errno.h> /* perror(), errno,
66
program_invocation_short_name */
67
#include <time.h> /* nanosleep(), time(), sleep() */
68
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
69
SIOCSIFFLAGS, if_indextoname(),
70
if_nametoindex(), IF_NAMESIZE */
71
#include <netinet/in.h> /* IN6_IS_ADDR_LINKLOCAL,
72
INET_ADDRSTRLEN, INET6_ADDRSTRLEN
74
#include <unistd.h> /* close(), SEEK_SET, off_t, write(),
75
getuid(), getgid(), seteuid(),
77
#include <arpa/inet.h> /* inet_pton(), htons, inet_ntop() */
78
#include <iso646.h> /* not, or, and */
79
#include <argp.h> /* struct argp_option, error_t, struct
80
argp_state, struct argp,
81
argp_parse(), ARGP_KEY_ARG,
82
ARGP_KEY_END, ARGP_ERR_UNKNOWN */
83
#include <signal.h> /* sigemptyset(), sigaddset(),
84
sigaction(), SIGTERM, sig_atomic_t,
86
#include <sysexits.h> /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
87
EX_NOHOST, EX_IOERR, EX_PROTOCOL */
90
#include <sys/klog.h> /* klogctl() */
91
#endif /* __linux__ */
94
/* All Avahi types, constants and functions
97
#include <avahi-core/core.h>
98
#include <avahi-core/lookup.h>
99
#include <avahi-core/log.h>
100
#include <avahi-common/simple-watch.h>
101
#include <avahi-common/malloc.h>
102
#include <avahi-common/error.h>
105
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
108
init_gnutls_session(),
110
#include <gnutls/openpgp.h>
111
/* gnutls_certificate_set_openpgp_key_file(),
112
GNUTLS_OPENPGP_FMT_BASE64 */
115
#include <gpgme.h> /* All GPGME types, constants and
118
GPGME_PROTOCOL_OpenPGP,
121
#define BUFFER_SIZE 256
123
#define PATHDIR "/conf/conf.d/mandos"
124
#define SECKEY "seckey.txt"
125
#define PUBKEY "pubkey.txt"
128
static const char mandos_protocol_version[] = "1";
129
const char *argp_program_version = "mandos-client " VERSION;
130
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
131
static const char sys_class_net[] = "/sys/class/net";
132
char *connect_to = NULL;
134
/* Doubly linked list that need to be circularly linked when used */
135
typedef struct server{
138
AvahiIfIndex if_index;
140
struct timespec last_seen;
145
/* Used for passing in values through the Avahi callback functions */
147
AvahiSimplePoll *simple_poll;
149
gnutls_certificate_credentials_t cred;
150
unsigned int dh_bits;
151
gnutls_dh_params_t dh_params;
152
const char *priority;
154
server *current_server;
157
/* global context so signal handler can reach it*/
158
mandos_context mc = { .simple_poll = NULL, .server = NULL,
159
.dh_bits = 1024, .priority = "SECURE256"
160
":!CTYPE-X.509:+CTYPE-OPENPGP",
161
.current_server = NULL };
163
sig_atomic_t quit_now = 0;
164
int signal_received = 0;
166
/* Function to use when printing errors */
167
void perror_plus(const char *print_text){
168
fprintf(stderr, "Mandos plugin %s: ",
169
program_invocation_short_name);
174
* Make additional room in "buffer" for at least BUFFER_SIZE more
175
* bytes. "buffer_capacity" is how much is currently allocated,
176
* "buffer_length" is how much is already used.
178
size_t incbuffer(char **buffer, size_t buffer_length,
179
size_t buffer_capacity){
180
if(buffer_length + BUFFER_SIZE > buffer_capacity){
181
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
185
buffer_capacity += BUFFER_SIZE;
187
return buffer_capacity;
190
int add_server(const char *ip, uint16_t port,
191
AvahiIfIndex if_index,
194
server *new_server = malloc(sizeof(server));
195
if(new_server == NULL){
196
perror_plus("malloc");
199
*new_server = (server){ .ip = strdup(ip),
201
.if_index = if_index,
203
if(new_server->ip == NULL){
204
perror_plus("strdup");
207
/* unique case of first server */
208
if (mc.current_server == NULL){
209
new_server->next = new_server;
210
new_server->prev = new_server;
211
mc.current_server = new_server;
212
/* Placing the new server last in the list */
214
new_server->next = mc.current_server;
215
new_server->prev = mc.current_server->prev;
216
new_server->prev->next = new_server;
217
mc.current_server->prev = new_server;
219
ret = clock_gettime(CLOCK_MONOTONIC, &mc.current_server->last_seen);
221
perror_plus("clock_gettime");
230
static bool init_gpgme(const char *seckey,
231
const char *pubkey, const char *tempdir){
233
gpgme_engine_info_t engine_info;
237
* Helper function to insert pub and seckey to the engine keyring.
239
bool import_key(const char *filename){
242
gpgme_data_t pgp_data;
244
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
250
rc = gpgme_data_new_from_fd(&pgp_data, fd);
251
if(rc != GPG_ERR_NO_ERROR){
252
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
253
gpgme_strsource(rc), gpgme_strerror(rc));
257
rc = gpgme_op_import(mc.ctx, pgp_data);
258
if(rc != GPG_ERR_NO_ERROR){
259
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
260
gpgme_strsource(rc), gpgme_strerror(rc));
264
ret = (int)TEMP_FAILURE_RETRY(close(fd));
266
perror_plus("close");
268
gpgme_data_release(pgp_data);
273
fprintf(stderr, "Initializing GPGME\n");
277
gpgme_check_version(NULL);
278
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
279
if(rc != GPG_ERR_NO_ERROR){
280
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
281
gpgme_strsource(rc), gpgme_strerror(rc));
285
/* Set GPGME home directory for the OpenPGP engine only */
286
rc = gpgme_get_engine_info(&engine_info);
287
if(rc != GPG_ERR_NO_ERROR){
288
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
289
gpgme_strsource(rc), gpgme_strerror(rc));
292
while(engine_info != NULL){
293
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
294
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
295
engine_info->file_name, tempdir);
298
engine_info = engine_info->next;
300
if(engine_info == NULL){
301
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
305
/* Create new GPGME "context" */
306
rc = gpgme_new(&(mc.ctx));
307
if(rc != GPG_ERR_NO_ERROR){
308
fprintf(stderr, "bad gpgme_new: %s: %s\n",
309
gpgme_strsource(rc), gpgme_strerror(rc));
313
if(not import_key(pubkey) or not import_key(seckey)){
321
* Decrypt OpenPGP data.
322
* Returns -1 on error
324
static ssize_t pgp_packet_decrypt(const char *cryptotext,
327
gpgme_data_t dh_crypto, dh_plain;
330
size_t plaintext_capacity = 0;
331
ssize_t plaintext_length = 0;
334
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
337
/* Create new GPGME data buffer from memory cryptotext */
338
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
340
if(rc != GPG_ERR_NO_ERROR){
341
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
342
gpgme_strsource(rc), gpgme_strerror(rc));
346
/* Create new empty GPGME data buffer for the plaintext */
347
rc = gpgme_data_new(&dh_plain);
348
if(rc != GPG_ERR_NO_ERROR){
349
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
350
gpgme_strsource(rc), gpgme_strerror(rc));
351
gpgme_data_release(dh_crypto);
355
/* Decrypt data from the cryptotext data buffer to the plaintext
357
rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
358
if(rc != GPG_ERR_NO_ERROR){
359
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
360
gpgme_strsource(rc), gpgme_strerror(rc));
361
plaintext_length = -1;
363
gpgme_decrypt_result_t result;
364
result = gpgme_op_decrypt_result(mc.ctx);
366
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
368
fprintf(stderr, "Unsupported algorithm: %s\n",
369
result->unsupported_algorithm);
370
fprintf(stderr, "Wrong key usage: %u\n",
371
result->wrong_key_usage);
372
if(result->file_name != NULL){
373
fprintf(stderr, "File name: %s\n", result->file_name);
375
gpgme_recipient_t recipient;
376
recipient = result->recipients;
377
while(recipient != NULL){
378
fprintf(stderr, "Public key algorithm: %s\n",
379
gpgme_pubkey_algo_name(recipient->pubkey_algo));
380
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
381
fprintf(stderr, "Secret key available: %s\n",
382
recipient->status == GPG_ERR_NO_SECKEY
384
recipient = recipient->next;
392
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
395
/* Seek back to the beginning of the GPGME plaintext data buffer */
396
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
397
perror_plus("gpgme_data_seek");
398
plaintext_length = -1;
404
plaintext_capacity = incbuffer(plaintext,
405
(size_t)plaintext_length,
407
if(plaintext_capacity == 0){
408
perror_plus("incbuffer");
409
plaintext_length = -1;
413
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
415
/* Print the data, if any */
421
perror_plus("gpgme_data_read");
422
plaintext_length = -1;
425
plaintext_length += ret;
429
fprintf(stderr, "Decrypted password is: ");
430
for(ssize_t i = 0; i < plaintext_length; i++){
431
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
433
fprintf(stderr, "\n");
438
/* Delete the GPGME cryptotext data buffer */
439
gpgme_data_release(dh_crypto);
441
/* Delete the GPGME plaintext data buffer */
442
gpgme_data_release(dh_plain);
443
return plaintext_length;
446
static const char * safer_gnutls_strerror(int value){
447
const char *ret = gnutls_strerror(value); /* Spurious warning from
448
-Wunreachable-code */
454
/* GnuTLS log function callback */
455
static void debuggnutls(__attribute__((unused)) int level,
457
fprintf(stderr, "GnuTLS: %s", string);
460
static int init_gnutls_global(const char *pubkeyfilename,
461
const char *seckeyfilename){
465
fprintf(stderr, "Initializing GnuTLS\n");
468
ret = gnutls_global_init();
469
if(ret != GNUTLS_E_SUCCESS){
470
fprintf(stderr, "GnuTLS global_init: %s\n",
471
safer_gnutls_strerror(ret));
476
/* "Use a log level over 10 to enable all debugging options."
479
gnutls_global_set_log_level(11);
480
gnutls_global_set_log_function(debuggnutls);
483
/* OpenPGP credentials */
484
ret = gnutls_certificate_allocate_credentials(&mc.cred);
485
if(ret != GNUTLS_E_SUCCESS){
486
fprintf(stderr, "GnuTLS memory error: %s\n",
487
safer_gnutls_strerror(ret));
488
gnutls_global_deinit();
493
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
494
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
498
ret = gnutls_certificate_set_openpgp_key_file
499
(mc.cred, pubkeyfilename, seckeyfilename,
500
GNUTLS_OPENPGP_FMT_BASE64);
501
if(ret != GNUTLS_E_SUCCESS){
503
"Error[%d] while reading the OpenPGP key pair ('%s',"
504
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
505
fprintf(stderr, "The GnuTLS error is: %s\n",
506
safer_gnutls_strerror(ret));
510
/* GnuTLS server initialization */
511
ret = gnutls_dh_params_init(&mc.dh_params);
512
if(ret != GNUTLS_E_SUCCESS){
513
fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
514
" %s\n", safer_gnutls_strerror(ret));
517
ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
518
if(ret != GNUTLS_E_SUCCESS){
519
fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
520
safer_gnutls_strerror(ret));
524
gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
530
gnutls_certificate_free_credentials(mc.cred);
531
gnutls_global_deinit();
532
gnutls_dh_params_deinit(mc.dh_params);
536
static int init_gnutls_session(gnutls_session_t *session){
538
/* GnuTLS session creation */
540
ret = gnutls_init(session, GNUTLS_SERVER);
544
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
545
if(ret != GNUTLS_E_SUCCESS){
546
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
547
safer_gnutls_strerror(ret));
553
ret = gnutls_priority_set_direct(*session, mc.priority, &err);
555
gnutls_deinit(*session);
558
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
559
if(ret != GNUTLS_E_SUCCESS){
560
fprintf(stderr, "Syntax error at: %s\n", err);
561
fprintf(stderr, "GnuTLS error: %s\n",
562
safer_gnutls_strerror(ret));
563
gnutls_deinit(*session);
569
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
572
gnutls_deinit(*session);
575
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
576
if(ret != GNUTLS_E_SUCCESS){
577
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
578
safer_gnutls_strerror(ret));
579
gnutls_deinit(*session);
583
/* ignore client certificate if any. */
584
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
586
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
591
/* Avahi log function callback */
592
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
593
__attribute__((unused)) const char *txt){}
595
/* Called when a Mandos server is found */
596
static int start_mandos_communication(const char *ip, uint16_t port,
597
AvahiIfIndex if_index,
599
int ret, tcp_sd = -1;
602
struct sockaddr_in in;
603
struct sockaddr_in6 in6;
606
char *decrypted_buffer = NULL;
607
size_t buffer_length = 0;
608
size_t buffer_capacity = 0;
611
gnutls_session_t session;
612
int pf; /* Protocol family */
629
fprintf(stderr, "Bad address family: %d\n", af);
634
ret = init_gnutls_session(&session);
640
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
644
tcp_sd = socket(pf, SOCK_STREAM, 0);
647
perror_plus("socket");
657
memset(&to, 0, sizeof(to));
659
to.in6.sin6_family = (sa_family_t)af;
660
ret = inet_pton(af, ip, &to.in6.sin6_addr);
662
to.in.sin_family = (sa_family_t)af;
663
ret = inet_pton(af, ip, &to.in.sin_addr);
667
perror_plus("inet_pton");
673
fprintf(stderr, "Bad address: %s\n", ip);
678
to.in6.sin6_port = htons(port); /* Spurious warnings from
680
-Wunreachable-code */
682
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
683
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
685
if(if_index == AVAHI_IF_UNSPEC){
686
fprintf(stderr, "An IPv6 link-local address is incomplete"
687
" without a network interface\n");
691
/* Set the network interface number as scope */
692
to.in6.sin6_scope_id = (uint32_t)if_index;
695
to.in.sin_port = htons(port); /* Spurious warnings from
697
-Wunreachable-code */
706
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
707
char interface[IF_NAMESIZE];
708
if(if_indextoname((unsigned int)if_index, interface) == NULL){
709
perror_plus("if_indextoname");
711
fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
712
ip, interface, port);
715
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
718
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
719
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
722
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
725
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
729
perror_plus("inet_ntop");
731
if(strcmp(addrstr, ip) != 0){
732
fprintf(stderr, "Canonical address form: %s\n", addrstr);
743
ret = connect(tcp_sd, &to.in6, sizeof(to));
745
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
748
if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
750
perror_plus("connect");
761
const char *out = mandos_protocol_version;
764
size_t out_size = strlen(out);
765
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
766
out_size - written));
769
perror_plus("write");
773
written += (size_t)ret;
774
if(written < out_size){
777
if(out == mandos_protocol_version){
792
fprintf(stderr, "Establishing TLS session with %s\n", ip);
800
/* Spurious warning from -Wint-to-pointer-cast */
801
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
809
ret = gnutls_handshake(session);
814
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
816
if(ret != GNUTLS_E_SUCCESS){
818
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
825
/* Read OpenPGP packet that contains the wanted password */
828
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
839
buffer_capacity = incbuffer(&buffer, buffer_length,
841
if(buffer_capacity == 0){
843
perror_plus("incbuffer");
853
sret = gnutls_record_recv(session, buffer+buffer_length,
860
case GNUTLS_E_INTERRUPTED:
863
case GNUTLS_E_REHANDSHAKE:
865
ret = gnutls_handshake(session);
871
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
873
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
880
fprintf(stderr, "Unknown error while reading data from"
881
" encrypted session with Mandos server\n");
882
gnutls_bye(session, GNUTLS_SHUT_RDWR);
887
buffer_length += (size_t) sret;
892
fprintf(stderr, "Closing TLS session\n");
901
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
906
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
908
if(buffer_length > 0){
909
ssize_t decrypted_buffer_size;
910
decrypted_buffer_size = pgp_packet_decrypt(buffer,
913
if(decrypted_buffer_size >= 0){
916
while(written < (size_t) decrypted_buffer_size){
922
ret = (int)fwrite(decrypted_buffer + written, 1,
923
(size_t)decrypted_buffer_size - written,
925
if(ret == 0 and ferror(stdout)){
928
fprintf(stderr, "Error writing encrypted data: %s\n",
934
written += (size_t)ret;
940
/* Shutdown procedure */
945
free(decrypted_buffer);
948
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
954
perror_plus("close");
956
gnutls_deinit(session);
966
static void resolve_callback(AvahiSServiceResolver *r,
967
AvahiIfIndex interface,
969
AvahiResolverEvent event,
973
const char *host_name,
974
const AvahiAddress *address,
976
AVAHI_GCC_UNUSED AvahiStringList *txt,
977
AVAHI_GCC_UNUSED AvahiLookupResultFlags
979
AVAHI_GCC_UNUSED void* userdata){
982
/* Called whenever a service has been resolved successfully or
991
case AVAHI_RESOLVER_FAILURE:
992
fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
993
" of type '%s' in domain '%s': %s\n", name, type, domain,
994
avahi_strerror(avahi_server_errno(mc.server)));
997
case AVAHI_RESOLVER_FOUND:
999
char ip[AVAHI_ADDRESS_STR_MAX];
1000
avahi_address_snprint(ip, sizeof(ip), address);
1002
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
1003
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
1004
ip, (intmax_t)interface, port);
1006
int ret = start_mandos_communication(ip, port, interface,
1007
avahi_proto_to_af(proto));
1009
avahi_simple_poll_quit(mc.simple_poll);
1011
ret = add_server(ip, port, interface,
1012
avahi_proto_to_af(proto));
1016
avahi_s_service_resolver_free(r);
1019
static void browse_callback(AvahiSServiceBrowser *b,
1020
AvahiIfIndex interface,
1021
AvahiProtocol protocol,
1022
AvahiBrowserEvent event,
1026
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1028
AVAHI_GCC_UNUSED void* userdata){
1031
/* Called whenever a new services becomes available on the LAN or
1032
is removed from the LAN */
1040
case AVAHI_BROWSER_FAILURE:
1042
fprintf(stderr, "(Avahi browser) %s\n",
1043
avahi_strerror(avahi_server_errno(mc.server)));
1044
avahi_simple_poll_quit(mc.simple_poll);
1047
case AVAHI_BROWSER_NEW:
1048
/* We ignore the returned Avahi resolver object. In the callback
1049
function we free it. If the Avahi server is terminated before
1050
the callback function is called the Avahi server will free the
1053
if(avahi_s_service_resolver_new(mc.server, interface, protocol,
1054
name, type, domain, protocol, 0,
1055
resolve_callback, NULL) == NULL)
1056
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
1057
name, avahi_strerror(avahi_server_errno(mc.server)));
1060
case AVAHI_BROWSER_REMOVE:
1063
case AVAHI_BROWSER_ALL_FOR_NOW:
1064
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1066
fprintf(stderr, "No Mandos server found, still searching...\n");
1072
/* Signal handler that stops main loop after SIGTERM */
1073
static void handle_sigterm(int sig){
1078
signal_received = sig;
1079
int old_errno = errno;
1080
/* set main loop to exit */
1081
if(mc.simple_poll != NULL){
1082
avahi_simple_poll_quit(mc.simple_poll);
1088
* This function determines if a directory entry in /sys/class/net
1089
* corresponds to an acceptable network device.
1090
* (This function is passed to scandir(3) as a filter function.)
1092
int good_interface(const struct dirent *if_entry){
1094
char *flagname = NULL;
1095
if(if_entry->d_name[0] == '.'){
1098
int ret = asprintf(&flagname, "%s/%s/flags", sys_class_net,
1101
perror_plus("asprintf");
1104
int flags_fd = (int)TEMP_FAILURE_RETRY(open(flagname, O_RDONLY));
1106
perror_plus("open");
1111
typedef short ifreq_flags; /* ifreq.ifr_flags in netdevice(7) */
1112
/* read line from flags_fd */
1113
ssize_t to_read = 2+(sizeof(ifreq_flags)*2)+1; /* "0x1003\n" */
1114
char *flagstring = malloc((size_t)to_read+1); /* +1 for final \0 */
1115
flagstring[(size_t)to_read] = '\0';
1116
if(flagstring == NULL){
1117
perror_plus("malloc");
1122
ssret = (ssize_t)TEMP_FAILURE_RETRY(read(flags_fd, flagstring,
1125
perror_plus("read");
1139
tmpmax = strtoimax(flagstring, &tmp, 0);
1140
if(errno != 0 or tmp == flagstring or (*tmp != '\0'
1141
and not (isspace(*tmp)))
1142
or tmpmax != (ifreq_flags)tmpmax){
1144
fprintf(stderr, "Invalid flags \"%s\" for interface \"%s\"\n",
1145
flagstring, if_entry->d_name);
1151
ifreq_flags flags = (ifreq_flags)tmpmax;
1152
/* Reject the loopback device */
1153
if(flags & IFF_LOOPBACK){
1155
fprintf(stderr, "Rejecting loopback interface \"%s\"\n",
1160
/* Accept point-to-point devices only if connect_to is specified */
1161
if(connect_to != NULL and (flags & IFF_POINTOPOINT)){
1163
fprintf(stderr, "Accepting point-to-point interface \"%s\"\n",
1168
/* Otherwise, reject non-broadcast-capable devices */
1169
if(not (flags & IFF_BROADCAST)){
1171
fprintf(stderr, "Rejecting non-broadcast interface \"%s\"\n",
1176
/* Reject non-ARP interfaces (including dummy interfaces) */
1177
if(flags & IFF_NOARP){
1179
fprintf(stderr, "Rejecting non-ARP interface \"%s\"\n",
1184
/* Accept this device */
1186
fprintf(stderr, "Interface \"%s\" is acceptable\n",
1192
int notdotentries(const struct dirent *direntry){
1193
/* Skip "." and ".." */
1194
if(direntry->d_name[0] == '.'
1195
and (direntry->d_name[1] == '\0'
1196
or (direntry->d_name[1] == '.'
1197
and direntry->d_name[2] == '\0'))){
1203
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval){
1205
struct timespec now;
1206
struct timespec waited_time;
1207
intmax_t block_time;
1210
if(mc.current_server == NULL){
1213
"Wait until first server is found. No timeout!\n");
1215
ret = avahi_simple_poll_iterate(s, -1);
1218
fprintf(stderr, "Check current_server if we should run it,"
1221
/* the current time */
1222
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1224
perror_plus("clock_gettime");
1227
/* Calculating in ms how long time between now and server
1228
who we visted longest time ago. Now - last seen. */
1229
waited_time.tv_sec = (now.tv_sec
1230
- mc.current_server->last_seen.tv_sec);
1231
waited_time.tv_nsec = (now.tv_nsec
1232
- mc.current_server->last_seen.tv_nsec);
1233
/* total time is 10s/10,000ms.
1234
Converting to s from ms by dividing by 1,000,
1235
and ns to ms by dividing by 1,000,000. */
1236
block_time = ((retry_interval
1237
- ((intmax_t)waited_time.tv_sec * 1000))
1238
- ((intmax_t)waited_time.tv_nsec / 1000000));
1241
fprintf(stderr, "Blocking for %ld ms\n", block_time);
1244
if(block_time <= 0){
1245
ret = start_mandos_communication(mc.current_server->ip,
1246
mc.current_server->port,
1247
mc.current_server->if_index,
1248
mc.current_server->af);
1250
avahi_simple_poll_quit(mc.simple_poll);
1253
ret = clock_gettime(CLOCK_MONOTONIC,
1254
&mc.current_server->last_seen);
1256
perror_plus("clock_gettime");
1259
mc.current_server = mc.current_server->next;
1260
block_time = 0; /* Call avahi to find new Mandos
1261
servers, but don't block */
1264
ret = avahi_simple_poll_iterate(s, (int)block_time);
1267
if (ret > 0 or errno != EINTR) {
1268
return (ret != 1) ? ret : 0;
1274
int main(int argc, char *argv[]){
1275
AvahiSServiceBrowser *sb = NULL;
1280
int exitcode = EXIT_SUCCESS;
1281
const char *interface = "";
1282
struct ifreq network;
1284
bool take_down_interface = false;
1287
char tempdir[] = "/tmp/mandosXXXXXX";
1288
bool tempdir_created = false;
1289
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1290
const char *seckey = PATHDIR "/" SECKEY;
1291
const char *pubkey = PATHDIR "/" PUBKEY;
1293
bool gnutls_initialized = false;
1294
bool gpgme_initialized = false;
1296
double retry_interval = 10; /* 10s between trying a server and
1297
retrying the same server again */
1299
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
1300
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
1305
/* Lower any group privileges we might have, just to be safe */
1309
perror_plus("setgid");
1312
/* Lower user privileges (temporarily) */
1316
perror_plus("seteuid");
1324
struct argp_option options[] = {
1325
{ .name = "debug", .key = 128,
1326
.doc = "Debug mode", .group = 3 },
1327
{ .name = "connect", .key = 'c',
1328
.arg = "ADDRESS:PORT",
1329
.doc = "Connect directly to a specific Mandos server",
1331
{ .name = "interface", .key = 'i',
1333
.doc = "Network interface that will be used to search for"
1336
{ .name = "seckey", .key = 's',
1338
.doc = "OpenPGP secret key file base name",
1340
{ .name = "pubkey", .key = 'p',
1342
.doc = "OpenPGP public key file base name",
1344
{ .name = "dh-bits", .key = 129,
1346
.doc = "Bit length of the prime number used in the"
1347
" Diffie-Hellman key exchange",
1349
{ .name = "priority", .key = 130,
1351
.doc = "GnuTLS priority string for the TLS handshake",
1353
{ .name = "delay", .key = 131,
1355
.doc = "Maximum delay to wait for interface startup",
1357
{ .name = "retry", .key = 132,
1359
.doc = "Retry interval used when denied by the mandos server",
1362
* These reproduce what we would get without ARGP_NO_HELP
1364
{ .name = "help", .key = '?',
1365
.doc = "Give this help list", .group = -1 },
1366
{ .name = "usage", .key = -3,
1367
.doc = "Give a short usage message", .group = -1 },
1368
{ .name = "version", .key = 'V',
1369
.doc = "Print program version", .group = -1 },
1373
error_t parse_opt(int key, char *arg,
1374
struct argp_state *state){
1377
case 128: /* --debug */
1380
case 'c': /* --connect */
1383
case 'i': /* --interface */
1386
case 's': /* --seckey */
1389
case 'p': /* --pubkey */
1392
case 129: /* --dh-bits */
1394
tmpmax = strtoimax(arg, &tmp, 10);
1395
if(errno != 0 or tmp == arg or *tmp != '\0'
1396
or tmpmax != (typeof(mc.dh_bits))tmpmax){
1397
argp_error(state, "Bad number of DH bits");
1399
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
1401
case 130: /* --priority */
1404
case 131: /* --delay */
1406
delay = strtof(arg, &tmp);
1407
if(errno != 0 or tmp == arg or *tmp != '\0'){
1408
argp_error(state, "Bad delay");
1410
case 132: /* --retry */
1412
retry_interval = strtod(arg, &tmp);
1413
if(errno != 0 or tmp == arg or *tmp != '\0'
1414
or (retry_interval * 1000) > INT_MAX){
1415
argp_error(state, "Bad retry interval");
1419
* These reproduce what we would get without ARGP_NO_HELP
1421
case '?': /* --help */
1422
argp_state_help(state, state->out_stream,
1423
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
1424
& ~(unsigned int)ARGP_HELP_EXIT_OK);
1425
case -3: /* --usage */
1426
argp_state_help(state, state->out_stream,
1427
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
1428
case 'V': /* --version */
1429
fprintf(state->out_stream, "%s\n", argp_program_version);
1430
exit(argp_err_exit_status);
1433
return ARGP_ERR_UNKNOWN;
1438
struct argp argp = { .options = options, .parser = parse_opt,
1440
.doc = "Mandos client -- Get and decrypt"
1441
" passwords from a Mandos server" };
1442
ret = argp_parse(&argp, argc, argv,
1443
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
1450
perror_plus("argp_parse");
1451
exitcode = EX_OSERR;
1454
exitcode = EX_USAGE;
1460
/* Work around Debian bug #633582:
1461
<http://bugs.debian.org/633582> */
1464
/* Re-raise priviliges */
1468
perror_plus("seteuid");
1471
int seckey_fd = open(PATHDIR "/" SECKEY, O_RDONLY);
1472
if(seckey_fd == -1){
1473
perror_plus("open");
1475
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
1477
perror_plus("fstat");
1479
if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
1480
ret = fchown(seckey_fd, uid, gid);
1482
perror_plus("fchown");
1486
TEMP_FAILURE_RETRY(close(seckey_fd));
1489
int pubkey_fd = open(PATHDIR "/" PUBKEY, O_RDONLY);
1490
if(pubkey_fd == -1){
1491
perror_plus("open");
1493
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
1495
perror_plus("fstat");
1497
if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
1498
ret = fchown(pubkey_fd, uid, gid);
1500
perror_plus("fchown");
1504
TEMP_FAILURE_RETRY(close(pubkey_fd));
1507
/* Lower privileges */
1511
perror_plus("seteuid");
1516
avahi_set_log_function(empty_log);
1519
if(interface[0] == '\0'){
1520
struct dirent **direntries;
1521
ret = scandir(sys_class_net, &direntries, good_interface,
1524
/* Pick the first good interface */
1525
interface = strdup(direntries[0]->d_name);
1527
fprintf(stderr, "Using interface \"%s\"\n", interface);
1529
if(interface == NULL){
1530
perror_plus("malloc");
1532
exitcode = EXIT_FAILURE;
1538
fprintf(stderr, "Could not find a network interface\n");
1539
exitcode = EXIT_FAILURE;
1544
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1545
from the signal handler */
1546
/* Initialize the pseudo-RNG for Avahi */
1547
srand((unsigned int) time(NULL));
1548
mc.simple_poll = avahi_simple_poll_new();
1549
if(mc.simple_poll == NULL){
1550
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1551
exitcode = EX_UNAVAILABLE;
1555
sigemptyset(&sigterm_action.sa_mask);
1556
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1558
perror_plus("sigaddset");
1559
exitcode = EX_OSERR;
1562
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1564
perror_plus("sigaddset");
1565
exitcode = EX_OSERR;
1568
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1570
perror_plus("sigaddset");
1571
exitcode = EX_OSERR;
1574
/* Need to check if the handler is SIG_IGN before handling:
1575
| [[info:libc:Initial Signal Actions]] |
1576
| [[info:libc:Basic Signal Handling]] |
1578
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
1580
perror_plus("sigaction");
1583
if(old_sigterm_action.sa_handler != SIG_IGN){
1584
ret = sigaction(SIGINT, &sigterm_action, NULL);
1586
perror_plus("sigaction");
1587
exitcode = EX_OSERR;
1591
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
1593
perror_plus("sigaction");
1596
if(old_sigterm_action.sa_handler != SIG_IGN){
1597
ret = sigaction(SIGHUP, &sigterm_action, NULL);
1599
perror_plus("sigaction");
1600
exitcode = EX_OSERR;
1604
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
1606
perror_plus("sigaction");
1609
if(old_sigterm_action.sa_handler != SIG_IGN){
1610
ret = sigaction(SIGTERM, &sigterm_action, NULL);
1612
perror_plus("sigaction");
1613
exitcode = EX_OSERR;
1618
/* If the interface is down, bring it up */
1619
if(strcmp(interface, "none") != 0){
1620
if_index = (AvahiIfIndex) if_nametoindex(interface);
1622
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1623
exitcode = EX_UNAVAILABLE;
1631
/* Re-raise priviliges */
1635
perror_plus("seteuid");
1639
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1640
messages about the network interface to mess up the prompt */
1641
ret = klogctl(8, NULL, 5);
1642
bool restore_loglevel = true;
1644
restore_loglevel = false;
1645
perror_plus("klogctl");
1647
#endif /* __linux__ */
1649
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1651
perror_plus("socket");
1652
exitcode = EX_OSERR;
1654
if(restore_loglevel){
1655
ret = klogctl(7, NULL, 0);
1657
perror_plus("klogctl");
1660
#endif /* __linux__ */
1661
/* Lower privileges */
1665
perror_plus("seteuid");
1669
strcpy(network.ifr_name, interface);
1670
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1672
perror_plus("ioctl SIOCGIFFLAGS");
1674
if(restore_loglevel){
1675
ret = klogctl(7, NULL, 0);
1677
perror_plus("klogctl");
1680
#endif /* __linux__ */
1681
exitcode = EX_OSERR;
1682
/* Lower privileges */
1686
perror_plus("seteuid");
1690
if((network.ifr_flags & IFF_UP) == 0){
1691
network.ifr_flags |= IFF_UP;
1692
take_down_interface = true;
1693
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1695
take_down_interface = false;
1696
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1697
exitcode = EX_OSERR;
1699
if(restore_loglevel){
1700
ret = klogctl(7, NULL, 0);
1702
perror_plus("klogctl");
1705
#endif /* __linux__ */
1706
/* Lower privileges */
1710
perror_plus("seteuid");
1715
/* Sleep checking until interface is running.
1716
Check every 0.25s, up to total time of delay */
1717
for(int i=0; i < delay * 4; i++){
1718
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1720
perror_plus("ioctl SIOCGIFFLAGS");
1721
} else if(network.ifr_flags & IFF_RUNNING){
1724
struct timespec sleeptime = { .tv_nsec = 250000000 };
1725
ret = nanosleep(&sleeptime, NULL);
1726
if(ret == -1 and errno != EINTR){
1727
perror_plus("nanosleep");
1730
if(not take_down_interface){
1731
/* We won't need the socket anymore */
1732
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1734
perror_plus("close");
1738
if(restore_loglevel){
1739
/* Restores kernel loglevel to default */
1740
ret = klogctl(7, NULL, 0);
1742
perror_plus("klogctl");
1745
#endif /* __linux__ */
1746
/* Lower privileges */
1748
if(take_down_interface){
1749
/* Lower privileges */
1752
perror_plus("seteuid");
1755
/* Lower privileges permanently */
1758
perror_plus("setuid");
1767
ret = init_gnutls_global(pubkey, seckey);
1769
fprintf(stderr, "init_gnutls_global failed\n");
1770
exitcode = EX_UNAVAILABLE;
1773
gnutls_initialized = true;
1780
if(mkdtemp(tempdir) == NULL){
1781
perror_plus("mkdtemp");
1784
tempdir_created = true;
1790
if(not init_gpgme(pubkey, seckey, tempdir)){
1791
fprintf(stderr, "init_gpgme failed\n");
1792
exitcode = EX_UNAVAILABLE;
1795
gpgme_initialized = true;
1802
if(connect_to != NULL){
1803
/* Connect directly, do not use Zeroconf */
1804
/* (Mainly meant for debugging) */
1805
char *address = strrchr(connect_to, ':');
1806
if(address == NULL){
1807
fprintf(stderr, "No colon in address\n");
1808
exitcode = EX_USAGE;
1818
tmpmax = strtoimax(address+1, &tmp, 10);
1819
if(errno != 0 or tmp == address+1 or *tmp != '\0'
1820
or tmpmax != (uint16_t)tmpmax){
1821
fprintf(stderr, "Bad port number\n");
1822
exitcode = EX_USAGE;
1830
port = (uint16_t)tmpmax;
1832
address = connect_to;
1833
/* Colon in address indicates IPv6 */
1835
if(strchr(address, ':') != NULL){
1845
while(not quit_now){
1846
ret = start_mandos_communication(address, port, if_index, af);
1847
if(quit_now or ret == 0){
1850
sleep((int)retry_interval or 1);
1854
exitcode = EXIT_SUCCESS;
1865
AvahiServerConfig config;
1866
/* Do not publish any local Zeroconf records */
1867
avahi_server_config_init(&config);
1868
config.publish_hinfo = 0;
1869
config.publish_addresses = 0;
1870
config.publish_workstation = 0;
1871
config.publish_domain = 0;
1873
/* Allocate a new server */
1874
mc.server = avahi_server_new(avahi_simple_poll_get
1875
(mc.simple_poll), &config, NULL,
1878
/* Free the Avahi configuration data */
1879
avahi_server_config_free(&config);
1882
/* Check if creating the Avahi server object succeeded */
1883
if(mc.server == NULL){
1884
fprintf(stderr, "Failed to create Avahi server: %s\n",
1885
avahi_strerror(error));
1886
exitcode = EX_UNAVAILABLE;
1894
/* Create the Avahi service browser */
1895
sb = avahi_s_service_browser_new(mc.server, if_index,
1896
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
1897
NULL, 0, browse_callback, NULL);
1899
fprintf(stderr, "Failed to create service browser: %s\n",
1900
avahi_strerror(avahi_server_errno(mc.server)));
1901
exitcode = EX_UNAVAILABLE;
1909
/* Run the main loop */
1912
fprintf(stderr, "Starting Avahi loop search\n");
1915
ret = avahi_loop_with_timeout(mc.simple_poll,
1916
(int)(retry_interval * 1000));
1918
fprintf(stderr, "avahi_loop_with_timeout exited %s\n",
1919
(ret == 0) ? "successfully" : "with error");
1925
fprintf(stderr, "%s exiting\n", argv[0]);
1928
/* Cleanup things */
1930
avahi_s_service_browser_free(sb);
1932
if(mc.server != NULL)
1933
avahi_server_free(mc.server);
1935
if(mc.simple_poll != NULL)
1936
avahi_simple_poll_free(mc.simple_poll);
1938
if(gnutls_initialized){
1939
gnutls_certificate_free_credentials(mc.cred);
1940
gnutls_global_deinit();
1941
gnutls_dh_params_deinit(mc.dh_params);
1944
if(gpgme_initialized){
1945
gpgme_release(mc.ctx);
1948
/* Cleans up the circular linked list of Mandos servers the client
1950
if(mc.current_server != NULL){
1951
mc.current_server->prev->next = NULL;
1952
while(mc.current_server != NULL){
1953
server *next = mc.current_server->next;
1954
free(mc.current_server);
1955
mc.current_server = next;
1959
/* Take down the network interface */
1960
if(take_down_interface){
1961
/* Re-raise priviliges */
1965
perror_plus("seteuid");
1968
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1970
perror_plus("ioctl SIOCGIFFLAGS");
1971
} else if(network.ifr_flags & IFF_UP) {
1972
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
1973
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1975
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
1978
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1980
perror_plus("close");
1982
/* Lower privileges permanently */
1986
perror_plus("setuid");
1991
/* Removes the GPGME temp directory and all files inside */
1992
if(tempdir_created){
1993
struct dirent **direntries = NULL;
1994
struct dirent *direntry = NULL;
1995
ret = scandir(tempdir, &direntries, notdotentries, alphasort);
1997
for(int i = 0; i < ret; i++){
1998
direntry = direntries[i];
1999
char *fullname = NULL;
2000
ret = asprintf(&fullname, "%s/%s", tempdir,
2003
perror_plus("asprintf");
2006
ret = remove(fullname);
2008
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
2015
/* need to be cleaned even if ret == 0 because man page doesn't
2019
perror_plus("scandir");
2021
ret = rmdir(tempdir);
2022
if(ret == -1 and errno != ENOENT){
2023
perror_plus("rmdir");
2028
sigemptyset(&old_sigterm_action.sa_mask);
2029
old_sigterm_action.sa_handler = SIG_DFL;
2030
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
2031
&old_sigterm_action,
2034
perror_plus("sigaction");
2037
ret = raise(signal_received);
2038
} while(ret != 0 and errno == EINTR);
2040
perror_plus("raise");
2043
TEMP_FAILURE_RETRY(pause());