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-2014 Teddy Hogeborn
13
* Copyright © 2008-2014 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
35
#endif /* not _LARGEFILE_SOURCE */
36
#ifndef _FILE_OFFSET_BITS
37
#define _FILE_OFFSET_BITS 64
38
#endif /* not _FILE_OFFSET_BITS */
40
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
42
#include <stdio.h> /* fprintf(), stderr, fwrite(),
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(),
60
#include <fcntl.h> /* open(), unlinkat() */
61
#include <dirent.h> /* opendir(), struct dirent, readdir()
63
#include <inttypes.h> /* PRIu16, PRIdMAX, intmax_t,
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(),
76
setgid(), pause(), _exit(),
78
#include <arpa/inet.h> /* inet_pton(), htons() */
79
#include <iso646.h> /* not, or, and */
80
#include <argp.h> /* struct argp_option, error_t, struct
81
argp_state, struct argp,
82
argp_parse(), ARGP_KEY_ARG,
83
ARGP_KEY_END, ARGP_ERR_UNKNOWN */
84
#include <signal.h> /* sigemptyset(), sigaddset(),
85
sigaction(), SIGTERM, sig_atomic_t,
87
#include <sysexits.h> /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
88
EX_NOHOST, EX_IOERR, EX_PROTOCOL */
89
#include <sys/wait.h> /* waitpid(), WIFEXITED(),
90
WEXITSTATUS(), WTERMSIG() */
91
#include <grp.h> /* setgroups() */
92
#include <argz.h> /* argz_add_sep(), argz_next(),
93
argz_delete(), argz_append(),
94
argz_stringify(), argz_add(),
96
#include <netdb.h> /* getnameinfo(), NI_NUMERICHOST,
97
EAI_SYSTEM, gai_strerror() */
100
#include <sys/klog.h> /* klogctl() */
101
#endif /* __linux__ */
104
/* All Avahi types, constants and functions
107
#include <avahi-core/core.h>
108
#include <avahi-core/lookup.h>
109
#include <avahi-core/log.h>
110
#include <avahi-common/simple-watch.h>
111
#include <avahi-common/malloc.h>
112
#include <avahi-common/error.h>
115
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
118
init_gnutls_session(),
120
#include <gnutls/openpgp.h>
121
/* gnutls_certificate_set_openpgp_key_file(),
122
GNUTLS_OPENPGP_FMT_BASE64 */
125
#include <gpgme.h> /* All GPGME types, constants and
128
GPGME_PROTOCOL_OpenPGP,
131
#define BUFFER_SIZE 256
133
#define PATHDIR "/conf/conf.d/mandos"
134
#define SECKEY "seckey.txt"
135
#define PUBKEY "pubkey.txt"
136
#define HOOKDIR "/lib/mandos/network-hooks.d"
139
static const char mandos_protocol_version[] = "1";
140
const char *argp_program_version = "mandos-client " VERSION;
141
const char *argp_program_bug_address = "<mandos@recompile.se>";
142
static const char sys_class_net[] = "/sys/class/net";
143
char *connect_to = NULL;
144
const char *hookdir = HOOKDIR;
149
/* Doubly linked list that need to be circularly linked when used */
150
typedef struct server{
153
AvahiIfIndex if_index;
155
struct timespec last_seen;
160
/* Used for passing in values through the Avahi callback functions */
163
gnutls_certificate_credentials_t cred;
164
unsigned int dh_bits;
165
gnutls_dh_params_t dh_params;
166
const char *priority;
168
server *current_server;
170
size_t interfaces_size;
173
/* global so signal handler can reach it*/
174
AvahiSimplePoll *simple_poll;
176
sig_atomic_t quit_now = 0;
177
int signal_received = 0;
179
/* Function to use when printing errors */
180
void perror_plus(const char *print_text){
182
fprintf(stderr, "Mandos plugin %s: ",
183
program_invocation_short_name);
188
__attribute__((format (gnu_printf, 2, 3), nonnull))
189
int fprintf_plus(FILE *stream, const char *format, ...){
191
va_start (ap, format);
193
TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
194
program_invocation_short_name));
195
return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
199
* Make additional room in "buffer" for at least BUFFER_SIZE more
200
* bytes. "buffer_capacity" is how much is currently allocated,
201
* "buffer_length" is how much is already used.
203
__attribute__((nonnull, warn_unused_result))
204
size_t incbuffer(char **buffer, size_t buffer_length,
205
size_t buffer_capacity){
206
if(buffer_length + BUFFER_SIZE > buffer_capacity){
207
char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
209
int old_errno = errno;
216
buffer_capacity += BUFFER_SIZE;
218
return buffer_capacity;
221
/* Add server to set of servers to retry periodically */
222
__attribute__((nonnull, warn_unused_result))
223
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
224
int af, server **current_server){
226
server *new_server = malloc(sizeof(server));
227
if(new_server == NULL){
228
perror_plus("malloc");
231
*new_server = (server){ .ip = strdup(ip),
233
.if_index = if_index,
235
if(new_server->ip == NULL){
236
perror_plus("strdup");
240
ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
242
perror_plus("clock_gettime");
244
#pragma GCC diagnostic push
245
#pragma GCC diagnostic ignored "-Wcast-qual"
247
free((char *)(new_server->ip));
249
#pragma GCC diagnostic pop
254
/* Special case of first server */
255
if(*current_server == NULL){
256
new_server->next = new_server;
257
new_server->prev = new_server;
258
*current_server = new_server;
260
/* Place the new server last in the list */
261
new_server->next = *current_server;
262
new_server->prev = (*current_server)->prev;
263
new_server->prev->next = new_server;
264
(*current_server)->prev = new_server;
272
__attribute__((nonnull, warn_unused_result))
273
static bool init_gpgme(const char * const seckey,
274
const char * const pubkey,
275
const char * const tempdir,
278
gpgme_engine_info_t engine_info;
281
* Helper function to insert pub and seckey to the engine keyring.
283
bool import_key(const char * const filename){
286
gpgme_data_t pgp_data;
288
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
294
rc = gpgme_data_new_from_fd(&pgp_data, fd);
295
if(rc != GPG_ERR_NO_ERROR){
296
fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
297
gpgme_strsource(rc), gpgme_strerror(rc));
301
rc = gpgme_op_import(mc->ctx, pgp_data);
302
if(rc != GPG_ERR_NO_ERROR){
303
fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
304
gpgme_strsource(rc), gpgme_strerror(rc));
308
ret = (int)TEMP_FAILURE_RETRY(close(fd));
310
perror_plus("close");
312
gpgme_data_release(pgp_data);
317
fprintf_plus(stderr, "Initializing GPGME\n");
321
gpgme_check_version(NULL);
322
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
323
if(rc != GPG_ERR_NO_ERROR){
324
fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
325
gpgme_strsource(rc), gpgme_strerror(rc));
329
/* Set GPGME home directory for the OpenPGP engine only */
330
rc = gpgme_get_engine_info(&engine_info);
331
if(rc != GPG_ERR_NO_ERROR){
332
fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
333
gpgme_strsource(rc), gpgme_strerror(rc));
336
while(engine_info != NULL){
337
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
338
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
339
engine_info->file_name, tempdir);
342
engine_info = engine_info->next;
344
if(engine_info == NULL){
345
fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
350
/* Create new GPGME "context" */
351
rc = gpgme_new(&(mc->ctx));
352
if(rc != GPG_ERR_NO_ERROR){
353
fprintf_plus(stderr, "Mandos plugin mandos-client: "
354
"bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
359
if(not import_key(pubkey) or not import_key(seckey)){
367
* Decrypt OpenPGP data.
368
* Returns -1 on error
370
__attribute__((nonnull, warn_unused_result))
371
static ssize_t pgp_packet_decrypt(const char *cryptotext,
375
gpgme_data_t dh_crypto, dh_plain;
378
size_t plaintext_capacity = 0;
379
ssize_t plaintext_length = 0;
382
fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
385
/* Create new GPGME data buffer from memory cryptotext */
386
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
388
if(rc != GPG_ERR_NO_ERROR){
389
fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
390
gpgme_strsource(rc), gpgme_strerror(rc));
394
/* Create new empty GPGME data buffer for the plaintext */
395
rc = gpgme_data_new(&dh_plain);
396
if(rc != GPG_ERR_NO_ERROR){
397
fprintf_plus(stderr, "Mandos plugin mandos-client: "
398
"bad gpgme_data_new: %s: %s\n",
399
gpgme_strsource(rc), gpgme_strerror(rc));
400
gpgme_data_release(dh_crypto);
404
/* Decrypt data from the cryptotext data buffer to the plaintext
406
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
407
if(rc != GPG_ERR_NO_ERROR){
408
fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
409
gpgme_strsource(rc), gpgme_strerror(rc));
410
plaintext_length = -1;
412
gpgme_decrypt_result_t result;
413
result = gpgme_op_decrypt_result(mc->ctx);
415
fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
417
fprintf_plus(stderr, "Unsupported algorithm: %s\n",
418
result->unsupported_algorithm);
419
fprintf_plus(stderr, "Wrong key usage: %u\n",
420
result->wrong_key_usage);
421
if(result->file_name != NULL){
422
fprintf_plus(stderr, "File name: %s\n", result->file_name);
424
gpgme_recipient_t recipient;
425
recipient = result->recipients;
426
while(recipient != NULL){
427
fprintf_plus(stderr, "Public key algorithm: %s\n",
428
gpgme_pubkey_algo_name
429
(recipient->pubkey_algo));
430
fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
431
fprintf_plus(stderr, "Secret key available: %s\n",
432
recipient->status == GPG_ERR_NO_SECKEY
434
recipient = recipient->next;
442
fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
445
/* Seek back to the beginning of the GPGME plaintext data buffer */
446
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
447
perror_plus("gpgme_data_seek");
448
plaintext_length = -1;
454
plaintext_capacity = incbuffer(plaintext,
455
(size_t)plaintext_length,
457
if(plaintext_capacity == 0){
458
perror_plus("incbuffer");
459
plaintext_length = -1;
463
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
465
/* Print the data, if any */
471
perror_plus("gpgme_data_read");
472
plaintext_length = -1;
475
plaintext_length += ret;
479
fprintf_plus(stderr, "Decrypted password is: ");
480
for(ssize_t i = 0; i < plaintext_length; i++){
481
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
483
fprintf(stderr, "\n");
488
/* Delete the GPGME cryptotext data buffer */
489
gpgme_data_release(dh_crypto);
491
/* Delete the GPGME plaintext data buffer */
492
gpgme_data_release(dh_plain);
493
return plaintext_length;
496
__attribute__((warn_unused_result))
497
static const char *safer_gnutls_strerror(int value){
498
const char *ret = gnutls_strerror(value);
504
/* GnuTLS log function callback */
505
__attribute__((nonnull))
506
static void debuggnutls(__attribute__((unused)) int level,
508
fprintf_plus(stderr, "GnuTLS: %s", string);
511
__attribute__((nonnull, warn_unused_result))
512
static int init_gnutls_global(const char *pubkeyfilename,
513
const char *seckeyfilename,
518
fprintf_plus(stderr, "Initializing GnuTLS\n");
521
ret = gnutls_global_init();
522
if(ret != GNUTLS_E_SUCCESS){
523
fprintf_plus(stderr, "GnuTLS global_init: %s\n",
524
safer_gnutls_strerror(ret));
529
/* "Use a log level over 10 to enable all debugging options."
532
gnutls_global_set_log_level(11);
533
gnutls_global_set_log_function(debuggnutls);
536
/* OpenPGP credentials */
537
ret = gnutls_certificate_allocate_credentials(&mc->cred);
538
if(ret != GNUTLS_E_SUCCESS){
539
fprintf_plus(stderr, "GnuTLS memory error: %s\n",
540
safer_gnutls_strerror(ret));
541
gnutls_global_deinit();
546
fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
547
" secret key %s as GnuTLS credentials\n",
552
ret = gnutls_certificate_set_openpgp_key_file
553
(mc->cred, pubkeyfilename, seckeyfilename,
554
GNUTLS_OPENPGP_FMT_BASE64);
555
if(ret != GNUTLS_E_SUCCESS){
557
"Error[%d] while reading the OpenPGP key pair ('%s',"
558
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
559
fprintf_plus(stderr, "The GnuTLS error is: %s\n",
560
safer_gnutls_strerror(ret));
564
/* GnuTLS server initialization */
565
ret = gnutls_dh_params_init(&mc->dh_params);
566
if(ret != GNUTLS_E_SUCCESS){
567
fprintf_plus(stderr, "Error in GnuTLS DH parameter"
568
" initialization: %s\n",
569
safer_gnutls_strerror(ret));
572
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
573
if(ret != GNUTLS_E_SUCCESS){
574
fprintf_plus(stderr, "Error in GnuTLS prime generation: %s\n",
575
safer_gnutls_strerror(ret));
579
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
585
gnutls_certificate_free_credentials(mc->cred);
586
gnutls_global_deinit();
587
gnutls_dh_params_deinit(mc->dh_params);
591
__attribute__((nonnull, warn_unused_result))
592
static int init_gnutls_session(gnutls_session_t *session,
595
/* GnuTLS session creation */
597
ret = gnutls_init(session, GNUTLS_SERVER);
601
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
602
if(ret != GNUTLS_E_SUCCESS){
604
"Error in GnuTLS session initialization: %s\n",
605
safer_gnutls_strerror(ret));
611
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
613
gnutls_deinit(*session);
616
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
617
if(ret != GNUTLS_E_SUCCESS){
618
fprintf_plus(stderr, "Syntax error at: %s\n", err);
619
fprintf_plus(stderr, "GnuTLS error: %s\n",
620
safer_gnutls_strerror(ret));
621
gnutls_deinit(*session);
627
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
630
gnutls_deinit(*session);
633
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
634
if(ret != GNUTLS_E_SUCCESS){
635
fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
636
safer_gnutls_strerror(ret));
637
gnutls_deinit(*session);
641
/* ignore client certificate if any. */
642
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
644
gnutls_dh_set_prime_bits(*session, mc->dh_bits);
649
/* Avahi log function callback */
650
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
651
__attribute__((unused)) const char *txt){}
653
/* Called when a Mandos server is found */
654
__attribute__((nonnull, warn_unused_result))
655
static int start_mandos_communication(const char *ip, in_port_t port,
656
AvahiIfIndex if_index,
657
int af, mandos_context *mc){
658
int ret, tcp_sd = -1;
660
struct sockaddr_storage to;
662
char *decrypted_buffer = NULL;
663
size_t buffer_length = 0;
664
size_t buffer_capacity = 0;
667
gnutls_session_t session;
668
int pf; /* Protocol family */
685
fprintf_plus(stderr, "Bad address family: %d\n", af);
690
/* If the interface is specified and we have a list of interfaces */
691
if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
692
/* Check if the interface is one of the interfaces we are using */
695
char *interface = NULL;
696
while((interface=argz_next(mc->interfaces, mc->interfaces_size,
698
if(if_nametoindex(interface) == (unsigned int)if_index){
705
/* This interface does not match any in the list, so we don't
706
connect to the server */
708
char interface[IF_NAMESIZE];
709
if(if_indextoname((unsigned int)if_index, interface) == NULL){
710
perror_plus("if_indextoname");
712
fprintf_plus(stderr, "Skipping server on non-used interface"
714
if_indextoname((unsigned int)if_index,
722
ret = init_gnutls_session(&session, mc);
728
fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
729
PRIuMAX "\n", ip, (uintmax_t)port);
732
tcp_sd = socket(pf, SOCK_STREAM, 0);
735
perror_plus("socket");
745
memset(&to, 0, sizeof(to));
747
((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
748
ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
750
((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
751
ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
755
perror_plus("inet_pton");
761
fprintf_plus(stderr, "Bad address: %s\n", ip);
766
((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
767
if(IN6_IS_ADDR_LINKLOCAL
768
(&((struct sockaddr_in6 *)&to)->sin6_addr)){
769
if(if_index == AVAHI_IF_UNSPEC){
770
fprintf_plus(stderr, "An IPv6 link-local address is"
771
" incomplete without a network interface\n");
775
/* Set the network interface number as scope */
776
((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
779
((struct sockaddr_in *)&to)->sin_port = htons(port);
788
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
789
char interface[IF_NAMESIZE];
790
if(if_indextoname((unsigned int)if_index, interface) == NULL){
791
perror_plus("if_indextoname");
793
fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
794
"\n", ip, interface, (uintmax_t)port);
797
fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
798
ip, (uintmax_t)port);
800
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
801
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
803
ret = getnameinfo((struct sockaddr *)&to,
804
sizeof(struct sockaddr_in6),
805
addrstr, sizeof(addrstr), NULL, 0,
808
ret = getnameinfo((struct sockaddr *)&to,
809
sizeof(struct sockaddr_in),
810
addrstr, sizeof(addrstr), NULL, 0,
813
if(ret == EAI_SYSTEM){
814
perror_plus("getnameinfo");
815
} else if(ret != 0) {
816
fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
817
} else if(strcmp(addrstr, ip) != 0){
818
fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
828
ret = connect(tcp_sd, (struct sockaddr *)&to,
829
sizeof(struct sockaddr_in6));
831
ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
832
sizeof(struct sockaddr_in));
835
if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
837
perror_plus("connect");
848
const char *out = mandos_protocol_version;
851
size_t out_size = strlen(out);
852
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
853
out_size - written));
856
perror_plus("write");
860
written += (size_t)ret;
861
if(written < out_size){
864
if(out == mandos_protocol_version){
879
fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
887
/* This casting via intptr_t is to eliminate warning about casting
888
an int to a pointer type. This is exactly how the GnuTLS Guile
889
function "set-session-transport-fd!" does it. */
890
gnutls_transport_set_ptr(session,
891
(gnutls_transport_ptr_t)(intptr_t)tcp_sd);
899
ret = gnutls_handshake(session);
904
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
906
if(ret != GNUTLS_E_SUCCESS){
908
fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
915
/* Read OpenPGP packet that contains the wanted password */
918
fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
929
buffer_capacity = incbuffer(&buffer, buffer_length,
931
if(buffer_capacity == 0){
933
perror_plus("incbuffer");
943
sret = gnutls_record_recv(session, buffer+buffer_length,
950
case GNUTLS_E_INTERRUPTED:
953
case GNUTLS_E_REHANDSHAKE:
955
ret = gnutls_handshake(session);
961
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
963
fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
971
fprintf_plus(stderr, "Unknown error while reading data from"
972
" encrypted session with Mandos server\n");
973
gnutls_bye(session, GNUTLS_SHUT_RDWR);
978
buffer_length += (size_t) sret;
983
fprintf_plus(stderr, "Closing TLS session\n");
992
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
997
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
999
if(buffer_length > 0){
1000
ssize_t decrypted_buffer_size;
1001
decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
1002
&decrypted_buffer, mc);
1003
if(decrypted_buffer_size >= 0){
1006
while(written < (size_t) decrypted_buffer_size){
1012
ret = (int)fwrite(decrypted_buffer + written, 1,
1013
(size_t)decrypted_buffer_size - written,
1015
if(ret == 0 and ferror(stdout)){
1018
fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1024
written += (size_t)ret;
1030
/* Shutdown procedure */
1035
free(decrypted_buffer);
1038
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
1044
perror_plus("close");
1046
gnutls_deinit(session);
1056
__attribute__((nonnull))
1057
static void resolve_callback(AvahiSServiceResolver *r,
1058
AvahiIfIndex interface,
1059
AvahiProtocol proto,
1060
AvahiResolverEvent event,
1064
const char *host_name,
1065
const AvahiAddress *address,
1067
AVAHI_GCC_UNUSED AvahiStringList *txt,
1068
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1075
/* Called whenever a service has been resolved successfully or
1079
avahi_s_service_resolver_free(r);
1085
case AVAHI_RESOLVER_FAILURE:
1086
fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
1087
"'%s' of type '%s' in domain '%s': %s\n", name, type,
1089
avahi_strerror(avahi_server_errno
1090
(((mandos_context*)mc)->server)));
1093
case AVAHI_RESOLVER_FOUND:
1095
char ip[AVAHI_ADDRESS_STR_MAX];
1096
avahi_address_snprint(ip, sizeof(ip), address);
1098
fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
1099
PRIdMAX ") on port %" PRIu16 "\n", name,
1100
host_name, ip, (intmax_t)interface, port);
1102
int ret = start_mandos_communication(ip, (in_port_t)port,
1104
avahi_proto_to_af(proto),
1107
avahi_simple_poll_quit(simple_poll);
1109
if(not add_server(ip, (in_port_t)port, interface,
1110
avahi_proto_to_af(proto),
1111
&((mandos_context*)mc)->current_server)){
1112
fprintf_plus(stderr, "Failed to add server \"%s\" to server"
1118
avahi_s_service_resolver_free(r);
1121
static void browse_callback(AvahiSServiceBrowser *b,
1122
AvahiIfIndex interface,
1123
AvahiProtocol protocol,
1124
AvahiBrowserEvent event,
1128
AVAHI_GCC_UNUSED AvahiLookupResultFlags
1135
/* Called whenever a new services becomes available on the LAN or
1136
is removed from the LAN */
1144
case AVAHI_BROWSER_FAILURE:
1146
fprintf_plus(stderr, "(Avahi browser) %s\n",
1147
avahi_strerror(avahi_server_errno
1148
(((mandos_context*)mc)->server)));
1149
avahi_simple_poll_quit(simple_poll);
1152
case AVAHI_BROWSER_NEW:
1153
/* We ignore the returned Avahi resolver object. In the callback
1154
function we free it. If the Avahi server is terminated before
1155
the callback function is called the Avahi server will free the
1158
if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
1159
interface, protocol, name, type,
1160
domain, protocol, 0,
1161
resolve_callback, mc) == NULL)
1162
fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
1164
avahi_strerror(avahi_server_errno
1165
(((mandos_context*)mc)->server)));
1168
case AVAHI_BROWSER_REMOVE:
1171
case AVAHI_BROWSER_ALL_FOR_NOW:
1172
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1174
fprintf_plus(stderr, "No Mandos server found, still"
1181
/* Signal handler that stops main loop after SIGTERM */
1182
static void handle_sigterm(int sig){
1187
signal_received = sig;
1188
int old_errno = errno;
1189
/* set main loop to exit */
1190
if(simple_poll != NULL){
1191
avahi_simple_poll_quit(simple_poll);
1196
__attribute__((nonnull, warn_unused_result))
1197
bool get_flags(const char *ifname, struct ifreq *ifr){
1201
int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1204
perror_plus("socket");
1208
strcpy(ifr->ifr_name, ifname);
1209
ret = ioctl(s, SIOCGIFFLAGS, ifr);
1213
perror_plus("ioctl SIOCGIFFLAGS");
1221
__attribute__((nonnull, warn_unused_result))
1222
bool good_flags(const char *ifname, const struct ifreq *ifr){
1224
/* Reject the loopback device */
1225
if(ifr->ifr_flags & IFF_LOOPBACK){
1227
fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
1232
/* Accept point-to-point devices only if connect_to is specified */
1233
if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1235
fprintf_plus(stderr, "Accepting point-to-point interface"
1236
" \"%s\"\n", ifname);
1240
/* Otherwise, reject non-broadcast-capable devices */
1241
if(not (ifr->ifr_flags & IFF_BROADCAST)){
1243
fprintf_plus(stderr, "Rejecting non-broadcast interface"
1244
" \"%s\"\n", ifname);
1248
/* Reject non-ARP interfaces (including dummy interfaces) */
1249
if(ifr->ifr_flags & IFF_NOARP){
1251
fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
1257
/* Accept this device */
1259
fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
1265
* This function determines if a directory entry in /sys/class/net
1266
* corresponds to an acceptable network device.
1267
* (This function is passed to scandir(3) as a filter function.)
1269
__attribute__((nonnull, warn_unused_result))
1270
int good_interface(const struct dirent *if_entry){
1271
if(if_entry->d_name[0] == '.'){
1276
if(not get_flags(if_entry->d_name, &ifr)){
1278
fprintf_plus(stderr, "Failed to get flags for interface "
1279
"\"%s\"\n", if_entry->d_name);
1284
if(not good_flags(if_entry->d_name, &ifr)){
1291
* This function determines if a network interface is up.
1293
__attribute__((nonnull, warn_unused_result))
1294
bool interface_is_up(const char *interface){
1296
if(not get_flags(interface, &ifr)){
1298
fprintf_plus(stderr, "Failed to get flags for interface "
1299
"\"%s\"\n", interface);
1304
return (bool)(ifr.ifr_flags & IFF_UP);
1308
* This function determines if a network interface is running
1310
__attribute__((nonnull, warn_unused_result))
1311
bool interface_is_running(const char *interface){
1313
if(not get_flags(interface, &ifr)){
1315
fprintf_plus(stderr, "Failed to get flags for interface "
1316
"\"%s\"\n", interface);
1321
return (bool)(ifr.ifr_flags & IFF_RUNNING);
1324
__attribute__((nonnull, pure, warn_unused_result))
1325
int notdotentries(const struct dirent *direntry){
1326
/* Skip "." and ".." */
1327
if(direntry->d_name[0] == '.'
1328
and (direntry->d_name[1] == '\0'
1329
or (direntry->d_name[1] == '.'
1330
and direntry->d_name[2] == '\0'))){
1336
/* Is this directory entry a runnable program? */
1337
__attribute__((nonnull, warn_unused_result))
1338
int runnable_hook(const struct dirent *direntry){
1343
if((direntry->d_name)[0] == '\0'){
1348
sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1349
"abcdefghijklmnopqrstuvwxyz"
1352
if((direntry->d_name)[sret] != '\0'){
1353
/* Contains non-allowed characters */
1355
fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
1361
ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
1364
perror_plus("Could not stat hook");
1368
if(not (S_ISREG(st.st_mode))){
1369
/* Not a regular file */
1371
fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
1376
if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1377
/* Not executable */
1379
fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
1385
fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
1391
__attribute__((nonnull, warn_unused_result))
1392
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
1393
mandos_context *mc){
1395
struct timespec now;
1396
struct timespec waited_time;
1397
intmax_t block_time;
1400
if(mc->current_server == NULL){
1402
fprintf_plus(stderr, "Wait until first server is found."
1405
ret = avahi_simple_poll_iterate(s, -1);
1408
fprintf_plus(stderr, "Check current_server if we should run"
1411
/* the current time */
1412
ret = clock_gettime(CLOCK_MONOTONIC, &now);
1414
perror_plus("clock_gettime");
1417
/* Calculating in ms how long time between now and server
1418
who we visted longest time ago. Now - last seen. */
1419
waited_time.tv_sec = (now.tv_sec
1420
- mc->current_server->last_seen.tv_sec);
1421
waited_time.tv_nsec = (now.tv_nsec
1422
- mc->current_server->last_seen.tv_nsec);
1423
/* total time is 10s/10,000ms.
1424
Converting to s from ms by dividing by 1,000,
1425
and ns to ms by dividing by 1,000,000. */
1426
block_time = ((retry_interval
1427
- ((intmax_t)waited_time.tv_sec * 1000))
1428
- ((intmax_t)waited_time.tv_nsec / 1000000));
1431
fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
1435
if(block_time <= 0){
1436
ret = start_mandos_communication(mc->current_server->ip,
1437
mc->current_server->port,
1438
mc->current_server->if_index,
1439
mc->current_server->af, mc);
1441
avahi_simple_poll_quit(s);
1444
ret = clock_gettime(CLOCK_MONOTONIC,
1445
&mc->current_server->last_seen);
1447
perror_plus("clock_gettime");
1450
mc->current_server = mc->current_server->next;
1451
block_time = 0; /* Call avahi to find new Mandos
1452
servers, but don't block */
1455
ret = avahi_simple_poll_iterate(s, (int)block_time);
1458
if(ret > 0 or errno != EINTR){
1459
return (ret != 1) ? ret : 0;
1465
/* Set effective uid to 0, return errno */
1466
__attribute__((warn_unused_result))
1467
error_t raise_privileges(void){
1468
error_t old_errno = errno;
1469
error_t ret_errno = 0;
1470
if(seteuid(0) == -1){
1477
/* Set effective and real user ID to 0. Return errno. */
1478
__attribute__((warn_unused_result))
1479
error_t raise_privileges_permanently(void){
1480
error_t old_errno = errno;
1481
error_t ret_errno = raise_privileges();
1486
if(setuid(0) == -1){
1493
/* Set effective user ID to unprivileged saved user ID */
1494
__attribute__((warn_unused_result))
1495
error_t lower_privileges(void){
1496
error_t old_errno = errno;
1497
error_t ret_errno = 0;
1498
if(seteuid(uid) == -1){
1505
/* Lower privileges permanently */
1506
__attribute__((warn_unused_result))
1507
error_t lower_privileges_permanently(void){
1508
error_t old_errno = errno;
1509
error_t ret_errno = 0;
1510
if(setuid(uid) == -1){
1517
__attribute__((nonnull))
1518
void run_network_hooks(const char *mode, const char *interface,
1520
struct dirent **direntries = NULL;
1521
if(hookdir_fd == -1){
1522
hookdir_fd = open(hookdir, O_RDONLY);
1523
if(hookdir_fd == -1){
1524
if(errno == ENOENT){
1526
fprintf_plus(stderr, "Network hook directory \"%s\" not"
1527
" found\n", hookdir);
1530
perror_plus("open");
1536
#if __GLIBC_PREREQ(2, 15)
1537
int numhooks = scandirat(hookdir_fd, ".", &direntries,
1538
runnable_hook, alphasort);
1539
#else /* not __GLIBC_PREREQ(2, 15) */
1540
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1542
#endif /* not __GLIBC_PREREQ(2, 15) */
1543
#else /* not __GLIBC__ */
1544
int numhooks = scandir(hookdir, &direntries, runnable_hook,
1546
#endif /* not __GLIBC__ */
1548
perror_plus("scandir");
1551
struct dirent *direntry;
1553
int devnull = open("/dev/null", O_RDONLY);
1554
for(int i = 0; i < numhooks; i++){
1555
direntry = direntries[i];
1557
fprintf_plus(stderr, "Running network hook \"%s\"\n",
1560
pid_t hook_pid = fork();
1563
/* Raise privileges */
1564
errno = raise_privileges_permanently();
1566
perror_plus("Failed to raise privileges");
1573
perror_plus("setgid");
1576
/* Reset supplementary groups */
1578
ret = setgroups(0, NULL);
1580
perror_plus("setgroups");
1583
ret = dup2(devnull, STDIN_FILENO);
1585
perror_plus("dup2(devnull, STDIN_FILENO)");
1588
ret = close(devnull);
1590
perror_plus("close");
1593
ret = dup2(STDERR_FILENO, STDOUT_FILENO);
1595
perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
1598
ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
1600
perror_plus("setenv");
1603
ret = setenv("DEVICE", interface, 1);
1605
perror_plus("setenv");
1608
ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
1610
perror_plus("setenv");
1613
ret = setenv("MODE", mode, 1);
1615
perror_plus("setenv");
1619
ret = asprintf(&delaystring, "%f", (double)delay);
1621
perror_plus("asprintf");
1624
ret = setenv("DELAY", delaystring, 1);
1627
perror_plus("setenv");
1631
if(connect_to != NULL){
1632
ret = setenv("CONNECT", connect_to, 1);
1634
perror_plus("setenv");
1638
int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
1640
perror_plus("openat");
1641
_exit(EXIT_FAILURE);
1643
if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
1644
perror_plus("close");
1645
_exit(EXIT_FAILURE);
1647
if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
1649
perror_plus("fexecve");
1650
_exit(EXIT_FAILURE);
1654
perror_plus("fork");
1659
if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1660
perror_plus("waitpid");
1664
if(WIFEXITED(status)){
1665
if(WEXITSTATUS(status) != 0){
1666
fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
1667
" with status %d\n", direntry->d_name,
1668
WEXITSTATUS(status));
1672
} else if(WIFSIGNALED(status)){
1673
fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
1674
" signal %d\n", direntry->d_name,
1679
fprintf_plus(stderr, "Warning: network hook \"%s\""
1680
" crashed\n", direntry->d_name);
1686
fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
1692
if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
1693
perror_plus("close");
1700
__attribute__((nonnull, warn_unused_result))
1701
error_t bring_up_interface(const char *const interface,
1703
error_t old_errno = errno;
1705
struct ifreq network;
1706
unsigned int if_index = if_nametoindex(interface);
1708
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1718
if(not interface_is_up(interface)){
1719
error_t ret_errno = 0, ioctl_errno = 0;
1720
if(not get_flags(interface, &network)){
1722
fprintf_plus(stderr, "Failed to get flags for interface "
1723
"\"%s\"\n", interface);
1727
network.ifr_flags |= IFF_UP; /* set flag */
1729
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1732
perror_plus("socket");
1738
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1740
perror_plus("close");
1747
fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
1751
/* Raise privileges */
1752
ret_errno = raise_privileges();
1755
perror_plus("Failed to raise privileges");
1760
bool restore_loglevel = false;
1762
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1763
messages about the network interface to mess up the prompt */
1764
ret_linux = klogctl(8, NULL, 5);
1765
if(ret_linux == -1){
1766
perror_plus("klogctl");
1768
restore_loglevel = true;
1771
#endif /* __linux__ */
1772
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1773
ioctl_errno = errno;
1775
if(restore_loglevel){
1776
ret_linux = klogctl(7, NULL, 0);
1777
if(ret_linux == -1){
1778
perror_plus("klogctl");
1781
#endif /* __linux__ */
1783
/* If raise_privileges() succeeded above */
1785
/* Lower privileges */
1786
ret_errno = lower_privileges();
1789
perror_plus("Failed to lower privileges");
1793
/* Close the socket */
1794
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1796
perror_plus("close");
1799
if(ret_setflags == -1){
1800
errno = ioctl_errno;
1801
perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1806
fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
1810
/* Sleep checking until interface is running.
1811
Check every 0.25s, up to total time of delay */
1812
for(int i=0; i < delay * 4; i++){
1813
if(interface_is_running(interface)){
1816
struct timespec sleeptime = { .tv_nsec = 250000000 };
1817
ret = nanosleep(&sleeptime, NULL);
1818
if(ret == -1 and errno != EINTR){
1819
perror_plus("nanosleep");
1827
__attribute__((nonnull, warn_unused_result))
1828
error_t take_down_interface(const char *const interface){
1829
error_t old_errno = errno;
1830
struct ifreq network;
1831
unsigned int if_index = if_nametoindex(interface);
1833
fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
1837
if(interface_is_up(interface)){
1838
error_t ret_errno = 0, ioctl_errno = 0;
1839
if(not get_flags(interface, &network) and debug){
1841
fprintf_plus(stderr, "Failed to get flags for interface "
1842
"\"%s\"\n", interface);
1846
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
1848
int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1851
perror_plus("socket");
1857
fprintf_plus(stderr, "Taking down interface \"%s\"\n",
1861
/* Raise privileges */
1862
ret_errno = raise_privileges();
1865
perror_plus("Failed to raise privileges");
1868
int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
1869
ioctl_errno = errno;
1871
/* If raise_privileges() succeeded above */
1873
/* Lower privileges */
1874
ret_errno = lower_privileges();
1877
perror_plus("Failed to lower privileges");
1881
/* Close the socket */
1882
int ret = (int)TEMP_FAILURE_RETRY(close(sd));
1884
perror_plus("close");
1887
if(ret_setflags == -1){
1888
errno = ioctl_errno;
1889
perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
1894
fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
1902
int main(int argc, char *argv[]){
1903
mandos_context mc = { .server = NULL, .dh_bits = 1024,
1904
.priority = "SECURE256:!CTYPE-X.509:"
1905
"+CTYPE-OPENPGP:!RSA", .current_server = NULL,
1906
.interfaces = NULL, .interfaces_size = 0 };
1907
AvahiSServiceBrowser *sb = NULL;
1912
int exitcode = EXIT_SUCCESS;
1913
char *interfaces_to_take_down = NULL;
1914
size_t interfaces_to_take_down_size = 0;
1915
char run_tempdir[] = "/run/tmp/mandosXXXXXX";
1916
char old_tempdir[] = "/tmp/mandosXXXXXX";
1917
char *tempdir = NULL;
1918
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1919
const char *seckey = PATHDIR "/" SECKEY;
1920
const char *pubkey = PATHDIR "/" PUBKEY;
1921
char *interfaces_hooks = NULL;
1923
bool gnutls_initialized = false;
1924
bool gpgme_initialized = false;
1926
double retry_interval = 10; /* 10s between trying a server and
1927
retrying the same server again */
1929
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
1930
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
1935
/* Lower any group privileges we might have, just to be safe */
1939
perror_plus("setgid");
1942
/* Lower user privileges (temporarily) */
1946
perror_plus("seteuid");
1954
struct argp_option options[] = {
1955
{ .name = "debug", .key = 128,
1956
.doc = "Debug mode", .group = 3 },
1957
{ .name = "connect", .key = 'c',
1958
.arg = "ADDRESS:PORT",
1959
.doc = "Connect directly to a specific Mandos server",
1961
{ .name = "interface", .key = 'i',
1963
.doc = "Network interface that will be used to search for"
1966
{ .name = "seckey", .key = 's',
1968
.doc = "OpenPGP secret key file base name",
1970
{ .name = "pubkey", .key = 'p',
1972
.doc = "OpenPGP public key file base name",
1974
{ .name = "dh-bits", .key = 129,
1976
.doc = "Bit length of the prime number used in the"
1977
" Diffie-Hellman key exchange",
1979
{ .name = "priority", .key = 130,
1981
.doc = "GnuTLS priority string for the TLS handshake",
1983
{ .name = "delay", .key = 131,
1985
.doc = "Maximum delay to wait for interface startup",
1987
{ .name = "retry", .key = 132,
1989
.doc = "Retry interval used when denied by the Mandos server",
1991
{ .name = "network-hook-dir", .key = 133,
1993
.doc = "Directory where network hooks are located",
1996
* These reproduce what we would get without ARGP_NO_HELP
1998
{ .name = "help", .key = '?',
1999
.doc = "Give this help list", .group = -1 },
2000
{ .name = "usage", .key = -3,
2001
.doc = "Give a short usage message", .group = -1 },
2002
{ .name = "version", .key = 'V',
2003
.doc = "Print program version", .group = -1 },
2007
error_t parse_opt(int key, char *arg,
2008
struct argp_state *state){
2011
case 128: /* --debug */
2014
case 'c': /* --connect */
2017
case 'i': /* --interface */
2018
ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
2021
argp_error(state, "%s", strerror(ret_errno));
2024
case 's': /* --seckey */
2027
case 'p': /* --pubkey */
2030
case 129: /* --dh-bits */
2032
tmpmax = strtoimax(arg, &tmp, 10);
2033
if(errno != 0 or tmp == arg or *tmp != '\0'
2034
or tmpmax != (typeof(mc.dh_bits))tmpmax){
2035
argp_error(state, "Bad number of DH bits");
2037
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
2039
case 130: /* --priority */
2042
case 131: /* --delay */
2044
delay = strtof(arg, &tmp);
2045
if(errno != 0 or tmp == arg or *tmp != '\0'){
2046
argp_error(state, "Bad delay");
2048
case 132: /* --retry */
2050
retry_interval = strtod(arg, &tmp);
2051
if(errno != 0 or tmp == arg or *tmp != '\0'
2052
or (retry_interval * 1000) > INT_MAX
2053
or retry_interval < 0){
2054
argp_error(state, "Bad retry interval");
2057
case 133: /* --network-hook-dir */
2061
* These reproduce what we would get without ARGP_NO_HELP
2063
case '?': /* --help */
2064
argp_state_help(state, state->out_stream,
2065
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
2066
& ~(unsigned int)ARGP_HELP_EXIT_OK);
2067
case -3: /* --usage */
2068
argp_state_help(state, state->out_stream,
2069
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
2070
case 'V': /* --version */
2071
fprintf_plus(state->out_stream, "%s\n", argp_program_version);
2072
exit(argp_err_exit_status);
2075
return ARGP_ERR_UNKNOWN;
2080
struct argp argp = { .options = options, .parser = parse_opt,
2082
.doc = "Mandos client -- Get and decrypt"
2083
" passwords from a Mandos server" };
2084
ret = argp_parse(&argp, argc, argv,
2085
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
2092
perror_plus("argp_parse");
2093
exitcode = EX_OSERR;
2096
exitcode = EX_USAGE;
2102
/* Work around Debian bug #633582:
2103
<http://bugs.debian.org/633582> */
2105
/* Re-raise privileges */
2106
ret_errno = raise_privileges();
2109
perror_plus("Failed to raise privileges");
2113
if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
2114
int seckey_fd = open(seckey, O_RDONLY);
2115
if(seckey_fd == -1){
2116
perror_plus("open");
2118
ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
2120
perror_plus("fstat");
2122
if(S_ISREG(st.st_mode)
2123
and st.st_uid == 0 and st.st_gid == 0){
2124
ret = fchown(seckey_fd, uid, gid);
2126
perror_plus("fchown");
2130
TEMP_FAILURE_RETRY(close(seckey_fd));
2134
if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
2135
int pubkey_fd = open(pubkey, O_RDONLY);
2136
if(pubkey_fd == -1){
2137
perror_plus("open");
2139
ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
2141
perror_plus("fstat");
2143
if(S_ISREG(st.st_mode)
2144
and st.st_uid == 0 and st.st_gid == 0){
2145
ret = fchown(pubkey_fd, uid, gid);
2147
perror_plus("fchown");
2151
TEMP_FAILURE_RETRY(close(pubkey_fd));
2155
/* Lower privileges */
2156
ret_errno = lower_privileges();
2159
perror_plus("Failed to lower privileges");
2164
/* Remove invalid interface names (except "none") */
2166
char *interface = NULL;
2167
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2169
if(strcmp(interface, "none") != 0
2170
and if_nametoindex(interface) == 0){
2171
if(interface[0] != '\0'){
2172
fprintf_plus(stderr, "Not using nonexisting interface"
2173
" \"%s\"\n", interface);
2175
argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
2181
/* Run network hooks */
2183
if(mc.interfaces != NULL){
2184
interfaces_hooks = malloc(mc.interfaces_size);
2185
if(interfaces_hooks == NULL){
2186
perror_plus("malloc");
2189
memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
2190
argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
2192
run_network_hooks("start", interfaces_hooks != NULL ?
2193
interfaces_hooks : "", delay);
2197
avahi_set_log_function(empty_log);
2200
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
2201
from the signal handler */
2202
/* Initialize the pseudo-RNG for Avahi */
2203
srand((unsigned int) time(NULL));
2204
simple_poll = avahi_simple_poll_new();
2205
if(simple_poll == NULL){
2206
fprintf_plus(stderr,
2207
"Avahi: Failed to create simple poll object.\n");
2208
exitcode = EX_UNAVAILABLE;
2212
sigemptyset(&sigterm_action.sa_mask);
2213
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
2215
perror_plus("sigaddset");
2216
exitcode = EX_OSERR;
2219
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
2221
perror_plus("sigaddset");
2222
exitcode = EX_OSERR;
2225
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
2227
perror_plus("sigaddset");
2228
exitcode = EX_OSERR;
2231
/* Need to check if the handler is SIG_IGN before handling:
2232
| [[info:libc:Initial Signal Actions]] |
2233
| [[info:libc:Basic Signal Handling]] |
2235
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
2237
perror_plus("sigaction");
2240
if(old_sigterm_action.sa_handler != SIG_IGN){
2241
ret = sigaction(SIGINT, &sigterm_action, NULL);
2243
perror_plus("sigaction");
2244
exitcode = EX_OSERR;
2248
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
2250
perror_plus("sigaction");
2253
if(old_sigterm_action.sa_handler != SIG_IGN){
2254
ret = sigaction(SIGHUP, &sigterm_action, NULL);
2256
perror_plus("sigaction");
2257
exitcode = EX_OSERR;
2261
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
2263
perror_plus("sigaction");
2266
if(old_sigterm_action.sa_handler != SIG_IGN){
2267
ret = sigaction(SIGTERM, &sigterm_action, NULL);
2269
perror_plus("sigaction");
2270
exitcode = EX_OSERR;
2275
/* If no interfaces were specified, make a list */
2276
if(mc.interfaces == NULL){
2277
struct dirent **direntries = NULL;
2278
/* Look for any good interfaces */
2279
ret = scandir(sys_class_net, &direntries, good_interface,
2282
/* Add all found interfaces to interfaces list */
2283
for(int i = 0; i < ret; ++i){
2284
ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
2285
direntries[i]->d_name);
2288
perror_plus("argz_add");
2289
free(direntries[i]);
2293
fprintf_plus(stderr, "Will use interface \"%s\"\n",
2294
direntries[i]->d_name);
2296
free(direntries[i]);
2303
fprintf_plus(stderr, "Could not find a network interface\n");
2304
exitcode = EXIT_FAILURE;
2309
/* Bring up interfaces which are down, and remove any "none"s */
2311
char *interface = NULL;
2312
while((interface = argz_next(mc.interfaces, mc.interfaces_size,
2314
/* If interface name is "none", stop bringing up interfaces.
2315
Also remove all instances of "none" from the list */
2316
if(strcmp(interface, "none") == 0){
2317
argz_delete(&mc.interfaces, &mc.interfaces_size,
2320
while((interface = argz_next(mc.interfaces,
2321
mc.interfaces_size, interface))){
2322
if(strcmp(interface, "none") == 0){
2323
argz_delete(&mc.interfaces, &mc.interfaces_size,
2330
bool interface_was_up = interface_is_up(interface);
2331
errno = bring_up_interface(interface, delay);
2332
if(not interface_was_up){
2334
perror_plus("Failed to bring up interface");
2336
errno = argz_add(&interfaces_to_take_down,
2337
&interfaces_to_take_down_size,
2340
perror_plus("argz_add");
2345
if(debug and (interfaces_to_take_down == NULL)){
2346
fprintf_plus(stderr, "No interfaces were brought up\n");
2350
/* If we only got one interface, explicitly use only that one */
2351
if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
2353
fprintf_plus(stderr, "Using only interface \"%s\"\n",
2356
if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
2363
ret = init_gnutls_global(pubkey, seckey, &mc);
2365
fprintf_plus(stderr, "init_gnutls_global failed\n");
2366
exitcode = EX_UNAVAILABLE;
2369
gnutls_initialized = true;
2376
/* Try /run/tmp before /tmp */
2377
tempdir = mkdtemp(run_tempdir);
2378
if(tempdir == NULL and errno == ENOENT){
2380
fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
2381
run_tempdir, old_tempdir);
2383
tempdir = mkdtemp(old_tempdir);
2385
if(tempdir == NULL){
2386
perror_plus("mkdtemp");
2394
if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
2395
fprintf_plus(stderr, "init_gpgme failed\n");
2396
exitcode = EX_UNAVAILABLE;
2399
gpgme_initialized = true;
2406
if(connect_to != NULL){
2407
/* Connect directly, do not use Zeroconf */
2408
/* (Mainly meant for debugging) */
2409
char *address = strrchr(connect_to, ':');
2411
if(address == NULL){
2412
fprintf_plus(stderr, "No colon in address\n");
2413
exitcode = EX_USAGE;
2423
tmpmax = strtoimax(address+1, &tmp, 10);
2424
if(errno != 0 or tmp == address+1 or *tmp != '\0'
2425
or tmpmax != (in_port_t)tmpmax){
2426
fprintf_plus(stderr, "Bad port number\n");
2427
exitcode = EX_USAGE;
2435
port = (in_port_t)tmpmax;
2437
/* Colon in address indicates IPv6 */
2439
if(strchr(connect_to, ':') != NULL){
2441
/* Accept [] around IPv6 address - see RFC 5952 */
2442
if(connect_to[0] == '[' and address[-1] == ']')
2450
address = connect_to;
2456
while(not quit_now){
2457
ret = start_mandos_communication(address, port, if_index, af,
2459
if(quit_now or ret == 0){
2463
fprintf_plus(stderr, "Retrying in %d seconds\n",
2464
(int)retry_interval);
2466
sleep((unsigned int)retry_interval);
2470
exitcode = EXIT_SUCCESS;
2481
AvahiServerConfig config;
2482
/* Do not publish any local Zeroconf records */
2483
avahi_server_config_init(&config);
2484
config.publish_hinfo = 0;
2485
config.publish_addresses = 0;
2486
config.publish_workstation = 0;
2487
config.publish_domain = 0;
2489
/* Allocate a new server */
2490
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
2491
&config, NULL, NULL, &ret_errno);
2493
/* Free the Avahi configuration data */
2494
avahi_server_config_free(&config);
2497
/* Check if creating the Avahi server object succeeded */
2498
if(mc.server == NULL){
2499
fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
2500
avahi_strerror(ret_errno));
2501
exitcode = EX_UNAVAILABLE;
2509
/* Create the Avahi service browser */
2510
sb = avahi_s_service_browser_new(mc.server, if_index,
2511
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2512
NULL, 0, browse_callback,
2515
fprintf_plus(stderr, "Failed to create service browser: %s\n",
2516
avahi_strerror(avahi_server_errno(mc.server)));
2517
exitcode = EX_UNAVAILABLE;
2525
/* Run the main loop */
2528
fprintf_plus(stderr, "Starting Avahi loop search\n");
2531
ret = avahi_loop_with_timeout(simple_poll,
2532
(int)(retry_interval * 1000), &mc);
2534
fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
2535
(ret == 0) ? "successfully" : "with error");
2541
fprintf_plus(stderr, "%s exiting\n", argv[0]);
2544
/* Cleanup things */
2545
free(mc.interfaces);
2548
avahi_s_service_browser_free(sb);
2550
if(mc.server != NULL)
2551
avahi_server_free(mc.server);
2553
if(simple_poll != NULL)
2554
avahi_simple_poll_free(simple_poll);
2556
if(gnutls_initialized){
2557
gnutls_certificate_free_credentials(mc.cred);
2558
gnutls_global_deinit();
2559
gnutls_dh_params_deinit(mc.dh_params);
2562
if(gpgme_initialized){
2563
gpgme_release(mc.ctx);
2566
/* Cleans up the circular linked list of Mandos servers the client
2568
if(mc.current_server != NULL){
2569
mc.current_server->prev->next = NULL;
2570
while(mc.current_server != NULL){
2571
server *next = mc.current_server->next;
2573
#pragma GCC diagnostic push
2574
#pragma GCC diagnostic ignored "-Wcast-qual"
2576
free((char *)(mc.current_server->ip));
2578
#pragma GCC diagnostic pop
2580
free(mc.current_server);
2581
mc.current_server = next;
2585
/* Re-raise privileges */
2587
ret_errno = raise_privileges();
2590
perror_plus("Failed to raise privileges");
2593
/* Run network hooks */
2594
run_network_hooks("stop", interfaces_hooks != NULL ?
2595
interfaces_hooks : "", delay);
2597
/* Take down the network interfaces which were brought up */
2599
char *interface = NULL;
2600
while((interface=argz_next(interfaces_to_take_down,
2601
interfaces_to_take_down_size,
2603
ret_errno = take_down_interface(interface);
2606
perror_plus("Failed to take down interface");
2609
if(debug and (interfaces_to_take_down == NULL)){
2610
fprintf_plus(stderr, "No interfaces needed to be taken"
2616
ret_errno = lower_privileges_permanently();
2619
perror_plus("Failed to lower privileges permanently");
2623
free(interfaces_to_take_down);
2624
free(interfaces_hooks);
2626
/* Removes the GPGME temp directory and all files inside */
2627
if(tempdir != NULL){
2628
struct dirent **direntries = NULL;
2629
int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
2631
if(tempdir_fd == -1){
2632
perror_plus("open");
2635
#if __GLIBC_PREREQ(2, 15)
2636
int numentries = scandirat(tempdir_fd, ".", &direntries,
2637
notdotentries, alphasort);
2638
#else /* not __GLIBC_PREREQ(2, 15) */
2639
int numentries = scandir(tempdir, &direntries, notdotentries,
2641
#endif /* not __GLIBC_PREREQ(2, 15) */
2642
#else /* not __GLIBC__ */
2643
int numentries = scandir(tempdir, &direntries, notdotentries,
2645
#endif /* not __GLIBC__ */
2646
if(numentries >= 0){
2647
for(int i = 0; i < numentries; i++){
2648
ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
2650
fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
2651
" \"%s\", 0): %s\n", tempdir,
2652
direntries[i]->d_name, strerror(errno));
2654
free(direntries[i]);
2657
/* need to clean even if 0 because man page doesn't specify */
2659
if(numentries == -1){
2660
perror_plus("scandir");
2662
ret = rmdir(tempdir);
2663
if(ret == -1 and errno != ENOENT){
2664
perror_plus("rmdir");
2667
TEMP_FAILURE_RETRY(close(tempdir_fd));
2672
sigemptyset(&old_sigterm_action.sa_mask);
2673
old_sigterm_action.sa_handler = SIG_DFL;
2674
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
2675
&old_sigterm_action,
2678
perror_plus("sigaction");
2681
ret = raise(signal_received);
2682
} while(ret != 0 and errno == EINTR);
2684
perror_plus("raise");
2687
TEMP_FAILURE_RETRY(pause());