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@recompile.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"
126
#define HOOKDIR "/lib/mandos/network-hooks.d"
129
static const char mandos_protocol_version[] = "1";
130
const char *argp_program_version = "mandos-client " VERSION;
131
const char *argp_program_bug_address = "<mandos@recompile.se>";
132
static const char sys_class_net[] = "/sys/class/net";
133
char *connect_to = NULL;
135
/* Doubly linked list that need to be circularly linked when used */
136
typedef struct server{
139
AvahiIfIndex if_index;
141
struct timespec last_seen;
146
/* Used for passing in values through the Avahi callback functions */
148
AvahiSimplePoll *simple_poll;
150
gnutls_certificate_credentials_t cred;
151
unsigned int dh_bits;
152
gnutls_dh_params_t dh_params;
153
const char *priority;
155
server *current_server;
158
/* global context so signal handler can reach it*/
159
mandos_context mc = { .simple_poll = NULL, .server = NULL,
160
.dh_bits = 1024, .priority = "SECURE256"
161
":!CTYPE-X.509:+CTYPE-OPENPGP",
162
.current_server = NULL };
164
sig_atomic_t quit_now = 0;
165
int signal_received = 0;
167
/* Function to use when printing errors */
168
void perror_plus(const char *print_text){
169
fprintf(stderr, "Mandos plugin %s: ",
170
program_invocation_short_name);
174
int fprintf_plus(FILE *stream, const char *format, ...){
176
va_start (ap, format);
178
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ", program_invocation_short_name));
179
return TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
183
* Make additional room in "buffer" for at least BUFFER_SIZE more
184
* bytes. "buffer_capacity" is how much is currently allocated,
185
* "buffer_length" is how much is already used.
187
size_t incbuffer(char **buffer, size_t buffer_length,
188
size_t buffer_capacity){
189
if(buffer_length + BUFFER_SIZE > buffer_capacity){
190
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
194
buffer_capacity += BUFFER_SIZE;
196
return buffer_capacity;
199
/* Add server to set of servers to retry periodically */
200
int add_server(const char *ip, uint16_t port,
201
AvahiIfIndex if_index,
204
server *new_server = malloc(sizeof(server));
205
if(new_server == NULL){
206
perror_plus("malloc");
209
*new_server = (server){ .ip = strdup(ip),
211
.if_index = if_index,
213
if(new_server->ip == NULL){
214
perror_plus("strdup");
217
/* Special case of first server */
218
if (mc.current_server == NULL){
219
new_server->next = new_server;
220
new_server->prev = new_server;
221
mc.current_server = new_server;
222
/* Place the new server last in the list */
224
new_server->next = mc.current_server;
225
new_server->prev = mc.current_server->prev;
226
new_server->prev->next = new_server;
227
mc.current_server->prev = new_server;
229
ret = clock_gettime(CLOCK_MONOTONIC, &mc.current_server->last_seen);
231
perror_plus("clock_gettime");
240
static bool init_gpgme(const char *seckey,
241
const char *pubkey, const char *tempdir){
243
gpgme_engine_info_t engine_info;
247
* Helper function to insert pub and seckey to the engine keyring.
249
bool import_key(const char *filename){
252
gpgme_data_t pgp_data;
254
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
260
rc = gpgme_data_new_from_fd(&pgp_data, fd);
261
if(rc != GPG_ERR_NO_ERROR){
262
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
263
gpgme_strsource(rc), gpgme_strerror(rc));
267
rc = gpgme_op_import(mc.ctx, pgp_data);
268
if(rc != GPG_ERR_NO_ERROR){
269
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
270
gpgme_strsource(rc), gpgme_strerror(rc));
274
ret = (int)TEMP_FAILURE_RETRY(close(fd));
276
perror_plus("close");
278
gpgme_data_release(pgp_data);
283
fprintf(stderr, "Initializing GPGME\n");
287
gpgme_check_version(NULL);
288
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
289
if(rc != GPG_ERR_NO_ERROR){
290
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
291
gpgme_strsource(rc), gpgme_strerror(rc));
295
/* Set GPGME home directory for the OpenPGP engine only */
296
rc = gpgme_get_engine_info(&engine_info);
297
if(rc != GPG_ERR_NO_ERROR){
298
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
299
gpgme_strsource(rc), gpgme_strerror(rc));
302
while(engine_info != NULL){
303
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
304
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
305
engine_info->file_name, tempdir);
308
engine_info = engine_info->next;
310
if(engine_info == NULL){
311
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
315
/* Create new GPGME "context" */
316
rc = gpgme_new(&(mc.ctx));
317
if(rc != GPG_ERR_NO_ERROR){
318
fprintf(stderr, "bad gpgme_new: %s: %s\n",
319
gpgme_strsource(rc), gpgme_strerror(rc));
323
if(not import_key(pubkey) or not import_key(seckey)){
331
* Decrypt OpenPGP data.
332
* Returns -1 on error
334
static ssize_t pgp_packet_decrypt(const char *cryptotext,
337
gpgme_data_t dh_crypto, dh_plain;
340
size_t plaintext_capacity = 0;
341
ssize_t plaintext_length = 0;
344
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
347
/* Create new GPGME data buffer from memory cryptotext */
348
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
350
if(rc != GPG_ERR_NO_ERROR){
351
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
352
gpgme_strsource(rc), gpgme_strerror(rc));
356
/* Create new empty GPGME data buffer for the plaintext */
357
rc = gpgme_data_new(&dh_plain);
358
if(rc != GPG_ERR_NO_ERROR){
359
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
360
gpgme_strsource(rc), gpgme_strerror(rc));
361
gpgme_data_release(dh_crypto);
365
/* Decrypt data from the cryptotext data buffer to the plaintext
367
rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
368
if(rc != GPG_ERR_NO_ERROR){
369
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
370
gpgme_strsource(rc), gpgme_strerror(rc));
371
plaintext_length = -1;
373
gpgme_decrypt_result_t result;
374
result = gpgme_op_decrypt_result(mc.ctx);
376
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
378
fprintf(stderr, "Unsupported algorithm: %s\n",
379
result->unsupported_algorithm);
380
fprintf(stderr, "Wrong key usage: %u\n",
381
result->wrong_key_usage);
382
if(result->file_name != NULL){
383
fprintf(stderr, "File name: %s\n", result->file_name);
385
gpgme_recipient_t recipient;
386
recipient = result->recipients;
387
while(recipient != NULL){
388
fprintf(stderr, "Public key algorithm: %s\n",
389
gpgme_pubkey_algo_name(recipient->pubkey_algo));
390
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
391
fprintf(stderr, "Secret key available: %s\n",
392
recipient->status == GPG_ERR_NO_SECKEY
394
recipient = recipient->next;
402
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
405
/* Seek back to the beginning of the GPGME plaintext data buffer */
406
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
407
perror_plus("gpgme_data_seek");
408
plaintext_length = -1;
414
plaintext_capacity = incbuffer(plaintext,
415
(size_t)plaintext_length,
417
if(plaintext_capacity == 0){
418
perror_plus("incbuffer");
419
plaintext_length = -1;
423
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
425
/* Print the data, if any */
431
perror_plus("gpgme_data_read");
432
plaintext_length = -1;
435
plaintext_length += ret;
439
fprintf(stderr, "Decrypted password is: ");
440
for(ssize_t i = 0; i < plaintext_length; i++){
441
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
443
fprintf(stderr, "\n");
448
/* Delete the GPGME cryptotext data buffer */
449
gpgme_data_release(dh_crypto);
451
/* Delete the GPGME plaintext data buffer */
452
gpgme_data_release(dh_plain);
453
return plaintext_length;
456
static const char * safer_gnutls_strerror(int value){
457
const char *ret = gnutls_strerror(value); /* Spurious warning from
458
-Wunreachable-code */
464
/* GnuTLS log function callback */
465
static void debuggnutls(__attribute__((unused)) int level,
467
fprintf(stderr, "GnuTLS: %s", string);
470
static int init_gnutls_global(const char *pubkeyfilename,
471
const char *seckeyfilename){
475
fprintf(stderr, "Initializing GnuTLS\n");
478
ret = gnutls_global_init();
479
if(ret != GNUTLS_E_SUCCESS){
480
fprintf(stderr, "GnuTLS global_init: %s\n",
481
safer_gnutls_strerror(ret));
486
/* "Use a log level over 10 to enable all debugging options."
489
gnutls_global_set_log_level(11);
490
gnutls_global_set_log_function(debuggnutls);
493
/* OpenPGP credentials */
494
ret = gnutls_certificate_allocate_credentials(&mc.cred);
495
if(ret != GNUTLS_E_SUCCESS){
496
fprintf(stderr, "GnuTLS memory error: %s\n",
497
safer_gnutls_strerror(ret));
498
gnutls_global_deinit();
503
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
504
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
508
ret = gnutls_certificate_set_openpgp_key_file
509
(mc.cred, pubkeyfilename, seckeyfilename,
510
GNUTLS_OPENPGP_FMT_BASE64);
511
if(ret != GNUTLS_E_SUCCESS){
513
"Error[%d] while reading the OpenPGP key pair ('%s',"
514
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
515
fprintf(stderr, "The GnuTLS error is: %s\n",
516
safer_gnutls_strerror(ret));
520
/* GnuTLS server initialization */
521
ret = gnutls_dh_params_init(&mc.dh_params);
522
if(ret != GNUTLS_E_SUCCESS){
523
fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
524
" %s\n", safer_gnutls_strerror(ret));
527
ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
528
if(ret != GNUTLS_E_SUCCESS){
529
fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
530
safer_gnutls_strerror(ret));
534
gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
540
gnutls_certificate_free_credentials(mc.cred);
541
gnutls_global_deinit();
542
gnutls_dh_params_deinit(mc.dh_params);
546
static int init_gnutls_session(gnutls_session_t *session){
548
/* GnuTLS session creation */
550
ret = gnutls_init(session, GNUTLS_SERVER);
554
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
555
if(ret != GNUTLS_E_SUCCESS){
556
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
557
safer_gnutls_strerror(ret));
563
ret = gnutls_priority_set_direct(*session, mc.priority, &err);
565
gnutls_deinit(*session);
568
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
569
if(ret != GNUTLS_E_SUCCESS){
570
fprintf(stderr, "Syntax error at: %s\n", err);
571
fprintf(stderr, "GnuTLS error: %s\n",
572
safer_gnutls_strerror(ret));
573
gnutls_deinit(*session);
579
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
582
gnutls_deinit(*session);
585
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
586
if(ret != GNUTLS_E_SUCCESS){
587
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
588
safer_gnutls_strerror(ret));
589
gnutls_deinit(*session);
593
/* ignore client certificate if any. */
594
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
596
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
601
/* Avahi log function callback */
602
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
603
__attribute__((unused)) const char *txt){}
605
/* Called when a Mandos server is found */
606
static int start_mandos_communication(const char *ip, uint16_t port,
607
AvahiIfIndex if_index,
609
int ret, tcp_sd = -1;
612
struct sockaddr_in in;
613
struct sockaddr_in6 in6;
616
char *decrypted_buffer = NULL;
617
size_t buffer_length = 0;
618
size_t buffer_capacity = 0;
621
gnutls_session_t session;
622
int pf; /* Protocol family */
639
fprintf(stderr, "Bad address family: %d\n", af);
644
ret = init_gnutls_session(&session);
650
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
654
tcp_sd = socket(pf, SOCK_STREAM, 0);
657
perror_plus("socket");
667
memset(&to, 0, sizeof(to));
669
to.in6.sin6_family = (sa_family_t)af;
670
ret = inet_pton(af, ip, &to.in6.sin6_addr);
672
to.in.sin_family = (sa_family_t)af;
673
ret = inet_pton(af, ip, &to.in.sin_addr);
677
perror_plus("inet_pton");
683
fprintf(stderr, "Bad address: %s\n", ip);
688
to.in6.sin6_port = htons(port); /* Spurious warnings from
690
-Wunreachable-code */
692
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
693
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
695
if(if_index == AVAHI_IF_UNSPEC){
696
fprintf(stderr, "An IPv6 link-local address is incomplete"
697
" without a network interface\n");
701
/* Set the network interface number as scope */
702
to.in6.sin6_scope_id = (uint32_t)if_index;
705
to.in.sin_port = htons(port); /* Spurious warnings from
707
-Wunreachable-code */
716
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
717
char interface[IF_NAMESIZE];
718
if(if_indextoname((unsigned int)if_index, interface) == NULL){
719
perror_plus("if_indextoname");
721
fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
722
ip, interface, port);
725
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
728
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
729
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
732
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
735
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
739
perror_plus("inet_ntop");
741
if(strcmp(addrstr, ip) != 0){
742
fprintf(stderr, "Canonical address form: %s\n", addrstr);
753
ret = connect(tcp_sd, &to.in6, sizeof(to));
755
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
758
if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
760
perror_plus("connect");
771
const char *out = mandos_protocol_version;
774
size_t out_size = strlen(out);
775
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
776
out_size - written));
779
perror_plus("write");
783
written += (size_t)ret;
784
if(written < out_size){
787
if(out == mandos_protocol_version){
802
fprintf(stderr, "Establishing TLS session with %s\n", ip);
810
/* Spurious warning from -Wint-to-pointer-cast */
811
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
819
ret = gnutls_handshake(session);
824
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
826
if(ret != GNUTLS_E_SUCCESS){
828
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
835
/* Read OpenPGP packet that contains the wanted password */
838
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
849
buffer_capacity = incbuffer(&buffer, buffer_length,
851
if(buffer_capacity == 0){
853
perror_plus("incbuffer");
863
sret = gnutls_record_recv(session, buffer+buffer_length,
870
case GNUTLS_E_INTERRUPTED:
873
case GNUTLS_E_REHANDSHAKE:
875
ret = gnutls_handshake(session);
881
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
883
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
890
fprintf(stderr, "Unknown error while reading data from"
891
" encrypted session with Mandos server\n");
892
gnutls_bye(session, GNUTLS_SHUT_RDWR);
897
buffer_length += (size_t) sret;
902
fprintf(stderr, "Closing TLS session\n");
911
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
916
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
918
if(buffer_length > 0){
919
ssize_t decrypted_buffer_size;
920
decrypted_buffer_size = pgp_packet_decrypt(buffer,
923
if(decrypted_buffer_size >= 0){
926
while(written < (size_t) decrypted_buffer_size){
932
ret = (int)fwrite(decrypted_buffer + written, 1,
933
(size_t)decrypted_buffer_size - written,
935
if(ret == 0 and ferror(stdout)){
938
fprintf(stderr, "Error writing encrypted data: %s\n",
944
written += (size_t)ret;
950
/* Shutdown procedure */
955
free(decrypted_buffer);
958
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
964
perror_plus("close");
966
gnutls_deinit(session);
976
static void resolve_callback(AvahiSServiceResolver *r,
977
AvahiIfIndex interface,
979
AvahiResolverEvent event,
983
const char *host_name,
984
const AvahiAddress *address,
986
AVAHI_GCC_UNUSED AvahiStringList *txt,
987
AVAHI_GCC_UNUSED AvahiLookupResultFlags
989
AVAHI_GCC_UNUSED void* userdata){
992
/* Called whenever a service has been resolved successfully or
1001
case AVAHI_RESOLVER_FAILURE:
1002
fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
1003
" of type '%s' in domain '%s': %s\n", name, type, domain,
1004
avahi_strerror(avahi_server_errno(mc.server)));
1007
case AVAHI_RESOLVER_FOUND:
1009
char ip[AVAHI_ADDRESS_STR_MAX];
1010
avahi_address_snprint(ip, sizeof(ip), address);
1012
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
1013
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
1014
ip, (intmax_t)interface, port);
1016
int ret = start_mandos_communication(ip, port, interface,
1017
avahi_proto_to_af(proto));
1019
avahi_simple_poll_quit(mc.simple_poll);
1021
ret = add_server(ip, port, interface,
1022
avahi_proto_to_af(proto));
1026
avahi_s_service_resolver_free(r);
1029
static void browse_callback(AvahiSServiceBrowser *b,
1030
AvahiIfIndex interface,
1031
AvahiProtocol protocol,
1032
AvahiBrowserEvent event,
1036
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1038
AVAHI_GCC_UNUSED void* userdata){
1041
/* Called whenever a new services becomes available on the LAN or
1042
is removed from the LAN */
1050
case AVAHI_BROWSER_FAILURE:
1052
fprintf(stderr, "(Avahi browser) %s\n",
1053
avahi_strerror(avahi_server_errno(mc.server)));
1054
avahi_simple_poll_quit(mc.simple_poll);
1057
case AVAHI_BROWSER_NEW:
1058
/* We ignore the returned Avahi resolver object. In the callback
1059
function we free it. If the Avahi server is terminated before
1060
the callback function is called the Avahi server will free the
1063
if(avahi_s_service_resolver_new(mc.server, interface, protocol,
1064
name, type, domain, protocol, 0,
1065
resolve_callback, NULL) == NULL)
1066
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
1067
name, avahi_strerror(avahi_server_errno(mc.server)));
1070
case AVAHI_BROWSER_REMOVE:
1073
case AVAHI_BROWSER_ALL_FOR_NOW:
1074
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1076
fprintf(stderr, "No Mandos server found, still searching...\n");
1082
/* Signal handler that stops main loop after SIGTERM */
1083
static void handle_sigterm(int sig){
1088
signal_received = sig;
1089
int old_errno = errno;
1090
/* set main loop to exit */
1091
if(mc.simple_poll != NULL){
1092
avahi_simple_poll_quit(mc.simple_poll);
1097
bool get_flags(const char *ifname, struct ifreq *ifr){
1100
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1102
perror_plus("socket");
1105
strcpy(ifr->ifr_name, ifname);
1106
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1109
perror_plus("ioctl SIOCGIFFLAGS");
1116
bool good_flags(const char *ifname, const struct ifreq *ifr){
1118
/* Reject the loopback device */
1119
if(ifr->ifr_flags & IFF_LOOPBACK){
1121
fprintf(stderr, "Rejecting loopback interface \"%s\"\n",
1126
/* Accept point-to-point devices only if connect_to is specified */
1127
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1129
fprintf(stderr, "Accepting point-to-point interface \"%s\"\n",
1134
/* Otherwise, reject non-broadcast-capable devices */
1135
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1137
fprintf(stderr, "Rejecting non-broadcast interface \"%s\"\n",
1142
/* Reject non-ARP interfaces (including dummy interfaces) */
1143
if(ifr->ifr_flags & IFF_NOARP){
1145
fprintf(stderr, "Rejecting non-ARP interface \"%s\"\n", ifname);
1150
/* Accept this device */
1152
fprintf(stderr, "Interface \"%s\" is good\n", ifname);
1158
* This function determines if a directory entry in /sys/class/net
1159
* corresponds to an acceptable network device.
1160
* (This function is passed to scandir(3) as a filter function.)
1162
int good_interface(const struct dirent *if_entry){
1164
if(if_entry->d_name[0] == '.'){
1169
if(not get_flags(if_entry->d_name, &ifr)){
1173
if(not good_flags(if_entry->d_name, &ifr)){
1180
* This function determines if a directory entry in /sys/class/net
1181
* corresponds to an acceptable network device which is up.
1182
* (This function is passed to scandir(3) as a filter function.)
1184
int up_interface(const struct dirent *if_entry){
1186
char *flagname = NULL;
1187
if(if_entry->d_name[0] == '.'){
1190
int ret = asprintf(&flagname, "%s/%s/flags", sys_class_net,
1193
perror_plus("asprintf");
1196
int flags_fd = (int)TEMP_FAILURE_RETRY(open(flagname, O_RDONLY));
1198
perror_plus("open");
1203
typedef short ifreq_flags; /* ifreq.ifr_flags in netdevice(7) */
1204
/* read line from flags_fd */
1205
ssize_t to_read = 2+(sizeof(ifreq_flags)*2)+1; /* "0x1003\n" */
1206
char *flagstring = malloc((size_t)to_read+1); /* +1 for final \0 */
1207
flagstring[(size_t)to_read] = '\0';
1208
if(flagstring == NULL){
1209
perror_plus("malloc");
1214
ssret = (ssize_t)TEMP_FAILURE_RETRY(read(flags_fd, flagstring,
1217
perror_plus("read");
1231
tmpmax = strtoimax(flagstring, &tmp, 0);
1232
if(errno != 0 or tmp == flagstring or (*tmp != '\0'
1233
and not (isspace(*tmp)))
1234
or tmpmax != (ifreq_flags)tmpmax){
1236
fprintf(stderr, "Invalid flags \"%s\" for interface \"%s\"\n",
1237
flagstring, if_entry->d_name);
1243
ifreq_flags flags = (ifreq_flags)tmpmax;
1244
/* Reject the loopback device */
1245
if(flags & IFF_LOOPBACK){
1247
fprintf(stderr, "Rejecting loopback interface \"%s\"\n",
1253
/* Reject down interfaces */
1254
if(not (flags & IFF_UP)){
1258
/* Accept point-to-point devices only if connect_to is specified */
1259
if(connect_to != NULL and (flags & IFF_POINTOPOINT)){
1261
fprintf(stderr, "Accepting point-to-point interface \"%s\"\n",
1266
/* Otherwise, reject non-broadcast-capable devices */
1267
if(not (flags & IFF_BROADCAST)){
1269
fprintf(stderr, "Rejecting non-broadcast interface \"%s\"\n",
1274
/* Reject non-ARP interfaces (including dummy interfaces) */
1275
if(flags & IFF_NOARP){
1277
fprintf(stderr, "Rejecting non-ARP interface \"%s\"\n",
1282
/* Accept this device */
1284
fprintf(stderr, "Interface \"%s\" is acceptable\n",
1290
int notdotentries(const struct dirent *direntry){
1291
/* Skip "." and ".." */
1292
if(direntry->d_name[0] == '.'
1293
and (direntry->d_name[1] == '\0'
1294
or (direntry->d_name[1] == '.'
1295
and direntry->d_name[2] == '\0'))){
1301
/* Is this directory entry a runnable program? */
1302
int runnable_hook(const struct dirent *direntry){
1306
if((direntry->d_name)[0] == '\0'){
1311
/* Save pointer to last character */
1312
char *end = strchr(direntry->d_name, '\0')-1;
1319
if(((direntry->d_name)[0] == '#')
1321
/* Temporary #name# */
1325
/* XXX more rules here */
1327
ret = stat(direntry->d_name, &st);
1330
perror_plus("Could not stat plugin");
1334
if(not (st.st_mode & S_ISREG)){
1335
/* Not a regular file */
1338
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1339
/* Not executable */
1345
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval){
1347
struct timespec now;
1348
struct timespec waited_time;
1349
intmax_t block_time;
1352
if(mc.current_server == NULL){
1355
"Wait until first server is found. No timeout!\n");
1357
ret = avahi_simple_poll_iterate(s, -1);
1360
fprintf(stderr, "Check current_server if we should run it,"
1363
/* the current time */
1364
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1366
perror_plus("clock_gettime");
1369
/* Calculating in ms how long time between now and server
1370
who we visted longest time ago. Now - last seen. */
1371
waited_time.tv_sec = (now.tv_sec
1372
- mc.current_server->last_seen.tv_sec);
1373
waited_time.tv_nsec = (now.tv_nsec
1374
- mc.current_server->last_seen.tv_nsec);
1375
/* total time is 10s/10,000ms.
1376
Converting to s from ms by dividing by 1,000,
1377
and ns to ms by dividing by 1,000,000. */
1378
block_time = ((retry_interval
1379
- ((intmax_t)waited_time.tv_sec * 1000))
1380
- ((intmax_t)waited_time.tv_nsec / 1000000));
1383
fprintf(stderr, "Blocking for %" PRIdMAX " ms\n", block_time);
1386
if(block_time <= 0){
1387
ret = start_mandos_communication(mc.current_server->ip,
1388
mc.current_server->port,
1389
mc.current_server->if_index,
1390
mc.current_server->af);
1392
avahi_simple_poll_quit(mc.simple_poll);
1395
ret = clock_gettime(CLOCK_MONOTONIC,
1396
&mc.current_server->last_seen);
1398
perror_plus("clock_gettime");
1401
mc.current_server = mc.current_server->next;
1402
block_time = 0; /* Call avahi to find new Mandos
1403
servers, but don't block */
1406
ret = avahi_simple_poll_iterate(s, (int)block_time);
1409
if (ret > 0 or errno != EINTR) {
1410
return (ret != 1) ? ret : 0;
1416
int main(int argc, char *argv[]){
1417
AvahiSServiceBrowser *sb = NULL;
1422
int exitcode = EXIT_SUCCESS;
1423
const char *interface = "";
1424
struct ifreq network;
1426
bool take_down_interface = false;
1429
char tempdir[] = "/tmp/mandosXXXXXX";
1430
bool tempdir_created = false;
1431
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1432
const char *seckey = PATHDIR "/" SECKEY;
1433
const char *pubkey = PATHDIR "/" PUBKEY;
1435
bool gnutls_initialized = false;
1436
bool gpgme_initialized = false;
1438
double retry_interval = 10; /* 10s between trying a server and
1439
retrying the same server again */
1441
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
1442
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
1447
/* Lower any group privileges we might have, just to be safe */
1451
perror_plus("setgid");
1454
/* Lower user privileges (temporarily) */
1458
perror_plus("seteuid");
1466
struct argp_option options[] = {
1467
{ .name = "debug", .key = 128,
1468
.doc = "Debug mode", .group = 3 },
1469
{ .name = "connect", .key = 'c',
1470
.arg = "ADDRESS:PORT",
1471
.doc = "Connect directly to a specific Mandos server",
1473
{ .name = "interface", .key = 'i',
1475
.doc = "Network interface that will be used to search for"
1478
{ .name = "seckey", .key = 's',
1480
.doc = "OpenPGP secret key file base name",
1482
{ .name = "pubkey", .key = 'p',
1484
.doc = "OpenPGP public key file base name",
1486
{ .name = "dh-bits", .key = 129,
1488
.doc = "Bit length of the prime number used in the"
1489
" Diffie-Hellman key exchange",
1491
{ .name = "priority", .key = 130,
1493
.doc = "GnuTLS priority string for the TLS handshake",
1495
{ .name = "delay", .key = 131,
1497
.doc = "Maximum delay to wait for interface startup",
1499
{ .name = "retry", .key = 132,
1501
.doc = "Retry interval used when denied by the mandos server",
1504
* These reproduce what we would get without ARGP_NO_HELP
1506
{ .name = "help", .key = '?',
1507
.doc = "Give this help list", .group = -1 },
1508
{ .name = "usage", .key = -3,
1509
.doc = "Give a short usage message", .group = -1 },
1510
{ .name = "version", .key = 'V',
1511
.doc = "Print program version", .group = -1 },
1515
error_t parse_opt(int key, char *arg,
1516
struct argp_state *state){
1519
case 128: /* --debug */
1522
case 'c': /* --connect */
1525
case 'i': /* --interface */
1528
case 's': /* --seckey */
1531
case 'p': /* --pubkey */
1534
case 129: /* --dh-bits */
1536
tmpmax = strtoimax(arg, &tmp, 10);
1537
if(errno != 0 or tmp == arg or *tmp != '\0'
1538
or tmpmax != (typeof(mc.dh_bits))tmpmax){
1539
argp_error(state, "Bad number of DH bits");
1541
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
1543
case 130: /* --priority */
1546
case 131: /* --delay */
1548
delay = strtof(arg, &tmp);
1549
if(errno != 0 or tmp == arg or *tmp != '\0'){
1550
argp_error(state, "Bad delay");
1552
case 132: /* --retry */
1554
retry_interval = strtod(arg, &tmp);
1555
if(errno != 0 or tmp == arg or *tmp != '\0'
1556
or (retry_interval * 1000) > INT_MAX
1557
or retry_interval < 0){
1558
argp_error(state, "Bad retry interval");
1562
* These reproduce what we would get without ARGP_NO_HELP
1564
case '?': /* --help */
1565
argp_state_help(state, state->out_stream,
1566
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
1567
& ~(unsigned int)ARGP_HELP_EXIT_OK);
1568
case -3: /* --usage */
1569
argp_state_help(state, state->out_stream,
1570
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
1571
case 'V': /* --version */
1572
fprintf(state->out_stream, "%s\n", argp_program_version);
1573
exit(argp_err_exit_status);
1576
return ARGP_ERR_UNKNOWN;
1581
struct argp argp = { .options = options, .parser = parse_opt,
1583
.doc = "Mandos client -- Get and decrypt"
1584
" passwords from a Mandos server" };
1585
ret = argp_parse(&argp, argc, argv,
1586
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
1593
perror_plus("argp_parse");
1594
exitcode = EX_OSERR;
1597
exitcode = EX_USAGE;
1603
/* Work around Debian bug #633582:
1604
<http://bugs.debian.org/633582> */
1607
/* Re-raise priviliges */
1611
perror_plus("seteuid");
1614
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
1615
int seckey_fd = open(seckey, O_RDONLY);
1616
if(seckey_fd == -1){
1617
perror_plus("open");
1619
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
1621
perror_plus("fstat");
1623
if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
1624
ret = fchown(seckey_fd, uid, gid);
1626
perror_plus("fchown");
1630
TEMP_FAILURE_RETRY(close(seckey_fd));
1634
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
1635
int pubkey_fd = open(pubkey, O_RDONLY);
1636
if(pubkey_fd == -1){
1637
perror_plus("open");
1639
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
1641
perror_plus("fstat");
1643
if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
1644
ret = fchown(pubkey_fd, uid, gid);
1646
perror_plus("fchown");
1650
TEMP_FAILURE_RETRY(close(pubkey_fd));
1654
/* Lower privileges */
1658
perror_plus("seteuid");
1662
/* Find network hooks and run them */
1664
struct dirent **direntries;
1665
struct dirent *direntry;
1666
int numhooks = scandir(HOOKDIR, &direntries, runnable_hook,
1668
int devnull = open("/dev/null", O_RDONLY);
1669
for(int i = 0; i < numhooks; i++){
1670
direntry = direntries[0];
1671
char *fullname = NULL;
1672
ret = asprintf(&fullname, "%s/%s", tempdir,
1675
perror_plus("asprintf");
1678
pid_t hook_pid = fork();
1681
dup2(devnull, STDIN_FILENO);
1683
dup2(STDERR_FILENO, STDOUT_FILENO);
1684
setenv("DEVICE", interface, 1);
1685
setenv("VERBOSE", debug ? "1" : "0", 1);
1686
setenv("MODE", "start", 1);
1687
/* setenv( XXX more here */
1688
ret = execl(fullname, direntry->d_name, "start");
1689
perror_plus("execl");
1700
avahi_set_log_function(empty_log);
1703
if(interface[0] == '\0'){
1704
struct dirent **direntries;
1705
ret = scandir(sys_class_net, &direntries, good_interface,
1708
/* Pick the first good interface */
1709
interface = strdup(direntries[0]->d_name);
1711
fprintf(stderr, "Using interface \"%s\"\n", interface);
1713
if(interface == NULL){
1714
perror_plus("malloc");
1716
exitcode = EXIT_FAILURE;
1722
fprintf(stderr, "Could not find a network interface\n");
1723
exitcode = EXIT_FAILURE;
1728
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1729
from the signal handler */
1730
/* Initialize the pseudo-RNG for Avahi */
1731
srand((unsigned int) time(NULL));
1732
mc.simple_poll = avahi_simple_poll_new();
1733
if(mc.simple_poll == NULL){
1734
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1735
exitcode = EX_UNAVAILABLE;
1739
sigemptyset(&sigterm_action.sa_mask);
1740
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1742
perror_plus("sigaddset");
1743
exitcode = EX_OSERR;
1746
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1748
perror_plus("sigaddset");
1749
exitcode = EX_OSERR;
1752
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1754
perror_plus("sigaddset");
1755
exitcode = EX_OSERR;
1758
/* Need to check if the handler is SIG_IGN before handling:
1759
| [[info:libc:Initial Signal Actions]] |
1760
| [[info:libc:Basic Signal Handling]] |
1762
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
1764
perror_plus("sigaction");
1767
if(old_sigterm_action.sa_handler != SIG_IGN){
1768
ret = sigaction(SIGINT, &sigterm_action, NULL);
1770
perror_plus("sigaction");
1771
exitcode = EX_OSERR;
1775
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
1777
perror_plus("sigaction");
1780
if(old_sigterm_action.sa_handler != SIG_IGN){
1781
ret = sigaction(SIGHUP, &sigterm_action, NULL);
1783
perror_plus("sigaction");
1784
exitcode = EX_OSERR;
1788
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
1790
perror_plus("sigaction");
1793
if(old_sigterm_action.sa_handler != SIG_IGN){
1794
ret = sigaction(SIGTERM, &sigterm_action, NULL);
1796
perror_plus("sigaction");
1797
exitcode = EX_OSERR;
1802
/* If the interface is down, bring it up */
1803
if(strcmp(interface, "none") != 0){
1804
if_index = (AvahiIfIndex) if_nametoindex(interface);
1806
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1807
exitcode = EX_UNAVAILABLE;
1815
/* Re-raise priviliges */
1819
perror_plus("seteuid");
1823
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1824
messages about the network interface to mess up the prompt */
1825
ret = klogctl(8, NULL, 5);
1826
bool restore_loglevel = true;
1828
restore_loglevel = false;
1829
perror_plus("klogctl");
1831
#endif /* __linux__ */
1833
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1835
perror_plus("socket");
1836
exitcode = EX_OSERR;
1838
if(restore_loglevel){
1839
ret = klogctl(7, NULL, 0);
1841
perror_plus("klogctl");
1844
#endif /* __linux__ */
1845
/* Lower privileges */
1849
perror_plus("seteuid");
1853
strcpy(network.ifr_name, interface);
1854
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1856
perror_plus("ioctl SIOCGIFFLAGS");
1858
if(restore_loglevel){
1859
ret = klogctl(7, NULL, 0);
1861
perror_plus("klogctl");
1864
#endif /* __linux__ */
1865
exitcode = EX_OSERR;
1866
/* Lower privileges */
1870
perror_plus("seteuid");
1874
if((network.ifr_flags & IFF_UP) == 0){
1875
network.ifr_flags |= IFF_UP;
1876
take_down_interface = true;
1877
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1879
take_down_interface = false;
1880
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1881
exitcode = EX_OSERR;
1883
if(restore_loglevel){
1884
ret = klogctl(7, NULL, 0);
1886
perror_plus("klogctl");
1889
#endif /* __linux__ */
1890
/* Lower privileges */
1894
perror_plus("seteuid");
1899
/* Sleep checking until interface is running.
1900
Check every 0.25s, up to total time of delay */
1901
for(int i=0; i < delay * 4; i++){
1902
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1904
perror_plus("ioctl SIOCGIFFLAGS");
1905
} else if(network.ifr_flags & IFF_RUNNING){
1908
struct timespec sleeptime = { .tv_nsec = 250000000 };
1909
ret = nanosleep(&sleeptime, NULL);
1910
if(ret == -1 and errno != EINTR){
1911
perror_plus("nanosleep");
1914
if(not take_down_interface){
1915
/* We won't need the socket anymore */
1916
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1918
perror_plus("close");
1922
if(restore_loglevel){
1923
/* Restores kernel loglevel to default */
1924
ret = klogctl(7, NULL, 0);
1926
perror_plus("klogctl");
1929
#endif /* __linux__ */
1930
/* Lower privileges */
1932
if(take_down_interface){
1933
/* Lower privileges */
1936
perror_plus("seteuid");
1939
/* Lower privileges permanently */
1942
perror_plus("setuid");
1951
ret = init_gnutls_global(pubkey, seckey);
1953
fprintf(stderr, "init_gnutls_global failed\n");
1954
exitcode = EX_UNAVAILABLE;
1957
gnutls_initialized = true;
1964
if(mkdtemp(tempdir) == NULL){
1965
perror_plus("mkdtemp");
1968
tempdir_created = true;
1974
if(not init_gpgme(pubkey, seckey, tempdir)){
1975
fprintf(stderr, "init_gpgme failed\n");
1976
exitcode = EX_UNAVAILABLE;
1979
gpgme_initialized = true;
1986
if(connect_to != NULL){
1987
/* Connect directly, do not use Zeroconf */
1988
/* (Mainly meant for debugging) */
1989
char *address = strrchr(connect_to, ':');
1990
if(address == NULL){
1991
fprintf(stderr, "No colon in address\n");
1992
exitcode = EX_USAGE;
2002
tmpmax = strtoimax(address+1, &tmp, 10);
2003
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2004
or tmpmax != (uint16_t)tmpmax){
2005
fprintf(stderr, "Bad port number\n");
2006
exitcode = EX_USAGE;
2014
port = (uint16_t)tmpmax;
2016
/* Colon in address indicates IPv6 */
2018
if(strchr(connect_to, ':') != NULL){
2020
/* Accept [] around IPv6 address - see RFC 5952 */
2021
if(connect_to[0] == '[' and address[-1] == ']')
2029
address = connect_to;
2035
while(not quit_now){
2036
ret = start_mandos_communication(address, port, if_index, af);
2037
if(quit_now or ret == 0){
2041
fprintf(stderr, "Retrying in %d seconds\n",
2042
(int)retry_interval);
2044
sleep((int)retry_interval);
2048
exitcode = EXIT_SUCCESS;
2059
AvahiServerConfig config;
2060
/* Do not publish any local Zeroconf records */
2061
avahi_server_config_init(&config);
2062
config.publish_hinfo = 0;
2063
config.publish_addresses = 0;
2064
config.publish_workstation = 0;
2065
config.publish_domain = 0;
2067
/* Allocate a new server */
2068
mc.server = avahi_server_new(avahi_simple_poll_get
2069
(mc.simple_poll), &config, NULL,
2072
/* Free the Avahi configuration data */
2073
avahi_server_config_free(&config);
2076
/* Check if creating the Avahi server object succeeded */
2077
if(mc.server == NULL){
2078
fprintf(stderr, "Failed to create Avahi server: %s\n",
2079
avahi_strerror(error));
2080
exitcode = EX_UNAVAILABLE;
2088
/* Create the Avahi service browser */
2089
sb = avahi_s_service_browser_new(mc.server, if_index,
2090
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2091
NULL, 0, browse_callback, NULL);
2093
fprintf(stderr, "Failed to create service browser: %s\n",
2094
avahi_strerror(avahi_server_errno(mc.server)));
2095
exitcode = EX_UNAVAILABLE;
2103
/* Run the main loop */
2106
fprintf(stderr, "Starting Avahi loop search\n");
2109
ret = avahi_loop_with_timeout(mc.simple_poll,
2110
(int)(retry_interval * 1000));
2112
fprintf(stderr, "avahi_loop_with_timeout exited %s\n",
2113
(ret == 0) ? "successfully" : "with error");
2119
fprintf(stderr, "%s exiting\n", argv[0]);
2122
/* Cleanup things */
2124
avahi_s_service_browser_free(sb);
2126
if(mc.server != NULL)
2127
avahi_server_free(mc.server);
2129
if(mc.simple_poll != NULL)
2130
avahi_simple_poll_free(mc.simple_poll);
2132
if(gnutls_initialized){
2133
gnutls_certificate_free_credentials(mc.cred);
2134
gnutls_global_deinit();
2135
gnutls_dh_params_deinit(mc.dh_params);
2138
if(gpgme_initialized){
2139
gpgme_release(mc.ctx);
2142
/* Cleans up the circular linked list of Mandos servers the client
2144
if(mc.current_server != NULL){
2145
mc.current_server->prev->next = NULL;
2146
while(mc.current_server != NULL){
2147
server *next = mc.current_server->next;
2148
free(mc.current_server);
2149
mc.current_server = next;
2153
/* XXX run network hooks "stop" here */
2155
/* Take down the network interface */
2156
if(take_down_interface){
2157
/* Re-raise priviliges */
2161
perror_plus("seteuid");
2164
ret = ioctl(sd, SIOCGIFFLAGS, &network);
2166
perror_plus("ioctl SIOCGIFFLAGS");
2167
} else if(network.ifr_flags & IFF_UP) {
2168
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2169
ret = ioctl(sd, SIOCSIFFLAGS, &network);
2171
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2174
ret = (int)TEMP_FAILURE_RETRY(close(sd));
2176
perror_plus("close");
2178
/* Lower privileges permanently */
2182
perror_plus("setuid");
2187
/* Removes the GPGME temp directory and all files inside */
2188
if(tempdir_created){
2189
struct dirent **direntries = NULL;
2190
struct dirent *direntry = NULL;
2191
int numentries = scandir(tempdir, &direntries, notdotentries,
2193
if (numentries > 0){
2194
for(int i = 0; i < numentries; i++){
2195
direntry = direntries[i];
2196
char *fullname = NULL;
2197
ret = asprintf(&fullname, "%s/%s", tempdir,
2200
perror_plus("asprintf");
2203
ret = remove(fullname);
2205
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
2212
/* need to clean even if 0 because man page doesn't specify */
2214
if (numentries == -1){
2215
perror_plus("scandir");
2217
ret = rmdir(tempdir);
2218
if(ret == -1 and errno != ENOENT){
2219
perror_plus("rmdir");
2224
sigemptyset(&old_sigterm_action.sa_mask);
2225
old_sigterm_action.sa_handler = SIG_DFL;
2226
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
2227
&old_sigterm_action,
2230
perror_plus("sigaction");
2233
ret = raise(signal_received);
2234
} while(ret != 0 and errno == EINTR);
2236
perror_plus("raise");
2239
TEMP_FAILURE_RETRY(pause());