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-2013 Teddy Hogeborn
13
* Copyright © 2008-2013 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, intptr_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 <errno.h> /* perror(), errno,
65
program_invocation_short_name */
66
#include <time.h> /* nanosleep(), time(), sleep() */
67
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
68
SIOCSIFFLAGS, if_indextoname(),
69
if_nametoindex(), IF_NAMESIZE */
70
#include <netinet/in.h> /* IN6_IS_ADDR_LINKLOCAL,
71
INET_ADDRSTRLEN, INET6_ADDRSTRLEN
73
#include <unistd.h> /* close(), SEEK_SET, off_t, write(),
74
getuid(), getgid(), seteuid(),
75
setgid(), pause(), _exit() */
76
#include <arpa/inet.h> /* inet_pton(), htons, inet_ntop() */
77
#include <iso646.h> /* not, or, and */
78
#include <argp.h> /* struct argp_option, error_t, struct
79
argp_state, struct argp,
80
argp_parse(), ARGP_KEY_ARG,
81
ARGP_KEY_END, ARGP_ERR_UNKNOWN */
82
#include <signal.h> /* sigemptyset(), sigaddset(),
83
sigaction(), SIGTERM, sig_atomic_t,
85
#include <sysexits.h> /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
86
EX_NOHOST, EX_IOERR, EX_PROTOCOL */
87
#include <sys/wait.h> /* waitpid(), WIFEXITED(),
88
WEXITSTATUS(), WTERMSIG() */
89
#include <grp.h> /* setgroups() */
90
#include <argz.h> /* argz_add_sep(), argz_next(),
91
argz_delete(), argz_append(),
92
argz_stringify(), argz_add(),
96
#include <sys/klog.h> /* klogctl() */
97
#endif /* __linux__ */
100
/* All Avahi types, constants and functions
103
#include <avahi-core/core.h>
104
#include <avahi-core/lookup.h>
105
#include <avahi-core/log.h>
106
#include <avahi-common/simple-watch.h>
107
#include <avahi-common/malloc.h>
108
#include <avahi-common/error.h>
111
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
114
init_gnutls_session(),
116
#include <gnutls/openpgp.h>
117
/* gnutls_certificate_set_openpgp_key_file(),
118
GNUTLS_OPENPGP_FMT_BASE64 */
121
#include <gpgme.h> /* All GPGME types, constants and
124
GPGME_PROTOCOL_OpenPGP,
127
#define BUFFER_SIZE 256
129
#define PATHDIR "/conf/conf.d/mandos"
130
#define SECKEY "seckey.txt"
131
#define PUBKEY "pubkey.txt"
132
#define HOOKDIR "/lib/mandos/network-hooks.d"
135
static const char mandos_protocol_version[] = "1";
136
const char *argp_program_version = "mandos-client " VERSION;
137
const char *argp_program_bug_address = "<mandos@recompile.se>";
138
static const char sys_class_net[] = "/sys/class/net";
139
char *connect_to = NULL;
140
const char *hookdir = HOOKDIR;
144
/* Doubly linked list that need to be circularly linked when used */
145
typedef struct server{
148
AvahiIfIndex if_index;
150
struct timespec last_seen;
155
/* Used for passing in values through the Avahi callback functions */
158
gnutls_certificate_credentials_t cred;
159
unsigned int dh_bits;
160
gnutls_dh_params_t dh_params;
161
const char *priority;
163
server *current_server;
165
size_t interfaces_size;
168
/* global so signal handler can reach it*/
169
AvahiSimplePoll *simple_poll;
171
sig_atomic_t quit_now = 0;
172
int signal_received = 0;
174
/* Function to use when printing errors */
175
void perror_plus(const char *print_text){
177
fprintf(stderr, "Mandos plugin %s: ",
178
program_invocation_short_name);
183
__attribute__((format (gnu_printf, 2, 3)))
184
int fprintf_plus(FILE *stream, const char *format, ...){
186
va_start (ap, format);
188
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
189
program_invocation_short_name));
190
return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
194
* Make additional room in "buffer" for at least BUFFER_SIZE more
195
* bytes. "buffer_capacity" is how much is currently allocated,
196
* "buffer_length" is how much is already used.
198
size_t incbuffer(char **buffer, size_t buffer_length,
199
size_t buffer_capacity){
200
if(buffer_length + BUFFER_SIZE > buffer_capacity){
201
char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
203
int old_errno = errno;
210
buffer_capacity += BUFFER_SIZE;
212
return buffer_capacity;
215
/* Add server to set of servers to retry periodically */
216
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
217
int af, server **current_server){
219
server *new_server = malloc(sizeof(server));
220
if(new_server == NULL){
221
perror_plus("malloc");
224
*new_server = (server){ .ip = strdup(ip),
226
.if_index = if_index,
228
if(new_server->ip == NULL){
229
perror_plus("strdup");
232
/* Special case of first server */
233
if(*current_server == NULL){
234
new_server->next = new_server;
235
new_server->prev = new_server;
236
*current_server = new_server;
237
/* Place the new server last in the list */
239
new_server->next = *current_server;
240
new_server->prev = (*current_server)->prev;
241
new_server->prev->next = new_server;
242
(*current_server)->prev = new_server;
244
ret = clock_gettime(CLOCK_MONOTONIC, &(*current_server)->last_seen);
246
perror_plus("clock_gettime");
255
static bool init_gpgme(const char *seckey, const char *pubkey,
256
const char *tempdir, mandos_context *mc){
258
gpgme_engine_info_t engine_info;
261
* Helper function to insert pub and seckey to the engine keyring.
263
bool import_key(const char *filename){
266
gpgme_data_t pgp_data;
268
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
274
rc = gpgme_data_new_from_fd(&pgp_data, fd);
275
if(rc != GPG_ERR_NO_ERROR){
276
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
277
gpgme_strsource(rc), gpgme_strerror(rc));
281
rc = gpgme_op_import(mc->ctx, pgp_data);
282
if(rc != GPG_ERR_NO_ERROR){
283
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
284
gpgme_strsource(rc), gpgme_strerror(rc));
288
ret = (int)TEMP_FAILURE_RETRY(close(fd));
290
perror_plus("close");
292
gpgme_data_release(pgp_data);
297
fprintf_plus(stderr, "Initializing GPGME\n");
301
gpgme_check_version(NULL);
302
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
303
if(rc != GPG_ERR_NO_ERROR){
304
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
305
gpgme_strsource(rc), gpgme_strerror(rc));
309
/* Set GPGME home directory for the OpenPGP engine only */
310
rc = gpgme_get_engine_info(&engine_info);
311
if(rc != GPG_ERR_NO_ERROR){
312
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
313
gpgme_strsource(rc), gpgme_strerror(rc));
316
while(engine_info != NULL){
317
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
318
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
319
engine_info->file_name, tempdir);
322
engine_info = engine_info->next;
324
if(engine_info == NULL){
325
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
330
/* Create new GPGME "context" */
331
rc = gpgme_new(&(mc->ctx));
332
if(rc != GPG_ERR_NO_ERROR){
333
fprintf_plus(stderr, "Mandos plugin mandos-client: "
334
"bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
339
if(not import_key(pubkey) or not import_key(seckey)){
347
* Decrypt OpenPGP data.
348
* Returns -1 on error
350
static ssize_t pgp_packet_decrypt(const char *cryptotext,
354
gpgme_data_t dh_crypto, dh_plain;
357
size_t plaintext_capacity = 0;
358
ssize_t plaintext_length = 0;
361
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
364
/* Create new GPGME data buffer from memory cryptotext */
365
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
367
if(rc != GPG_ERR_NO_ERROR){
368
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
369
gpgme_strsource(rc), gpgme_strerror(rc));
373
/* Create new empty GPGME data buffer for the plaintext */
374
rc = gpgme_data_new(&dh_plain);
375
if(rc != GPG_ERR_NO_ERROR){
376
fprintf_plus(stderr, "Mandos plugin mandos-client: "
377
"bad gpgme_data_new: %s: %s\n",
378
gpgme_strsource(rc), gpgme_strerror(rc));
379
gpgme_data_release(dh_crypto);
383
/* Decrypt data from the cryptotext data buffer to the plaintext
385
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
386
if(rc != GPG_ERR_NO_ERROR){
387
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
388
gpgme_strsource(rc), gpgme_strerror(rc));
389
plaintext_length = -1;
391
gpgme_decrypt_result_t result;
392
result = gpgme_op_decrypt_result(mc->ctx);
394
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
396
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
397
result->unsupported_algorithm);
398
fprintf_plus(stderr, "Wrong key usage: %u\n",
399
result->wrong_key_usage);
400
if(result->file_name != NULL){
401
fprintf_plus(stderr, "File name: %s\n", result->file_name);
403
gpgme_recipient_t recipient;
404
recipient = result->recipients;
405
while(recipient != NULL){
406
fprintf_plus(stderr, "Public key algorithm: %s\n",
407
gpgme_pubkey_algo_name
408
(recipient->pubkey_algo));
409
fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
410
fprintf_plus(stderr, "Secret key available: %s\n",
411
recipient->status == GPG_ERR_NO_SECKEY
413
recipient = recipient->next;
421
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
424
/* Seek back to the beginning of the GPGME plaintext data buffer */
425
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
426
perror_plus("gpgme_data_seek");
427
plaintext_length = -1;
433
plaintext_capacity = incbuffer(plaintext,
434
(size_t)plaintext_length,
436
if(plaintext_capacity == 0){
437
perror_plus("incbuffer");
438
plaintext_length = -1;
442
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
444
/* Print the data, if any */
450
perror_plus("gpgme_data_read");
451
plaintext_length = -1;
454
plaintext_length += ret;
458
fprintf_plus(stderr, "Decrypted password is: ");
459
for(ssize_t i = 0; i < plaintext_length; i++){
460
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
462
fprintf(stderr, "\n");
467
/* Delete the GPGME cryptotext data buffer */
468
gpgme_data_release(dh_crypto);
470
/* Delete the GPGME plaintext data buffer */
471
gpgme_data_release(dh_plain);
472
return plaintext_length;
475
static const char * safer_gnutls_strerror(int value){
476
const char *ret = gnutls_strerror(value);
482
/* GnuTLS log function callback */
483
static void debuggnutls(__attribute__((unused)) int level,
485
fprintf_plus(stderr, "GnuTLS: %s", string);
488
static int init_gnutls_global(const char *pubkeyfilename,
489
const char *seckeyfilename,
494
fprintf_plus(stderr, "Initializing GnuTLS\n");
497
ret = gnutls_global_init();
498
if(ret != GNUTLS_E_SUCCESS){
499
fprintf_plus(stderr, "GnuTLS global_init: %s\n",
500
safer_gnutls_strerror(ret));
505
/* "Use a log level over 10 to enable all debugging options."
508
gnutls_global_set_log_level(11);
509
gnutls_global_set_log_function(debuggnutls);
512
/* OpenPGP credentials */
513
ret = gnutls_certificate_allocate_credentials(&mc->cred);
514
if(ret != GNUTLS_E_SUCCESS){
515
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
516
safer_gnutls_strerror(ret));
517
gnutls_global_deinit();
522
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
523
" secret key %s as GnuTLS credentials\n",
528
ret = gnutls_certificate_set_openpgp_key_file
529
(mc->cred, pubkeyfilename, seckeyfilename,
530
GNUTLS_OPENPGP_FMT_BASE64);
531
if(ret != GNUTLS_E_SUCCESS){
533
"Error[%d] while reading the OpenPGP key pair ('%s',"
534
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
535
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
536
safer_gnutls_strerror(ret));
540
/* GnuTLS server initialization */
541
ret = gnutls_dh_params_init(&mc->dh_params);
542
if(ret != GNUTLS_E_SUCCESS){
543
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
544
" initialization: %s\n",
545
safer_gnutls_strerror(ret));
548
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
549
if(ret != GNUTLS_E_SUCCESS){
550
fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
551
safer_gnutls_strerror(ret));
555
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
561
gnutls_certificate_free_credentials(mc->cred);
562
gnutls_global_deinit();
563
gnutls_dh_params_deinit(mc->dh_params);
567
static int init_gnutls_session(gnutls_session_t *session,
570
/* GnuTLS session creation */
572
ret = gnutls_init(session, GNUTLS_SERVER);
576
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
577
if(ret != GNUTLS_E_SUCCESS){
579
"Error in GnuTLS session initialization: %s\n",
580
safer_gnutls_strerror(ret));
586
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
588
gnutls_deinit(*session);
591
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
592
if(ret != GNUTLS_E_SUCCESS){
593
fprintf_plus(stderr, "Syntax error at: %s\n", err);
594
fprintf_plus(stderr, "GnuTLS error: %s\n",
595
safer_gnutls_strerror(ret));
596
gnutls_deinit(*session);
602
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
605
gnutls_deinit(*session);
608
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
609
if(ret != GNUTLS_E_SUCCESS){
610
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
611
safer_gnutls_strerror(ret));
612
gnutls_deinit(*session);
616
/* ignore client certificate if any. */
617
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
619
gnutls_dh_set_prime_bits(*session, mc->dh_bits);
624
/* Avahi log function callback */
625
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
626
__attribute__((unused)) const char *txt){}
628
/* Called when a Mandos server is found */
629
static int start_mandos_communication(const char *ip, in_port_t port,
630
AvahiIfIndex if_index,
631
int af, mandos_context *mc){
632
int ret, tcp_sd = -1;
635
struct sockaddr_in in;
636
struct sockaddr_in6 in6;
639
char *decrypted_buffer = NULL;
640
size_t buffer_length = 0;
641
size_t buffer_capacity = 0;
644
gnutls_session_t session;
645
int pf; /* Protocol family */
662
fprintf_plus(stderr, "Bad address family: %d\n", af);
667
/* If the interface is specified and we have a list of interfaces */
668
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
669
/* Check if the interface is one of the interfaces we are using */
672
char *interface = NULL;
673
while((interface=argz_next(mc->interfaces, mc->interfaces_size,
675
if(if_nametoindex(interface) == (unsigned int)if_index){
682
/* This interface does not match any in the list, so we don't
683
connect to the server */
685
char interface[IF_NAMESIZE];
686
if(if_indextoname((unsigned int)if_index, interface) == NULL){
687
perror_plus("if_indextoname");
689
fprintf_plus(stderr, "Skipping server on non-used interface"
691
if_indextoname((unsigned int)if_index,
699
ret = init_gnutls_session(&session, mc);
705
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
706
PRIuMAX "\n", ip, (uintmax_t)port);
709
tcp_sd = socket(pf, SOCK_STREAM, 0);
712
perror_plus("socket");
722
memset(&to, 0, sizeof(to));
724
to.in6.sin6_family = (sa_family_t)af;
725
ret = inet_pton(af, ip, &to.in6.sin6_addr);
727
to.in.sin_family = (sa_family_t)af;
728
ret = inet_pton(af, ip, &to.in.sin_addr);
732
perror_plus("inet_pton");
738
fprintf_plus(stderr, "Bad address: %s\n", ip);
743
to.in6.sin6_port = htons(port);
745
#pragma GCC diagnostic push
746
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
748
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
749
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower */
751
#pragma GCC diagnostic pop
753
if(if_index == AVAHI_IF_UNSPEC){
754
fprintf_plus(stderr, "An IPv6 link-local address is"
755
" incomplete without a network interface\n");
759
/* Set the network interface number as scope */
760
to.in6.sin6_scope_id = (uint32_t)if_index;
763
to.in.sin_port = htons(port);
772
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
773
char interface[IF_NAMESIZE];
774
if(if_indextoname((unsigned int)if_index, interface) == NULL){
775
perror_plus("if_indextoname");
777
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
778
"\n", ip, interface, (uintmax_t)port);
781
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
782
ip, (uintmax_t)port);
784
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
785
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
788
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
791
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
795
perror_plus("inet_ntop");
797
if(strcmp(addrstr, ip) != 0){
798
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
809
ret = connect(tcp_sd, &to.in6, sizeof(to));
811
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
814
if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
816
perror_plus("connect");
827
const char *out = mandos_protocol_version;
830
size_t out_size = strlen(out);
831
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
832
out_size - written));
835
perror_plus("write");
839
written += (size_t)ret;
840
if(written < out_size){
843
if(out == mandos_protocol_version){
858
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
866
/* This casting via intptr_t is to eliminate warning about casting
867
an int to a pointer type. This is exactly how the GnuTLS Guile
868
function "set-session-transport-fd!" does it. */
869
gnutls_transport_set_ptr(session,
870
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
878
ret = gnutls_handshake(session);
883
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
885
if(ret != GNUTLS_E_SUCCESS){
887
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
894
/* Read OpenPGP packet that contains the wanted password */
897
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
908
buffer_capacity = incbuffer(&buffer, buffer_length,
910
if(buffer_capacity == 0){
912
perror_plus("incbuffer");
922
sret = gnutls_record_recv(session, buffer+buffer_length,
929
case GNUTLS_E_INTERRUPTED:
932
case GNUTLS_E_REHANDSHAKE:
934
ret = gnutls_handshake(session);
940
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
942
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
950
fprintf_plus(stderr, "Unknown error while reading data from"
951
" encrypted session with Mandos server\n");
952
gnutls_bye(session, GNUTLS_SHUT_RDWR);
957
buffer_length += (size_t) sret;
962
fprintf_plus(stderr, "Closing TLS session\n");
971
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
976
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
978
if(buffer_length > 0){
979
ssize_t decrypted_buffer_size;
980
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
981
&decrypted_buffer, mc);
982
if(decrypted_buffer_size >= 0){
985
while(written < (size_t) decrypted_buffer_size){
991
ret = (int)fwrite(decrypted_buffer + written, 1,
992
(size_t)decrypted_buffer_size - written,
994
if(ret == 0 and ferror(stdout)){
997
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1003
written += (size_t)ret;
1009
/* Shutdown procedure */
1014
free(decrypted_buffer);
1017
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
1023
perror_plus("close");
1025
gnutls_deinit(session);
1035
static void resolve_callback(AvahiSServiceResolver *r,
1036
AvahiIfIndex interface,
1037
AvahiProtocol proto,
1038
AvahiResolverEvent event,
1042
const char *host_name,
1043
const AvahiAddress *address,
1045
AVAHI_GCC_UNUSED AvahiStringList *txt,
1046
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1053
/* Called whenever a service has been resolved successfully or
1062
case AVAHI_RESOLVER_FAILURE:
1063
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1064
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1066
avahi_strerror(avahi_server_errno
1067
(((mandos_context*)mc)->server)));
1070
case AVAHI_RESOLVER_FOUND:
1072
char ip[AVAHI_ADDRESS_STR_MAX];
1073
avahi_address_snprint(ip, sizeof(ip), address);
1075
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1076
PRIdMAX ") on port %" PRIu16 "\n", name,
1077
host_name, ip, (intmax_t)interface, port);
1079
int ret = start_mandos_communication(ip, (in_port_t)port,
1081
avahi_proto_to_af(proto),
1084
avahi_simple_poll_quit(simple_poll);
1086
if(not add_server(ip, (in_port_t)port, interface,
1087
avahi_proto_to_af(proto),
1088
&((mandos_context*)mc)->current_server)){
1089
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1095
avahi_s_service_resolver_free(r);
1098
static void browse_callback(AvahiSServiceBrowser *b,
1099
AvahiIfIndex interface,
1100
AvahiProtocol protocol,
1101
AvahiBrowserEvent event,
1105
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1112
/* Called whenever a new services becomes available on the LAN or
1113
is removed from the LAN */
1121
case AVAHI_BROWSER_FAILURE:
1123
fprintf_plus(stderr, "(Avahi browser) %s\n",
1124
avahi_strerror(avahi_server_errno
1125
(((mandos_context*)mc)->server)));
1126
avahi_simple_poll_quit(simple_poll);
1129
case AVAHI_BROWSER_NEW:
1130
/* We ignore the returned Avahi resolver object. In the callback
1131
function we free it. If the Avahi server is terminated before
1132
the callback function is called the Avahi server will free the
1135
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1136
interface, protocol, name, type,
1137
domain, protocol, 0,
1138
resolve_callback, mc) == NULL)
1139
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1141
avahi_strerror(avahi_server_errno
1142
(((mandos_context*)mc)->server)));
1145
case AVAHI_BROWSER_REMOVE:
1148
case AVAHI_BROWSER_ALL_FOR_NOW:
1149
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1151
fprintf_plus(stderr, "No Mandos server found, still"
1158
/* Signal handler that stops main loop after SIGTERM */
1159
static void handle_sigterm(int sig){
1164
signal_received = sig;
1165
int old_errno = errno;
1166
/* set main loop to exit */
1167
if(simple_poll != NULL){
1168
avahi_simple_poll_quit(simple_poll);
1173
bool get_flags(const char *ifname, struct ifreq *ifr){
1177
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1180
perror_plus("socket");
1184
strcpy(ifr->ifr_name, ifname);
1185
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1189
perror_plus("ioctl SIOCGIFFLAGS");
1197
bool good_flags(const char *ifname, const struct ifreq *ifr){
1199
/* Reject the loopback device */
1200
if(ifr->ifr_flags & IFF_LOOPBACK){
1202
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1207
/* Accept point-to-point devices only if connect_to is specified */
1208
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1210
fprintf_plus(stderr, "Accepting point-to-point interface"
1211
" \"%s\"\n", ifname);
1215
/* Otherwise, reject non-broadcast-capable devices */
1216
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1218
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1219
" \"%s\"\n", ifname);
1223
/* Reject non-ARP interfaces (including dummy interfaces) */
1224
if(ifr->ifr_flags & IFF_NOARP){
1226
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1232
/* Accept this device */
1234
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1240
* This function determines if a directory entry in /sys/class/net
1241
* corresponds to an acceptable network device.
1242
* (This function is passed to scandir(3) as a filter function.)
1244
int good_interface(const struct dirent *if_entry){
1245
if(if_entry->d_name[0] == '.'){
1250
if(not get_flags(if_entry->d_name, &ifr)){
1252
fprintf_plus(stderr, "Failed to get flags for interface "
1253
"\"%s\"\n", if_entry->d_name);
1258
if(not good_flags(if_entry->d_name, &ifr)){
1265
* This function determines if a network interface is up.
1267
bool interface_is_up(const char *interface){
1269
if(not get_flags(interface, &ifr)){
1271
fprintf_plus(stderr, "Failed to get flags for interface "
1272
"\"%s\"\n", interface);
1277
return (bool)(ifr.ifr_flags & IFF_UP);
1281
* This function determines if a network interface is running
1283
bool interface_is_running(const char *interface){
1285
if(not get_flags(interface, &ifr)){
1287
fprintf_plus(stderr, "Failed to get flags for interface "
1288
"\"%s\"\n", interface);
1293
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1296
int notdotentries(const struct dirent *direntry){
1297
/* Skip "." and ".." */
1298
if(direntry->d_name[0] == '.'
1299
and (direntry->d_name[1] == '\0'
1300
or (direntry->d_name[1] == '.'
1301
and direntry->d_name[2] == '\0'))){
1307
/* Is this directory entry a runnable program? */
1308
int runnable_hook(const struct dirent *direntry){
1313
if((direntry->d_name)[0] == '\0'){
1318
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1319
"abcdefghijklmnopqrstuvwxyz"
1322
if((direntry->d_name)[sret] != '\0'){
1323
/* Contains non-allowed characters */
1325
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1331
char *fullname = NULL;
1332
ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1334
perror_plus("asprintf");
1338
ret = stat(fullname, &st);
1341
perror_plus("Could not stat hook");
1345
if(not (S_ISREG(st.st_mode))){
1346
/* Not a regular file */
1348
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1353
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1354
/* Not executable */
1356
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1362
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1368
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1369
mandos_context *mc){
1371
struct timespec now;
1372
struct timespec waited_time;
1373
intmax_t block_time;
1376
if(mc->current_server == NULL){
1378
fprintf_plus(stderr, "Wait until first server is found."
1381
ret = avahi_simple_poll_iterate(s, -1);
1384
fprintf_plus(stderr, "Check current_server if we should run"
1387
/* the current time */
1388
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1390
perror_plus("clock_gettime");
1393
/* Calculating in ms how long time between now and server
1394
who we visted longest time ago. Now - last seen. */
1395
waited_time.tv_sec = (now.tv_sec
1396
- mc->current_server->last_seen.tv_sec);
1397
waited_time.tv_nsec = (now.tv_nsec
1398
- mc->current_server->last_seen.tv_nsec);
1399
/* total time is 10s/10,000ms.
1400
Converting to s from ms by dividing by 1,000,
1401
and ns to ms by dividing by 1,000,000. */
1402
block_time = ((retry_interval
1403
- ((intmax_t)waited_time.tv_sec * 1000))
1404
- ((intmax_t)waited_time.tv_nsec / 1000000));
1407
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1411
if(block_time <= 0){
1412
ret = start_mandos_communication(mc->current_server->ip,
1413
mc->current_server->port,
1414
mc->current_server->if_index,
1415
mc->current_server->af, mc);
1417
avahi_simple_poll_quit(s);
1420
ret = clock_gettime(CLOCK_MONOTONIC,
1421
&mc->current_server->last_seen);
1423
perror_plus("clock_gettime");
1426
mc->current_server = mc->current_server->next;
1427
block_time = 0; /* Call avahi to find new Mandos
1428
servers, but don't block */
1431
ret = avahi_simple_poll_iterate(s, (int)block_time);
1434
if (ret > 0 or errno != EINTR){
1435
return (ret != 1) ? ret : 0;
1441
/* Set effective uid to 0, return errno */
1442
error_t raise_privileges(void){
1443
error_t old_errno = errno;
1444
error_t ret_errno = 0;
1445
if(seteuid(0) == -1){
1447
perror_plus("seteuid");
1453
/* Set effective and real user ID to 0. Return errno. */
1454
error_t raise_privileges_permanently(void){
1455
error_t old_errno = errno;
1456
error_t ret_errno = raise_privileges();
1461
if(setuid(0) == -1){
1463
perror_plus("seteuid");
1469
/* Set effective user ID to unprivileged saved user ID */
1470
error_t lower_privileges(void){
1471
error_t old_errno = errno;
1472
error_t ret_errno = 0;
1473
if(seteuid(uid) == -1){
1475
perror_plus("seteuid");
1481
/* Lower privileges permanently */
1482
error_t lower_privileges_permanently(void){
1483
error_t old_errno = errno;
1484
error_t ret_errno = 0;
1485
if(setuid(uid) == -1){
1487
perror_plus("setuid");
1493
bool run_network_hooks(const char *mode, const char *interface,
1495
struct dirent **direntries;
1496
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1499
if(errno == ENOENT){
1501
fprintf_plus(stderr, "Network hook directory \"%s\" not"
1502
" found\n", hookdir);
1505
perror_plus("scandir");
1508
struct dirent *direntry;
1510
int devnull = open("/dev/null", O_RDONLY);
1511
for(int i = 0; i < numhooks; i++){
1512
direntry = direntries[i];
1513
char *fullname = NULL;
1514
ret = asprintf(&fullname, "%s/%s", hookdir, direntry->d_name);
1516
perror_plus("asprintf");
1520
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1523
pid_t hook_pid = fork();
1526
/* Raise privileges */
1527
raise_privileges_permanently();
1532
perror_plus("setgid");
1534
/* Reset supplementary groups */
1536
ret = setgroups(0, NULL);
1538
perror_plus("setgroups");
1540
dup2(devnull, STDIN_FILENO);
1542
dup2(STDERR_FILENO, STDOUT_FILENO);
1543
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1545
perror_plus("setenv");
1548
ret = setenv("DEVICE", interface, 1);
1550
perror_plus("setenv");
1553
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1555
perror_plus("setenv");
1558
ret = setenv("MODE", mode, 1);
1560
perror_plus("setenv");
1564
ret = asprintf(&delaystring, "%f", delay);
1566
perror_plus("asprintf");
1569
ret = setenv("DELAY", delaystring, 1);
1572
perror_plus("setenv");
1576
if(connect_to != NULL){
1577
ret = setenv("CONNECT", connect_to, 1);
1579
perror_plus("setenv");
1583
if(execl(fullname, direntry->d_name, mode, NULL) == -1){
1584
perror_plus("execl");
1585
_exit(EXIT_FAILURE);
1589
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1590
perror_plus("waitpid");
1594
if(WIFEXITED(status)){
1595
if(WEXITSTATUS(status) != 0){
1596
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1597
" with status %d\n", direntry->d_name,
1598
WEXITSTATUS(status));
1602
} else if(WIFSIGNALED(status)){
1603
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1604
" signal %d\n", direntry->d_name,
1609
fprintf_plus(stderr, "Warning: network hook \"%s\""
1610
" crashed\n", direntry->d_name);
1617
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1626
error_t bring_up_interface(const char *const interface,
1629
error_t old_errno = errno;
1630
error_t ret_errno = 0;
1631
int ret, ret_setflags;
1632
struct ifreq network;
1633
unsigned int if_index = if_nametoindex(interface);
1635
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1645
if(not interface_is_up(interface)){
1646
if(not get_flags(interface, &network) and debug){
1648
fprintf_plus(stderr, "Failed to get flags for interface "
1649
"\"%s\"\n", interface);
1652
network.ifr_flags |= IFF_UP;
1654
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1657
perror_plus("socket");
1669
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
1673
/* Raise priviliges */
1677
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1678
messages about the network interface to mess up the prompt */
1679
int ret_linux = klogctl(8, NULL, 5);
1680
bool restore_loglevel = true;
1681
if(ret_linux == -1){
1682
restore_loglevel = false;
1683
perror_plus("klogctl");
1685
#endif /* __linux__ */
1686
ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1689
if(restore_loglevel){
1690
ret_linux = klogctl(7, NULL, 0);
1691
if(ret_linux == -1){
1692
perror_plus("klogctl");
1695
#endif /* __linux__ */
1697
/* Lower privileges */
1700
/* Close the socket */
1701
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1703
perror_plus("close");
1706
if(ret_setflags == -1){
1708
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1713
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
1717
/* Sleep checking until interface is running.
1718
Check every 0.25s, up to total time of delay */
1719
for(int i=0; i < delay * 4; i++){
1720
if(interface_is_running(interface)){
1723
struct timespec sleeptime = { .tv_nsec = 250000000 };
1724
ret = nanosleep(&sleeptime, NULL);
1725
if(ret == -1 and errno != EINTR){
1726
perror_plus("nanosleep");
1734
error_t take_down_interface(const char *const interface){
1735
error_t old_errno = errno;
1736
struct ifreq network;
1737
unsigned int if_index = if_nametoindex(interface);
1739
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1743
if(interface_is_up(interface)){
1744
error_t ret_errno = 0;
1745
if(not get_flags(interface, &network) and debug){
1747
fprintf_plus(stderr, "Failed to get flags for interface "
1748
"\"%s\"\n", interface);
1751
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
1753
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1756
perror_plus("socket");
1762
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
1766
/* Raise priviliges */
1769
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1772
/* Lower privileges */
1775
/* Close the socket */
1776
int ret = (int)TEMP_FAILURE_RETRY(close(sd));
1778
perror_plus("close");
1781
if(ret_setflags == -1){
1783
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
1788
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
1796
int main(int argc, char *argv[]){
1797
mandos_context mc = { .server = NULL, .dh_bits = 1024,
1798
.priority = "SECURE256:!CTYPE-X.509:"
1799
"+CTYPE-OPENPGP", .current_server = NULL,
1800
.interfaces = NULL, .interfaces_size = 0 };
1801
AvahiSServiceBrowser *sb = NULL;
1806
int exitcode = EXIT_SUCCESS;
1807
char *interfaces_to_take_down = NULL;
1808
size_t interfaces_to_take_down_size = 0;
1809
char tempdir[] = "/tmp/mandosXXXXXX";
1810
bool tempdir_created = false;
1811
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1812
const char *seckey = PATHDIR "/" SECKEY;
1813
const char *pubkey = PATHDIR "/" PUBKEY;
1814
char *interfaces_hooks = NULL;
1816
bool gnutls_initialized = false;
1817
bool gpgme_initialized = false;
1819
double retry_interval = 10; /* 10s between trying a server and
1820
retrying the same server again */
1822
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
1823
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
1828
/* Lower any group privileges we might have, just to be safe */
1832
perror_plus("setgid");
1835
/* Lower user privileges (temporarily) */
1839
perror_plus("seteuid");
1847
struct argp_option options[] = {
1848
{ .name = "debug", .key = 128,
1849
.doc = "Debug mode", .group = 3 },
1850
{ .name = "connect", .key = 'c',
1851
.arg = "ADDRESS:PORT",
1852
.doc = "Connect directly to a specific Mandos server",
1854
{ .name = "interface", .key = 'i',
1856
.doc = "Network interface that will be used to search for"
1859
{ .name = "seckey", .key = 's',
1861
.doc = "OpenPGP secret key file base name",
1863
{ .name = "pubkey", .key = 'p',
1865
.doc = "OpenPGP public key file base name",
1867
{ .name = "dh-bits", .key = 129,
1869
.doc = "Bit length of the prime number used in the"
1870
" Diffie-Hellman key exchange",
1872
{ .name = "priority", .key = 130,
1874
.doc = "GnuTLS priority string for the TLS handshake",
1876
{ .name = "delay", .key = 131,
1878
.doc = "Maximum delay to wait for interface startup",
1880
{ .name = "retry", .key = 132,
1882
.doc = "Retry interval used when denied by the Mandos server",
1884
{ .name = "network-hook-dir", .key = 133,
1886
.doc = "Directory where network hooks are located",
1889
* These reproduce what we would get without ARGP_NO_HELP
1891
{ .name = "help", .key = '?',
1892
.doc = "Give this help list", .group = -1 },
1893
{ .name = "usage", .key = -3,
1894
.doc = "Give a short usage message", .group = -1 },
1895
{ .name = "version", .key = 'V',
1896
.doc = "Print program version", .group = -1 },
1900
error_t parse_opt(int key, char *arg,
1901
struct argp_state *state){
1904
case 128: /* --debug */
1907
case 'c': /* --connect */
1910
case 'i': /* --interface */
1911
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
1914
argp_error(state, "%s", strerror(ret_errno));
1917
case 's': /* --seckey */
1920
case 'p': /* --pubkey */
1923
case 129: /* --dh-bits */
1925
tmpmax = strtoimax(arg, &tmp, 10);
1926
if(errno != 0 or tmp == arg or *tmp != '\0'
1927
or tmpmax != (typeof(mc.dh_bits))tmpmax){
1928
argp_error(state, "Bad number of DH bits");
1930
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
1932
case 130: /* --priority */
1935
case 131: /* --delay */
1937
delay = strtof(arg, &tmp);
1938
if(errno != 0 or tmp == arg or *tmp != '\0'){
1939
argp_error(state, "Bad delay");
1941
case 132: /* --retry */
1943
retry_interval = strtod(arg, &tmp);
1944
if(errno != 0 or tmp == arg or *tmp != '\0'
1945
or (retry_interval * 1000) > INT_MAX
1946
or retry_interval < 0){
1947
argp_error(state, "Bad retry interval");
1950
case 133: /* --network-hook-dir */
1954
* These reproduce what we would get without ARGP_NO_HELP
1956
case '?': /* --help */
1957
argp_state_help(state, state->out_stream,
1958
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
1959
& ~(unsigned int)ARGP_HELP_EXIT_OK);
1960
case -3: /* --usage */
1961
argp_state_help(state, state->out_stream,
1962
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
1963
case 'V': /* --version */
1964
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
1965
exit(argp_err_exit_status);
1968
return ARGP_ERR_UNKNOWN;
1973
struct argp argp = { .options = options, .parser = parse_opt,
1975
.doc = "Mandos client -- Get and decrypt"
1976
" passwords from a Mandos server" };
1977
ret = argp_parse(&argp, argc, argv,
1978
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
1985
perror_plus("argp_parse");
1986
exitcode = EX_OSERR;
1989
exitcode = EX_USAGE;
1995
/* Work around Debian bug #633582:
1996
<http://bugs.debian.org/633582> */
1998
/* Re-raise priviliges */
1999
if(raise_privileges() == 0){
2002
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2003
int seckey_fd = open(seckey, O_RDONLY);
2004
if(seckey_fd == -1){
2005
perror_plus("open");
2007
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2009
perror_plus("fstat");
2011
if(S_ISREG(st.st_mode)
2012
and st.st_uid == 0 and st.st_gid == 0){
2013
ret = fchown(seckey_fd, uid, gid);
2015
perror_plus("fchown");
2019
TEMP_FAILURE_RETRY(close(seckey_fd));
2023
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2024
int pubkey_fd = open(pubkey, O_RDONLY);
2025
if(pubkey_fd == -1){
2026
perror_plus("open");
2028
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2030
perror_plus("fstat");
2032
if(S_ISREG(st.st_mode)
2033
and st.st_uid == 0 and st.st_gid == 0){
2034
ret = fchown(pubkey_fd, uid, gid);
2036
perror_plus("fchown");
2040
TEMP_FAILURE_RETRY(close(pubkey_fd));
2044
/* Lower privileges */
2049
/* Remove invalid interface names (except "none") */
2051
char *interface = NULL;
2052
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2054
if(strcmp(interface, "none") != 0
2055
and if_nametoindex(interface) == 0){
2056
if(interface[0] != '\0'){
2057
fprintf_plus(stderr, "Not using nonexisting interface"
2058
" \"%s\"\n", interface);
2060
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2066
/* Run network hooks */
2068
if(mc.interfaces != NULL){
2069
interfaces_hooks = malloc(mc.interfaces_size);
2070
if(interfaces_hooks == NULL){
2071
perror_plus("malloc");
2074
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2075
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2077
if(not run_network_hooks("start", interfaces_hooks != NULL ?
2078
interfaces_hooks : "", delay)){
2084
avahi_set_log_function(empty_log);
2087
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2088
from the signal handler */
2089
/* Initialize the pseudo-RNG for Avahi */
2090
srand((unsigned int) time(NULL));
2091
simple_poll = avahi_simple_poll_new();
2092
if(simple_poll == NULL){
2093
fprintf_plus(stderr,
2094
"Avahi: Failed to create simple poll object.\n");
2095
exitcode = EX_UNAVAILABLE;
2099
sigemptyset(&sigterm_action.sa_mask);
2100
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2102
perror_plus("sigaddset");
2103
exitcode = EX_OSERR;
2106
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2108
perror_plus("sigaddset");
2109
exitcode = EX_OSERR;
2112
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2114
perror_plus("sigaddset");
2115
exitcode = EX_OSERR;
2118
/* Need to check if the handler is SIG_IGN before handling:
2119
| [[info:libc:Initial Signal Actions]] |
2120
| [[info:libc:Basic Signal Handling]] |
2122
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2124
perror_plus("sigaction");
2127
if(old_sigterm_action.sa_handler != SIG_IGN){
2128
ret = sigaction(SIGINT, &sigterm_action, NULL);
2130
perror_plus("sigaction");
2131
exitcode = EX_OSERR;
2135
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2137
perror_plus("sigaction");
2140
if(old_sigterm_action.sa_handler != SIG_IGN){
2141
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2143
perror_plus("sigaction");
2144
exitcode = EX_OSERR;
2148
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2150
perror_plus("sigaction");
2153
if(old_sigterm_action.sa_handler != SIG_IGN){
2154
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2156
perror_plus("sigaction");
2157
exitcode = EX_OSERR;
2162
/* If no interfaces were specified, make a list */
2163
if(mc.interfaces == NULL){
2164
struct dirent **direntries;
2165
/* Look for any good interfaces */
2166
ret = scandir(sys_class_net, &direntries, good_interface,
2169
/* Add all found interfaces to interfaces list */
2170
for(int i = 0; i < ret; ++i){
2171
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2172
direntries[i]->d_name);
2175
perror_plus("argz_add");
2179
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2180
direntries[i]->d_name);
2186
fprintf_plus(stderr, "Could not find a network interface\n");
2187
exitcode = EXIT_FAILURE;
2192
/* Bring up interfaces which are down, and remove any "none"s */
2194
char *interface = NULL;
2195
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2197
/* If interface name is "none", stop bringing up interfaces.
2198
Also remove all instances of "none" from the list */
2199
if(strcmp(interface, "none") == 0){
2200
argz_delete(&mc.interfaces, &mc.interfaces_size,
2203
while((interface = argz_next(mc.interfaces,
2204
mc.interfaces_size, interface))){
2205
if(strcmp(interface, "none") == 0){
2206
argz_delete(&mc.interfaces, &mc.interfaces_size,
2213
bool interface_was_up = interface_is_up(interface);
2214
ret = bring_up_interface(interface, delay);
2215
if(not interface_was_up){
2218
perror_plus("Failed to bring up interface");
2220
ret_errno = argz_add(&interfaces_to_take_down,
2221
&interfaces_to_take_down_size,
2225
perror_plus("argz_add");
2230
if(debug and (interfaces_to_take_down == NULL)){
2231
fprintf_plus(stderr, "No interfaces were brought up\n");
2235
/* If we only got one interface, explicitly use only that one */
2236
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2238
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2241
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2248
ret = init_gnutls_global(pubkey, seckey, &mc);
2250
fprintf_plus(stderr, "init_gnutls_global failed\n");
2251
exitcode = EX_UNAVAILABLE;
2254
gnutls_initialized = true;
2261
if(mkdtemp(tempdir) == NULL){
2262
perror_plus("mkdtemp");
2265
tempdir_created = true;
2271
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2272
fprintf_plus(stderr, "init_gpgme failed\n");
2273
exitcode = EX_UNAVAILABLE;
2276
gpgme_initialized = true;
2283
if(connect_to != NULL){
2284
/* Connect directly, do not use Zeroconf */
2285
/* (Mainly meant for debugging) */
2286
char *address = strrchr(connect_to, ':');
2288
if(address == NULL){
2289
fprintf_plus(stderr, "No colon in address\n");
2290
exitcode = EX_USAGE;
2300
tmpmax = strtoimax(address+1, &tmp, 10);
2301
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2302
or tmpmax != (in_port_t)tmpmax){
2303
fprintf_plus(stderr, "Bad port number\n");
2304
exitcode = EX_USAGE;
2312
port = (in_port_t)tmpmax;
2314
/* Colon in address indicates IPv6 */
2316
if(strchr(connect_to, ':') != NULL){
2318
/* Accept [] around IPv6 address - see RFC 5952 */
2319
if(connect_to[0] == '[' and address[-1] == ']')
2327
address = connect_to;
2333
while(not quit_now){
2334
ret = start_mandos_communication(address, port, if_index, af,
2336
if(quit_now or ret == 0){
2340
fprintf_plus(stderr, "Retrying in %d seconds\n",
2341
(int)retry_interval);
2343
sleep((unsigned int)retry_interval);
2347
exitcode = EXIT_SUCCESS;
2358
AvahiServerConfig config;
2359
/* Do not publish any local Zeroconf records */
2360
avahi_server_config_init(&config);
2361
config.publish_hinfo = 0;
2362
config.publish_addresses = 0;
2363
config.publish_workstation = 0;
2364
config.publish_domain = 0;
2366
/* Allocate a new server */
2367
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2368
&config, NULL, NULL, &ret_errno);
2370
/* Free the Avahi configuration data */
2371
avahi_server_config_free(&config);
2374
/* Check if creating the Avahi server object succeeded */
2375
if(mc.server == NULL){
2376
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2377
avahi_strerror(ret_errno));
2378
exitcode = EX_UNAVAILABLE;
2386
/* Create the Avahi service browser */
2387
sb = avahi_s_service_browser_new(mc.server, if_index,
2388
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2389
NULL, 0, browse_callback,
2392
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2393
avahi_strerror(avahi_server_errno(mc.server)));
2394
exitcode = EX_UNAVAILABLE;
2402
/* Run the main loop */
2405
fprintf_plus(stderr, "Starting Avahi loop search\n");
2408
ret = avahi_loop_with_timeout(simple_poll,
2409
(int)(retry_interval * 1000), &mc);
2411
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2412
(ret == 0) ? "successfully" : "with error");
2418
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2421
/* Cleanup things */
2422
free(mc.interfaces);
2425
avahi_s_service_browser_free(sb);
2427
if(mc.server != NULL)
2428
avahi_server_free(mc.server);
2430
if(simple_poll != NULL)
2431
avahi_simple_poll_free(simple_poll);
2433
if(gnutls_initialized){
2434
gnutls_certificate_free_credentials(mc.cred);
2435
gnutls_global_deinit();
2436
gnutls_dh_params_deinit(mc.dh_params);
2439
if(gpgme_initialized){
2440
gpgme_release(mc.ctx);
2443
/* Cleans up the circular linked list of Mandos servers the client
2445
if(mc.current_server != NULL){
2446
mc.current_server->prev->next = NULL;
2447
while(mc.current_server != NULL){
2448
server *next = mc.current_server->next;
2449
free(mc.current_server);
2450
mc.current_server = next;
2454
/* Re-raise priviliges */
2458
/* Run network hooks */
2459
run_network_hooks("stop", interfaces_hooks != NULL ?
2460
interfaces_hooks : "", delay);
2462
/* Take down the network interfaces which were brought up */
2464
char *interface = NULL;
2465
while((interface=argz_next(interfaces_to_take_down,
2466
interfaces_to_take_down_size,
2468
ret_errno = take_down_interface(interface);
2471
perror_plus("Failed to take down interface");
2474
if(debug and (interfaces_to_take_down == NULL)){
2475
fprintf_plus(stderr, "No interfaces needed to be taken"
2480
lower_privileges_permanently();
2483
free(interfaces_to_take_down);
2484
free(interfaces_hooks);
2486
/* Removes the GPGME temp directory and all files inside */
2487
if(tempdir_created){
2488
struct dirent **direntries = NULL;
2489
struct dirent *direntry = NULL;
2490
int numentries = scandir(tempdir, &direntries, notdotentries,
2492
if (numentries > 0){
2493
for(int i = 0; i < numentries; i++){
2494
direntry = direntries[i];
2495
char *fullname = NULL;
2496
ret = asprintf(&fullname, "%s/%s", tempdir,
2499
perror_plus("asprintf");
2502
ret = remove(fullname);
2504
fprintf_plus(stderr, "remove(\"%s\"): %s\n", fullname,
2511
/* need to clean even if 0 because man page doesn't specify */
2513
if (numentries == -1){
2514
perror_plus("scandir");
2516
ret = rmdir(tempdir);
2517
if(ret == -1 and errno != ENOENT){
2518
perror_plus("rmdir");
2523
sigemptyset(&old_sigterm_action.sa_mask);
2524
old_sigterm_action.sa_handler = SIG_DFL;
2525
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
2526
&old_sigterm_action,
2529
perror_plus("sigaction");
2532
ret = raise(signal_received);
2533
} while(ret != 0 and errno == EINTR);
2535
perror_plus("raise");
2538
TEMP_FAILURE_RETRY(pause());