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(), S_ISREG */
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 */
88
#include <sys/wait.h> /* waitpid(), WIFEXITED(),
89
WEXITSTATUS(), WTERMSIG() */
92
#include <sys/klog.h> /* klogctl() */
93
#endif /* __linux__ */
96
/* All Avahi types, constants and functions
99
#include <avahi-core/core.h>
100
#include <avahi-core/lookup.h>
101
#include <avahi-core/log.h>
102
#include <avahi-common/simple-watch.h>
103
#include <avahi-common/malloc.h>
104
#include <avahi-common/error.h>
107
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
110
init_gnutls_session(),
112
#include <gnutls/openpgp.h>
113
/* gnutls_certificate_set_openpgp_key_file(),
114
GNUTLS_OPENPGP_FMT_BASE64 */
117
#include <gpgme.h> /* All GPGME types, constants and
120
GPGME_PROTOCOL_OpenPGP,
123
#define BUFFER_SIZE 256
125
#define PATHDIR "/conf/conf.d/mandos"
126
#define SECKEY "seckey.txt"
127
#define PUBKEY "pubkey.txt"
128
#define HOOKDIR "/lib/mandos/network-hooks.d"
131
static const char mandos_protocol_version[] = "1";
132
const char *argp_program_version = "mandos-client " VERSION;
133
const char *argp_program_bug_address = "<mandos@recompile.se>";
134
static const char sys_class_net[] = "/sys/class/net";
135
char *connect_to = NULL;
136
const char *hookdir = HOOKDIR;
138
/* Doubly linked list that need to be circularly linked when used */
139
typedef struct server{
142
AvahiIfIndex if_index;
144
struct timespec last_seen;
149
/* Used for passing in values through the Avahi callback functions */
151
AvahiSimplePoll *simple_poll;
153
gnutls_certificate_credentials_t cred;
154
unsigned int dh_bits;
155
gnutls_dh_params_t dh_params;
156
const char *priority;
158
server *current_server;
161
/* global context so signal handler can reach it*/
162
mandos_context mc = { .simple_poll = NULL, .server = NULL,
163
.dh_bits = 1024, .priority = "SECURE256"
164
":!CTYPE-X.509:+CTYPE-OPENPGP",
165
.current_server = NULL };
167
sig_atomic_t quit_now = 0;
168
int signal_received = 0;
170
/* Function to use when printing errors */
171
void perror_plus(const char *print_text){
172
fprintf(stderr, "Mandos plugin %s: ",
173
program_invocation_short_name);
177
int fprintf_plus(FILE *stream, const char *format, ...){
179
va_start (ap, format);
181
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ", program_invocation_short_name));
182
return TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
186
* Make additional room in "buffer" for at least BUFFER_SIZE more
187
* bytes. "buffer_capacity" is how much is currently allocated,
188
* "buffer_length" is how much is already used.
190
size_t incbuffer(char **buffer, size_t buffer_length,
191
size_t buffer_capacity){
192
if(buffer_length + BUFFER_SIZE > buffer_capacity){
193
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
197
buffer_capacity += BUFFER_SIZE;
199
return buffer_capacity;
202
/* Add server to set of servers to retry periodically */
203
int add_server(const char *ip, uint16_t port, AvahiIfIndex if_index,
206
server *new_server = malloc(sizeof(server));
207
if(new_server == NULL){
208
perror_plus("malloc");
211
*new_server = (server){ .ip = strdup(ip),
213
.if_index = if_index,
215
if(new_server->ip == NULL){
216
perror_plus("strdup");
219
/* Special case of first server */
220
if (mc.current_server == NULL){
221
new_server->next = new_server;
222
new_server->prev = new_server;
223
mc.current_server = new_server;
224
/* Place the new server last in the list */
226
new_server->next = mc.current_server;
227
new_server->prev = mc.current_server->prev;
228
new_server->prev->next = new_server;
229
mc.current_server->prev = new_server;
231
ret = clock_gettime(CLOCK_MONOTONIC, &mc.current_server->last_seen);
233
perror_plus("clock_gettime");
242
static bool init_gpgme(const char *seckey, const char *pubkey,
243
const char *tempdir){
245
gpgme_engine_info_t engine_info;
249
* Helper function to insert pub and seckey to the engine keyring.
251
bool import_key(const char *filename){
254
gpgme_data_t pgp_data;
256
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
262
rc = gpgme_data_new_from_fd(&pgp_data, fd);
263
if(rc != GPG_ERR_NO_ERROR){
264
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
265
gpgme_strsource(rc), gpgme_strerror(rc));
269
rc = gpgme_op_import(mc.ctx, pgp_data);
270
if(rc != GPG_ERR_NO_ERROR){
271
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
272
gpgme_strsource(rc), gpgme_strerror(rc));
276
ret = (int)TEMP_FAILURE_RETRY(close(fd));
278
perror_plus("close");
280
gpgme_data_release(pgp_data);
285
fprintf_plus(stderr, "Initializing GPGME\n");
289
gpgme_check_version(NULL);
290
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
291
if(rc != GPG_ERR_NO_ERROR){
292
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
293
gpgme_strsource(rc), gpgme_strerror(rc));
297
/* Set GPGME home directory for the OpenPGP engine only */
298
rc = gpgme_get_engine_info(&engine_info);
299
if(rc != GPG_ERR_NO_ERROR){
300
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
301
gpgme_strsource(rc), gpgme_strerror(rc));
304
while(engine_info != NULL){
305
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
306
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
307
engine_info->file_name, tempdir);
310
engine_info = engine_info->next;
312
if(engine_info == NULL){
313
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n", tempdir);
317
/* Create new GPGME "context" */
318
rc = gpgme_new(&(mc.ctx));
319
if(rc != GPG_ERR_NO_ERROR){
320
fprintf_plus(stderr, "Mandos plugin mandos-client: "
321
"bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
326
if(not import_key(pubkey) or not import_key(seckey)){
334
* Decrypt OpenPGP data.
335
* Returns -1 on error
337
static ssize_t pgp_packet_decrypt(const char *cryptotext,
340
gpgme_data_t dh_crypto, dh_plain;
343
size_t plaintext_capacity = 0;
344
ssize_t plaintext_length = 0;
347
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
350
/* Create new GPGME data buffer from memory cryptotext */
351
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
353
if(rc != GPG_ERR_NO_ERROR){
354
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
355
gpgme_strsource(rc), gpgme_strerror(rc));
359
/* Create new empty GPGME data buffer for the plaintext */
360
rc = gpgme_data_new(&dh_plain);
361
if(rc != GPG_ERR_NO_ERROR){
362
fprintf_plus(stderr, "Mandos plugin mandos-client: "
363
"bad gpgme_data_new: %s: %s\n",
364
gpgme_strsource(rc), gpgme_strerror(rc));
365
gpgme_data_release(dh_crypto);
369
/* Decrypt data from the cryptotext data buffer to the plaintext
371
rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
372
if(rc != GPG_ERR_NO_ERROR){
373
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
374
gpgme_strsource(rc), gpgme_strerror(rc));
375
plaintext_length = -1;
377
gpgme_decrypt_result_t result;
378
result = gpgme_op_decrypt_result(mc.ctx);
380
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
382
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
383
result->unsupported_algorithm);
384
fprintf_plus(stderr, "Wrong key usage: %u\n",
385
result->wrong_key_usage);
386
if(result->file_name != NULL){
387
fprintf_plus(stderr, "File name: %s\n", result->file_name);
389
gpgme_recipient_t recipient;
390
recipient = result->recipients;
391
while(recipient != NULL){
392
fprintf_plus(stderr, "Public key algorithm: %s\n",
393
gpgme_pubkey_algo_name(recipient->pubkey_algo));
394
fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
395
fprintf_plus(stderr, "Secret key available: %s\n",
396
recipient->status == GPG_ERR_NO_SECKEY
398
recipient = recipient->next;
406
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
409
/* Seek back to the beginning of the GPGME plaintext data buffer */
410
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
411
perror_plus("gpgme_data_seek");
412
plaintext_length = -1;
418
plaintext_capacity = incbuffer(plaintext,
419
(size_t)plaintext_length,
421
if(plaintext_capacity == 0){
422
perror_plus("incbuffer");
423
plaintext_length = -1;
427
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
429
/* Print the data, if any */
435
perror_plus("gpgme_data_read");
436
plaintext_length = -1;
439
plaintext_length += ret;
443
fprintf_plus(stderr, "Decrypted password is: ");
444
for(ssize_t i = 0; i < plaintext_length; i++){
445
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
447
fprintf(stderr, "\n");
452
/* Delete the GPGME cryptotext data buffer */
453
gpgme_data_release(dh_crypto);
455
/* Delete the GPGME plaintext data buffer */
456
gpgme_data_release(dh_plain);
457
return plaintext_length;
460
static const char * safer_gnutls_strerror(int value){
461
const char *ret = gnutls_strerror(value); /* Spurious warning from
462
-Wunreachable-code */
468
/* GnuTLS log function callback */
469
static void debuggnutls(__attribute__((unused)) int level,
471
fprintf_plus(stderr, "GnuTLS: %s", string);
474
static int init_gnutls_global(const char *pubkeyfilename,
475
const char *seckeyfilename){
479
fprintf_plus(stderr, "Initializing GnuTLS\n");
482
ret = gnutls_global_init();
483
if(ret != GNUTLS_E_SUCCESS){
484
fprintf_plus(stderr, "GnuTLS global_init: %s\n", safer_gnutls_strerror(ret));
489
/* "Use a log level over 10 to enable all debugging options."
492
gnutls_global_set_log_level(11);
493
gnutls_global_set_log_function(debuggnutls);
496
/* OpenPGP credentials */
497
ret = gnutls_certificate_allocate_credentials(&mc.cred);
498
if(ret != GNUTLS_E_SUCCESS){
499
fprintf_plus(stderr, "GnuTLS memory error: %s\n", safer_gnutls_strerror(ret));
500
gnutls_global_deinit();
505
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
506
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
510
ret = gnutls_certificate_set_openpgp_key_file
511
(mc.cred, pubkeyfilename, seckeyfilename,
512
GNUTLS_OPENPGP_FMT_BASE64);
513
if(ret != GNUTLS_E_SUCCESS){
515
"Error[%d] while reading the OpenPGP key pair ('%s',"
516
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
517
fprintf_plus(stderr, "The GnuTLS error is: %s\n", safer_gnutls_strerror(ret));
521
/* GnuTLS server initialization */
522
ret = gnutls_dh_params_init(&mc.dh_params);
523
if(ret != GNUTLS_E_SUCCESS){
524
fprintf_plus(stderr, "Error in GnuTLS DH parameter initialization:"
525
" %s\n", safer_gnutls_strerror(ret));
528
ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
529
if(ret != GNUTLS_E_SUCCESS){
530
fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
531
safer_gnutls_strerror(ret));
535
gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
541
gnutls_certificate_free_credentials(mc.cred);
542
gnutls_global_deinit();
543
gnutls_dh_params_deinit(mc.dh_params);
547
static int init_gnutls_session(gnutls_session_t *session){
549
/* GnuTLS session creation */
551
ret = gnutls_init(session, GNUTLS_SERVER);
555
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
556
if(ret != GNUTLS_E_SUCCESS){
557
fprintf_plus(stderr, "Error in GnuTLS session initialization: %s\n",
558
safer_gnutls_strerror(ret));
564
ret = gnutls_priority_set_direct(*session, mc.priority, &err);
566
gnutls_deinit(*session);
569
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
570
if(ret != GNUTLS_E_SUCCESS){
571
fprintf_plus(stderr, "Syntax error at: %s\n", err);
572
fprintf_plus(stderr, "GnuTLS error: %s\n", 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_plus(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_plus(stderr, "Bad address family: %d\n", af);
644
ret = init_gnutls_session(&session);
650
fprintf_plus(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_plus(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_plus(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_plus(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
722
ip, interface, port);
725
fprintf_plus(stderr, "Connection to: %s, port %" PRIu16 "\n", ip, port);
727
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
728
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
731
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
734
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
738
perror_plus("inet_ntop");
740
if(strcmp(addrstr, ip) != 0){
741
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
752
ret = connect(tcp_sd, &to.in6, sizeof(to));
754
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
757
if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
759
perror_plus("connect");
770
const char *out = mandos_protocol_version;
773
size_t out_size = strlen(out);
774
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
775
out_size - written));
778
perror_plus("write");
782
written += (size_t)ret;
783
if(written < out_size){
786
if(out == mandos_protocol_version){
801
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
809
/* Spurious warning from -Wint-to-pointer-cast */
810
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
818
ret = gnutls_handshake(session);
823
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
825
if(ret != GNUTLS_E_SUCCESS){
827
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
834
/* Read OpenPGP packet that contains the wanted password */
837
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from %s\n", ip);
847
buffer_capacity = incbuffer(&buffer, buffer_length,
849
if(buffer_capacity == 0){
851
perror_plus("incbuffer");
861
sret = gnutls_record_recv(session, buffer+buffer_length,
868
case GNUTLS_E_INTERRUPTED:
871
case GNUTLS_E_REHANDSHAKE:
873
ret = gnutls_handshake(session);
879
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
881
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed ***\n");
888
fprintf_plus(stderr, "Unknown error while reading data from"
889
" encrypted session with Mandos server\n");
890
gnutls_bye(session, GNUTLS_SHUT_RDWR);
895
buffer_length += (size_t) sret;
900
fprintf_plus(stderr, "Closing TLS session\n");
909
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
914
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
916
if(buffer_length > 0){
917
ssize_t decrypted_buffer_size;
918
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
920
if(decrypted_buffer_size >= 0){
923
while(written < (size_t) decrypted_buffer_size){
929
ret = (int)fwrite(decrypted_buffer + written, 1,
930
(size_t)decrypted_buffer_size - written,
932
if(ret == 0 and ferror(stdout)){
935
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
941
written += (size_t)ret;
947
/* Shutdown procedure */
952
free(decrypted_buffer);
955
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
961
perror_plus("close");
963
gnutls_deinit(session);
973
static void resolve_callback(AvahiSServiceResolver *r,
974
AvahiIfIndex interface,
976
AvahiResolverEvent event,
980
const char *host_name,
981
const AvahiAddress *address,
983
AVAHI_GCC_UNUSED AvahiStringList *txt,
984
AVAHI_GCC_UNUSED AvahiLookupResultFlags
986
AVAHI_GCC_UNUSED void* userdata){
989
/* Called whenever a service has been resolved successfully or
998
case AVAHI_RESOLVER_FAILURE:
999
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
1000
" of type '%s' in domain '%s': %s\n", name, type, domain,
1001
avahi_strerror(avahi_server_errno(mc.server)));
1004
case AVAHI_RESOLVER_FOUND:
1006
char ip[AVAHI_ADDRESS_STR_MAX];
1007
avahi_address_snprint(ip, sizeof(ip), address);
1009
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1010
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
1011
ip, (intmax_t)interface, port);
1013
int ret = start_mandos_communication(ip, port, interface,
1014
avahi_proto_to_af(proto));
1016
avahi_simple_poll_quit(mc.simple_poll);
1018
ret = add_server(ip, port, interface,
1019
avahi_proto_to_af(proto));
1023
avahi_s_service_resolver_free(r);
1026
static void browse_callback(AvahiSServiceBrowser *b,
1027
AvahiIfIndex interface,
1028
AvahiProtocol protocol,
1029
AvahiBrowserEvent event,
1033
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1035
AVAHI_GCC_UNUSED void* userdata){
1038
/* Called whenever a new services becomes available on the LAN or
1039
is removed from the LAN */
1047
case AVAHI_BROWSER_FAILURE:
1049
fprintf_plus(stderr, "(Avahi browser) %s\n",
1050
avahi_strerror(avahi_server_errno(mc.server)));
1051
avahi_simple_poll_quit(mc.simple_poll);
1054
case AVAHI_BROWSER_NEW:
1055
/* We ignore the returned Avahi resolver object. In the callback
1056
function we free it. If the Avahi server is terminated before
1057
the callback function is called the Avahi server will free the
1060
if(avahi_s_service_resolver_new(mc.server, interface, protocol,
1061
name, type, domain, protocol, 0,
1062
resolve_callback, NULL) == NULL)
1063
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s': %s\n",
1064
name, avahi_strerror(avahi_server_errno(mc.server)));
1067
case AVAHI_BROWSER_REMOVE:
1070
case AVAHI_BROWSER_ALL_FOR_NOW:
1071
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1073
fprintf_plus(stderr, "No Mandos server found, still searching...\n");
1079
/* Signal handler that stops main loop after SIGTERM */
1080
static void handle_sigterm(int sig){
1085
signal_received = sig;
1086
int old_errno = errno;
1087
/* set main loop to exit */
1088
if(mc.simple_poll != NULL){
1089
avahi_simple_poll_quit(mc.simple_poll);
1094
bool get_flags(const char *ifname, struct ifreq *ifr){
1097
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1099
perror_plus("socket");
1102
strcpy(ifr->ifr_name, ifname);
1103
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1106
perror_plus("ioctl SIOCGIFFLAGS");
1113
bool good_flags(const char *ifname, const struct ifreq *ifr){
1115
/* Reject the loopback device */
1116
if(ifr->ifr_flags & IFF_LOOPBACK){
1118
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n", ifname);
1122
/* Accept point-to-point devices only if connect_to is specified */
1123
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1125
fprintf_plus(stderr, "Accepting point-to-point interface \"%s\"\n", ifname);
1129
/* Otherwise, reject non-broadcast-capable devices */
1130
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1132
fprintf_plus(stderr, "Rejecting non-broadcast interface \"%s\"\n", ifname);
1136
/* Reject non-ARP interfaces (including dummy interfaces) */
1137
if(ifr->ifr_flags & IFF_NOARP){
1139
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n", ifname);
1144
/* Accept this device */
1146
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1152
* This function determines if a directory entry in /sys/class/net
1153
* corresponds to an acceptable network device.
1154
* (This function is passed to scandir(3) as a filter function.)
1156
int good_interface(const struct dirent *if_entry){
1157
if(if_entry->d_name[0] == '.'){
1162
if(not get_flags(if_entry->d_name, &ifr)){
1164
fprintf_plus(stderr, "Failed to get flags for interface \"%s\"\n",
1170
if(not good_flags(if_entry->d_name, &ifr)){
1177
* This function determines if a directory entry in /sys/class/net
1178
* corresponds to an acceptable network device which is up.
1179
* (This function is passed to scandir(3) as a filter function.)
1181
int up_interface(const struct dirent *if_entry){
1182
if(if_entry->d_name[0] == '.'){
1187
if(not get_flags(if_entry->d_name, &ifr)){
1189
fprintf_plus(stderr, "Failed to get flags for interface \"%s\"\n",
1195
/* Reject down interfaces */
1196
if(not (ifr.ifr_flags & IFF_UP)){
1198
fprintf_plus(stderr, "Rejecting down interface \"%s\"\n",
1204
/* Reject non-running interfaces */
1205
if(not (ifr.ifr_flags & IFF_RUNNING)){
1207
fprintf_plus(stderr, "Rejecting non-running interface \"%s\"\n",
1213
if(not good_flags(if_entry->d_name, &ifr)){
1219
int notdotentries(const struct dirent *direntry){
1220
/* Skip "." and ".." */
1221
if(direntry->d_name[0] == '.'
1222
and (direntry->d_name[1] == '\0'
1223
or (direntry->d_name[1] == '.'
1224
and direntry->d_name[2] == '\0'))){
1230
/* Is this directory entry a runnable program? */
1231
int runnable_hook(const struct dirent *direntry){
1236
if((direntry->d_name)[0] == '\0'){
1241
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1242
"abcdefghijklmnopqrstuvwxyz"
1245
if((direntry->d_name)[sret] != '\0'){
1246
/* Contains non-allowed characters */
1248
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1254
char *fullname = NULL;
1255
ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1257
perror_plus("asprintf");
1261
ret = stat(fullname, &st);
1264
perror_plus("Could not stat hook");
1268
if(not (S_ISREG(st.st_mode))){
1269
/* Not a regular file */
1271
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1276
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1277
/* Not executable */
1279
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1287
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval){
1289
struct timespec now;
1290
struct timespec waited_time;
1291
intmax_t block_time;
1294
if(mc.current_server == NULL){
1296
fprintf_plus(stderr, "Wait until first server is found. No timeout!\n");
1298
ret = avahi_simple_poll_iterate(s, -1);
1301
fprintf_plus(stderr, "Check current_server if we should run it,"
1304
/* the current time */
1305
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1307
perror_plus("clock_gettime");
1310
/* Calculating in ms how long time between now and server
1311
who we visted longest time ago. Now - last seen. */
1312
waited_time.tv_sec = (now.tv_sec
1313
- mc.current_server->last_seen.tv_sec);
1314
waited_time.tv_nsec = (now.tv_nsec
1315
- mc.current_server->last_seen.tv_nsec);
1316
/* total time is 10s/10,000ms.
1317
Converting to s from ms by dividing by 1,000,
1318
and ns to ms by dividing by 1,000,000. */
1319
block_time = ((retry_interval
1320
- ((intmax_t)waited_time.tv_sec * 1000))
1321
- ((intmax_t)waited_time.tv_nsec / 1000000));
1324
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n", block_time);
1327
if(block_time <= 0){
1328
ret = start_mandos_communication(mc.current_server->ip,
1329
mc.current_server->port,
1330
mc.current_server->if_index,
1331
mc.current_server->af);
1333
avahi_simple_poll_quit(mc.simple_poll);
1336
ret = clock_gettime(CLOCK_MONOTONIC,
1337
&mc.current_server->last_seen);
1339
perror_plus("clock_gettime");
1342
mc.current_server = mc.current_server->next;
1343
block_time = 0; /* Call avahi to find new Mandos
1344
servers, but don't block */
1347
ret = avahi_simple_poll_iterate(s, (int)block_time);
1350
if (ret > 0 or errno != EINTR){
1351
return (ret != 1) ? ret : 0;
1357
int main(int argc, char *argv[]){
1358
AvahiSServiceBrowser *sb = NULL;
1363
int exitcode = EXIT_SUCCESS;
1364
const char *interface = "";
1365
struct ifreq network;
1367
bool take_down_interface = false;
1370
char tempdir[] = "/tmp/mandosXXXXXX";
1371
bool tempdir_created = false;
1372
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1373
const char *seckey = PATHDIR "/" SECKEY;
1374
const char *pubkey = PATHDIR "/" PUBKEY;
1376
bool gnutls_initialized = false;
1377
bool gpgme_initialized = false;
1379
double retry_interval = 10; /* 10s between trying a server and
1380
retrying the same server again */
1382
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
1383
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
1388
/* Lower any group privileges we might have, just to be safe */
1392
perror_plus("setgid");
1395
/* Lower user privileges (temporarily) */
1399
perror_plus("seteuid");
1407
struct argp_option options[] = {
1408
{ .name = "debug", .key = 128,
1409
.doc = "Debug mode", .group = 3 },
1410
{ .name = "connect", .key = 'c',
1411
.arg = "ADDRESS:PORT",
1412
.doc = "Connect directly to a specific Mandos server",
1414
{ .name = "interface", .key = 'i',
1416
.doc = "Network interface that will be used to search for"
1419
{ .name = "seckey", .key = 's',
1421
.doc = "OpenPGP secret key file base name",
1423
{ .name = "pubkey", .key = 'p',
1425
.doc = "OpenPGP public key file base name",
1427
{ .name = "dh-bits", .key = 129,
1429
.doc = "Bit length of the prime number used in the"
1430
" Diffie-Hellman key exchange",
1432
{ .name = "priority", .key = 130,
1434
.doc = "GnuTLS priority string for the TLS handshake",
1436
{ .name = "delay", .key = 131,
1438
.doc = "Maximum delay to wait for interface startup",
1440
{ .name = "retry", .key = 132,
1442
.doc = "Retry interval used when denied by the mandos server",
1444
{ .name = "network-hook-dir", .key = 133,
1446
.doc = "Directory where network hooks are located",
1449
* These reproduce what we would get without ARGP_NO_HELP
1451
{ .name = "help", .key = '?',
1452
.doc = "Give this help list", .group = -1 },
1453
{ .name = "usage", .key = -3,
1454
.doc = "Give a short usage message", .group = -1 },
1455
{ .name = "version", .key = 'V',
1456
.doc = "Print program version", .group = -1 },
1460
error_t parse_opt(int key, char *arg,
1461
struct argp_state *state){
1464
case 128: /* --debug */
1467
case 'c': /* --connect */
1470
case 'i': /* --interface */
1473
case 's': /* --seckey */
1476
case 'p': /* --pubkey */
1479
case 129: /* --dh-bits */
1481
tmpmax = strtoimax(arg, &tmp, 10);
1482
if(errno != 0 or tmp == arg or *tmp != '\0'
1483
or tmpmax != (typeof(mc.dh_bits))tmpmax){
1484
argp_error(state, "Bad number of DH bits");
1486
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
1488
case 130: /* --priority */
1491
case 131: /* --delay */
1493
delay = strtof(arg, &tmp);
1494
if(errno != 0 or tmp == arg or *tmp != '\0'){
1495
argp_error(state, "Bad delay");
1497
case 132: /* --retry */
1499
retry_interval = strtod(arg, &tmp);
1500
if(errno != 0 or tmp == arg or *tmp != '\0'
1501
or (retry_interval * 1000) > INT_MAX
1502
or retry_interval < 0){
1503
argp_error(state, "Bad retry interval");
1506
case 133: /* --network-hook-dir */
1510
* These reproduce what we would get without ARGP_NO_HELP
1512
case '?': /* --help */
1513
argp_state_help(state, state->out_stream,
1514
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
1515
& ~(unsigned int)ARGP_HELP_EXIT_OK);
1516
case -3: /* --usage */
1517
argp_state_help(state, state->out_stream,
1518
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
1519
case 'V': /* --version */
1520
fprintf_plus(state->out_stream, "Mandos plugin mandos-client: ");
1521
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
1522
exit(argp_err_exit_status);
1525
return ARGP_ERR_UNKNOWN;
1530
struct argp argp = { .options = options, .parser = parse_opt,
1532
.doc = "Mandos client -- Get and decrypt"
1533
" passwords from a Mandos server" };
1534
ret = argp_parse(&argp, argc, argv,
1535
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
1542
perror_plus("argp_parse");
1543
exitcode = EX_OSERR;
1546
exitcode = EX_USAGE;
1552
/* Work around Debian bug #633582:
1553
<http://bugs.debian.org/633582> */
1556
/* Re-raise priviliges */
1560
perror_plus("seteuid");
1563
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
1564
int seckey_fd = open(seckey, O_RDONLY);
1565
if(seckey_fd == -1){
1566
perror_plus("open");
1568
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
1570
perror_plus("fstat");
1572
if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
1573
ret = fchown(seckey_fd, uid, gid);
1575
perror_plus("fchown");
1579
TEMP_FAILURE_RETRY(close(seckey_fd));
1583
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
1584
int pubkey_fd = open(pubkey, O_RDONLY);
1585
if(pubkey_fd == -1){
1586
perror_plus("open");
1588
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
1590
perror_plus("fstat");
1592
if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
1593
ret = fchown(pubkey_fd, uid, gid);
1595
perror_plus("fchown");
1599
TEMP_FAILURE_RETRY(close(pubkey_fd));
1603
/* Lower privileges */
1607
perror_plus("seteuid");
1611
/* Find network hooks and run them */
1613
struct dirent **direntries;
1614
struct dirent *direntry;
1615
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1618
perror_plus("scandir");
1620
int devnull = open("/dev/null", O_RDONLY);
1621
for(int i = 0; i < numhooks; i++){
1622
direntry = direntries[0];
1623
char *fullname = NULL;
1624
ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1626
perror_plus("asprintf");
1629
pid_t hook_pid = fork();
1632
dup2(devnull, STDIN_FILENO);
1634
dup2(STDERR_FILENO, STDOUT_FILENO);
1635
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1637
perror_plus("setenv");
1640
ret = setenv("DEVICE", interface, 1);
1642
perror_plus("setenv");
1645
ret = setenv("VERBOSE", debug ? "1" : "0", 1);
1647
perror_plus("setenv");
1650
ret = setenv("MODE", "start", 1);
1652
perror_plus("setenv");
1656
ret = asprintf(&delaystring, "%f", delay);
1658
perror_plus("asprintf");
1661
ret = setenv("DELAY", delaystring, 1);
1664
perror_plus("setenv");
1668
ret = execl(fullname, direntry->d_name, "start", NULL);
1669
perror_plus("execl");
1672
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1673
perror_plus("waitpid");
1677
if(WIFEXITED(status)){
1678
if(WEXITSTATUS(status) != 0){
1679
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1680
" with status %d\n", direntry->d_name,
1681
WEXITSTATUS(status));
1685
} else if(WIFSIGNALED(status)){
1686
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1687
" signal %d\n", direntry->d_name,
1692
fprintf_plus(stderr, "Warning: network hook \"%s\" crashed\n",
1708
avahi_set_log_function(empty_log);
1711
if(interface[0] == '\0'){
1712
struct dirent **direntries;
1713
/* First look for interfaces that are up */
1714
ret = scandir(sys_class_net, &direntries, up_interface,
1717
/* No up interfaces, look for any good interfaces */
1719
ret = scandir(sys_class_net, &direntries, good_interface,
1723
/* Pick the first interface returned */
1724
interface = strdup(direntries[0]->d_name);
1726
fprintf_plus(stderr, "Using interface \"%s\"\n", interface);
1728
if(interface == NULL){
1729
perror_plus("malloc");
1731
exitcode = EXIT_FAILURE;
1737
fprintf_plus(stderr, "Could not find a network interface\n");
1738
exitcode = EXIT_FAILURE;
1743
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1744
from the signal handler */
1745
/* Initialize the pseudo-RNG for Avahi */
1746
srand((unsigned int) time(NULL));
1747
mc.simple_poll = avahi_simple_poll_new();
1748
if(mc.simple_poll == NULL){
1749
fprintf_plus(stderr, "Avahi: Failed to create simple poll object.\n");
1750
exitcode = EX_UNAVAILABLE;
1754
sigemptyset(&sigterm_action.sa_mask);
1755
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1757
perror_plus("sigaddset");
1758
exitcode = EX_OSERR;
1761
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1763
perror_plus("sigaddset");
1764
exitcode = EX_OSERR;
1767
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1769
perror_plus("sigaddset");
1770
exitcode = EX_OSERR;
1773
/* Need to check if the handler is SIG_IGN before handling:
1774
| [[info:libc:Initial Signal Actions]] |
1775
| [[info:libc:Basic Signal Handling]] |
1777
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
1779
perror_plus("sigaction");
1782
if(old_sigterm_action.sa_handler != SIG_IGN){
1783
ret = sigaction(SIGINT, &sigterm_action, NULL);
1785
perror_plus("sigaction");
1786
exitcode = EX_OSERR;
1790
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
1792
perror_plus("sigaction");
1795
if(old_sigterm_action.sa_handler != SIG_IGN){
1796
ret = sigaction(SIGHUP, &sigterm_action, NULL);
1798
perror_plus("sigaction");
1799
exitcode = EX_OSERR;
1803
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
1805
perror_plus("sigaction");
1808
if(old_sigterm_action.sa_handler != SIG_IGN){
1809
ret = sigaction(SIGTERM, &sigterm_action, NULL);
1811
perror_plus("sigaction");
1812
exitcode = EX_OSERR;
1817
/* If the interface is down, bring it up */
1818
if(strcmp(interface, "none") != 0){
1819
if_index = (AvahiIfIndex) if_nametoindex(interface);
1821
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1822
exitcode = EX_UNAVAILABLE;
1830
/* Re-raise priviliges */
1834
perror_plus("seteuid");
1838
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1839
messages about the network interface to mess up the prompt */
1840
ret = klogctl(8, NULL, 5);
1841
bool restore_loglevel = true;
1843
restore_loglevel = false;
1844
perror_plus("klogctl");
1846
#endif /* __linux__ */
1848
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1850
perror_plus("socket");
1851
exitcode = EX_OSERR;
1853
if(restore_loglevel){
1854
ret = klogctl(7, NULL, 0);
1856
perror_plus("klogctl");
1859
#endif /* __linux__ */
1860
/* Lower privileges */
1864
perror_plus("seteuid");
1868
strcpy(network.ifr_name, interface);
1869
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1871
perror_plus("ioctl SIOCGIFFLAGS");
1873
if(restore_loglevel){
1874
ret = klogctl(7, NULL, 0);
1876
perror_plus("klogctl");
1879
#endif /* __linux__ */
1880
exitcode = EX_OSERR;
1881
/* Lower privileges */
1885
perror_plus("seteuid");
1889
if((network.ifr_flags & IFF_UP) == 0){
1890
network.ifr_flags |= IFF_UP;
1891
take_down_interface = true;
1892
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1894
take_down_interface = false;
1895
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1896
exitcode = EX_OSERR;
1898
if(restore_loglevel){
1899
ret = klogctl(7, NULL, 0);
1901
perror_plus("klogctl");
1904
#endif /* __linux__ */
1905
/* Lower privileges */
1909
perror_plus("seteuid");
1914
/* Sleep checking until interface is running.
1915
Check every 0.25s, up to total time of delay */
1916
for(int i=0; i < delay * 4; i++){
1917
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1919
perror_plus("ioctl SIOCGIFFLAGS");
1920
} else if(network.ifr_flags & IFF_RUNNING){
1923
struct timespec sleeptime = { .tv_nsec = 250000000 };
1924
ret = nanosleep(&sleeptime, NULL);
1925
if(ret == -1 and errno != EINTR){
1926
perror_plus("nanosleep");
1929
if(not take_down_interface){
1930
/* We won't need the socket anymore */
1931
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1933
perror_plus("close");
1937
if(restore_loglevel){
1938
/* Restores kernel loglevel to default */
1939
ret = klogctl(7, NULL, 0);
1941
perror_plus("klogctl");
1944
#endif /* __linux__ */
1945
/* Lower privileges */
1947
if(take_down_interface){
1948
/* Lower privileges */
1951
perror_plus("seteuid");
1954
/* Lower privileges permanently */
1957
perror_plus("setuid");
1966
ret = init_gnutls_global(pubkey, seckey);
1968
fprintf_plus(stderr, "init_gnutls_global failed\n");
1969
exitcode = EX_UNAVAILABLE;
1972
gnutls_initialized = true;
1979
if(mkdtemp(tempdir) == NULL){
1980
perror_plus("mkdtemp");
1983
tempdir_created = true;
1989
if(not init_gpgme(pubkey, seckey, tempdir)){
1990
fprintf_plus(stderr, "init_gpgme failed\n");
1991
exitcode = EX_UNAVAILABLE;
1994
gpgme_initialized = true;
2001
if(connect_to != NULL){
2002
/* Connect directly, do not use Zeroconf */
2003
/* (Mainly meant for debugging) */
2004
char *address = strrchr(connect_to, ':');
2005
if(address == NULL){
2006
fprintf_plus(stderr, "No colon in address\n");
2007
exitcode = EX_USAGE;
2017
tmpmax = strtoimax(address+1, &tmp, 10);
2018
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2019
or tmpmax != (uint16_t)tmpmax){
2020
fprintf_plus(stderr, "Bad port number\n");
2021
exitcode = EX_USAGE;
2029
port = (uint16_t)tmpmax;
2031
/* Colon in address indicates IPv6 */
2033
if(strchr(connect_to, ':') != NULL){
2035
/* Accept [] around IPv6 address - see RFC 5952 */
2036
if(connect_to[0] == '[' and address[-1] == ']')
2044
address = connect_to;
2050
while(not quit_now){
2051
ret = start_mandos_communication(address, port, if_index, af);
2052
if(quit_now or ret == 0){
2056
fprintf_plus(stderr, "Retrying in %d seconds\n", (int)retry_interval);
2058
sleep((int)retry_interval);
2062
exitcode = EXIT_SUCCESS;
2073
AvahiServerConfig config;
2074
/* Do not publish any local Zeroconf records */
2075
avahi_server_config_init(&config);
2076
config.publish_hinfo = 0;
2077
config.publish_addresses = 0;
2078
config.publish_workstation = 0;
2079
config.publish_domain = 0;
2081
/* Allocate a new server */
2082
mc.server = avahi_server_new(avahi_simple_poll_get
2083
(mc.simple_poll), &config, NULL,
2086
/* Free the Avahi configuration data */
2087
avahi_server_config_free(&config);
2090
/* Check if creating the Avahi server object succeeded */
2091
if(mc.server == NULL){
2092
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2093
avahi_strerror(error));
2094
exitcode = EX_UNAVAILABLE;
2102
/* Create the Avahi service browser */
2103
sb = avahi_s_service_browser_new(mc.server, if_index,
2104
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2105
NULL, 0, browse_callback, NULL);
2107
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2108
avahi_strerror(avahi_server_errno(mc.server)));
2109
exitcode = EX_UNAVAILABLE;
2117
/* Run the main loop */
2120
fprintf_plus(stderr, "Starting Avahi loop search\n");
2123
ret = avahi_loop_with_timeout(mc.simple_poll,
2124
(int)(retry_interval * 1000));
2126
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2127
(ret == 0) ? "successfully" : "with error");
2133
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2136
/* Cleanup things */
2138
avahi_s_service_browser_free(sb);
2140
if(mc.server != NULL)
2141
avahi_server_free(mc.server);
2143
if(mc.simple_poll != NULL)
2144
avahi_simple_poll_free(mc.simple_poll);
2146
if(gnutls_initialized){
2147
gnutls_certificate_free_credentials(mc.cred);
2148
gnutls_global_deinit();
2149
gnutls_dh_params_deinit(mc.dh_params);
2152
if(gpgme_initialized){
2153
gpgme_release(mc.ctx);
2156
/* Cleans up the circular linked list of Mandos servers the client
2158
if(mc.current_server != NULL){
2159
mc.current_server->prev->next = NULL;
2160
while(mc.current_server != NULL){
2161
server *next = mc.current_server->next;
2162
free(mc.current_server);
2163
mc.current_server = next;
2167
/* XXX run network hooks "stop" here */
2169
/* Take down the network interface */
2170
if(take_down_interface){
2171
/* Re-raise priviliges */
2175
perror_plus("seteuid");
2178
ret = ioctl(sd, SIOCGIFFLAGS, &network);
2180
perror_plus("ioctl SIOCGIFFLAGS");
2181
} else if(network.ifr_flags & IFF_UP){
2182
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2183
ret = ioctl(sd, SIOCSIFFLAGS, &network);
2185
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2188
ret = (int)TEMP_FAILURE_RETRY(close(sd));
2190
perror_plus("close");
2192
/* Lower privileges permanently */
2196
perror_plus("setuid");
2201
/* Removes the GPGME temp directory and all files inside */
2202
if(tempdir_created){
2203
struct dirent **direntries = NULL;
2204
struct dirent *direntry = NULL;
2205
int numentries = scandir(tempdir, &direntries, notdotentries,
2207
if (numentries > 0){
2208
for(int i = 0; i < numentries; i++){
2209
direntry = direntries[i];
2210
char *fullname = NULL;
2211
ret = asprintf(&fullname, "%s/%s", tempdir,
2214
perror_plus("asprintf");
2217
ret = remove(fullname);
2219
fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname, strerror(errno));
2225
/* need to clean even if 0 because man page doesn't specify */
2227
if (numentries == -1){
2228
perror_plus("scandir");
2230
ret = rmdir(tempdir);
2231
if(ret == -1 and errno != ENOENT){
2232
perror_plus("rmdir");
2237
sigemptyset(&old_sigterm_action.sa_mask);
2238
old_sigterm_action.sa_handler = SIG_DFL;
2239
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
2240
&old_sigterm_action,
2243
perror_plus("sigaction");
2246
ret = raise(signal_received);
2247
} while(ret != 0 and errno == EINTR);
2249
perror_plus("raise");
2252
TEMP_FAILURE_RETRY(pause());