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,2009 Teddy Hogeborn
13
* Copyright © 2008,2009 Björn Påhlsson
15
* This program is free software: you can redistribute it and/or
16
* modify it under the terms of the GNU General Public License as
17
* published by the Free Software Foundation, either version 3 of the
18
* License, or (at your option) any later version.
20
* This program is distributed in the hope that it will be useful, but
21
* WITHOUT ANY WARRANTY; without even the implied warranty of
22
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23
* General Public License for more details.
25
* You should have received a copy of the GNU General Public License
26
* along with this program. If not, see
27
* <http://www.gnu.org/licenses/>.
29
* Contact the authors at <mandos@fukt.bsnet.se>.
32
/* Needed by GPGME, specifically gpgme_data_seek() */
33
#define _LARGEFILE_SOURCE
34
#define _FILE_OFFSET_BITS 64
36
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
38
#include <stdio.h> /* fprintf(), stderr, fwrite(),
39
stdout, ferror(), sscanf(),
41
#include <stdint.h> /* uint16_t, uint32_t */
42
#include <stddef.h> /* NULL, size_t, ssize_t */
43
#include <stdlib.h> /* free(), EXIT_SUCCESS, EXIT_FAILURE,
45
#include <stdbool.h> /* bool, false, true */
46
#include <string.h> /* memset(), strcmp(), strlen(),
47
strerror(), asprintf(), strcpy() */
48
#include <sys/ioctl.h> /* ioctl */
49
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
50
sockaddr_in6, PF_INET6,
51
SOCK_STREAM, uid_t, gid_t, open(),
53
#include <sys/stat.h> /* open() */
54
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
55
inet_pton(), connect() */
56
#include <fcntl.h> /* open() */
57
#include <dirent.h> /* opendir(), struct dirent, readdir()
59
#include <inttypes.h> /* PRIu16, intmax_t, SCNdMAX */
60
#include <assert.h> /* assert() */
61
#include <errno.h> /* perror(), errno */
62
#include <time.h> /* nanosleep(), time() */
63
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
64
SIOCSIFFLAGS, if_indextoname(),
65
if_nametoindex(), IF_NAMESIZE */
66
#include <netinet/in.h> /* IN6_IS_ADDR_LINKLOCAL,
67
INET_ADDRSTRLEN, INET6_ADDRSTRLEN
69
#include <unistd.h> /* close(), SEEK_SET, off_t, write(),
70
getuid(), getgid(), setuid(),
72
#include <arpa/inet.h> /* inet_pton(), htons */
73
#include <iso646.h> /* not, or, and */
74
#include <argp.h> /* struct argp_option, error_t, struct
75
argp_state, struct argp,
76
argp_parse(), ARGP_KEY_ARG,
77
ARGP_KEY_END, ARGP_ERR_UNKNOWN */
78
#include <signal.h> /* sigemptyset(), sigaddset(),
79
sigaction(), SIGTERM, sigaction,
83
#include <sys/klog.h> /* klogctl() */
84
#endif /* __linux__ */
87
/* All Avahi types, constants and functions
90
#include <avahi-core/core.h>
91
#include <avahi-core/lookup.h>
92
#include <avahi-core/log.h>
93
#include <avahi-common/simple-watch.h>
94
#include <avahi-common/malloc.h>
95
#include <avahi-common/error.h>
98
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
101
init_gnutls_session(),
103
#include <gnutls/openpgp.h>
104
/* gnutls_certificate_set_openpgp_key_file(),
105
GNUTLS_OPENPGP_FMT_BASE64 */
108
#include <gpgme.h> /* All GPGME types, constants and
111
GPGME_PROTOCOL_OpenPGP,
114
#define BUFFER_SIZE 256
116
#define PATHDIR "/conf/conf.d/mandos"
117
#define SECKEY "seckey.txt"
118
#define PUBKEY "pubkey.txt"
121
static const char mandos_protocol_version[] = "1";
122
const char *argp_program_version = "mandos-client " VERSION;
123
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
125
/* Used for passing in values through the Avahi callback functions */
127
AvahiSimplePoll *simple_poll;
129
gnutls_certificate_credentials_t cred;
130
unsigned int dh_bits;
131
gnutls_dh_params_t dh_params;
132
const char *priority;
136
/* global context so signal handler can reach it*/
137
mandos_context mc = { .simple_poll = NULL, .server = NULL,
138
.dh_bits = 1024, .priority = "SECURE256"
139
":!CTYPE-X.509:+CTYPE-OPENPGP" };
142
* Make additional room in "buffer" for at least BUFFER_SIZE more
143
* bytes. "buffer_capacity" is how much is currently allocated,
144
* "buffer_length" is how much is already used.
146
size_t incbuffer(char **buffer, size_t buffer_length,
147
size_t buffer_capacity){
148
if(buffer_length + BUFFER_SIZE > buffer_capacity){
149
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
153
buffer_capacity += BUFFER_SIZE;
155
return buffer_capacity;
161
static bool init_gpgme(const char *seckey,
162
const char *pubkey, const char *tempdir){
165
gpgme_engine_info_t engine_info;
169
* Helper function to insert pub and seckey to the engine keyring.
171
bool import_key(const char *filename){
173
gpgme_data_t pgp_data;
175
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
181
rc = gpgme_data_new_from_fd(&pgp_data, fd);
182
if(rc != GPG_ERR_NO_ERROR){
183
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
184
gpgme_strsource(rc), gpgme_strerror(rc));
188
rc = gpgme_op_import(mc.ctx, pgp_data);
189
if(rc != GPG_ERR_NO_ERROR){
190
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
191
gpgme_strsource(rc), gpgme_strerror(rc));
195
ret = (int)TEMP_FAILURE_RETRY(close(fd));
199
gpgme_data_release(pgp_data);
204
fprintf(stderr, "Initializing GPGME\n");
208
gpgme_check_version(NULL);
209
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
210
if(rc != GPG_ERR_NO_ERROR){
211
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
212
gpgme_strsource(rc), gpgme_strerror(rc));
216
/* Set GPGME home directory for the OpenPGP engine only */
217
rc = gpgme_get_engine_info(&engine_info);
218
if(rc != GPG_ERR_NO_ERROR){
219
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
220
gpgme_strsource(rc), gpgme_strerror(rc));
223
while(engine_info != NULL){
224
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
225
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
226
engine_info->file_name, tempdir);
229
engine_info = engine_info->next;
231
if(engine_info == NULL){
232
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
236
/* Create new GPGME "context" */
237
rc = gpgme_new(&(mc.ctx));
238
if(rc != GPG_ERR_NO_ERROR){
239
fprintf(stderr, "bad gpgme_new: %s: %s\n",
240
gpgme_strsource(rc), gpgme_strerror(rc));
244
if(not import_key(pubkey) or not import_key(seckey)){
252
* Decrypt OpenPGP data.
253
* Returns -1 on error
255
static ssize_t pgp_packet_decrypt(const char *cryptotext,
258
gpgme_data_t dh_crypto, dh_plain;
261
size_t plaintext_capacity = 0;
262
ssize_t plaintext_length = 0;
265
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
268
/* Create new GPGME data buffer from memory cryptotext */
269
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
271
if(rc != GPG_ERR_NO_ERROR){
272
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
273
gpgme_strsource(rc), gpgme_strerror(rc));
277
/* Create new empty GPGME data buffer for the plaintext */
278
rc = gpgme_data_new(&dh_plain);
279
if(rc != GPG_ERR_NO_ERROR){
280
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
281
gpgme_strsource(rc), gpgme_strerror(rc));
282
gpgme_data_release(dh_crypto);
286
/* Decrypt data from the cryptotext data buffer to the plaintext
288
rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
289
if(rc != GPG_ERR_NO_ERROR){
290
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
291
gpgme_strsource(rc), gpgme_strerror(rc));
292
plaintext_length = -1;
294
gpgme_decrypt_result_t result;
295
result = gpgme_op_decrypt_result(mc.ctx);
297
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
299
fprintf(stderr, "Unsupported algorithm: %s\n",
300
result->unsupported_algorithm);
301
fprintf(stderr, "Wrong key usage: %u\n",
302
result->wrong_key_usage);
303
if(result->file_name != NULL){
304
fprintf(stderr, "File name: %s\n", result->file_name);
306
gpgme_recipient_t recipient;
307
recipient = result->recipients;
309
while(recipient != NULL){
310
fprintf(stderr, "Public key algorithm: %s\n",
311
gpgme_pubkey_algo_name(recipient->pubkey_algo));
312
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
313
fprintf(stderr, "Secret key available: %s\n",
314
recipient->status == GPG_ERR_NO_SECKEY
316
recipient = recipient->next;
325
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
328
/* Seek back to the beginning of the GPGME plaintext data buffer */
329
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
330
perror("gpgme_data_seek");
331
plaintext_length = -1;
337
plaintext_capacity = incbuffer(plaintext,
338
(size_t)plaintext_length,
340
if(plaintext_capacity == 0){
342
plaintext_length = -1;
346
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
348
/* Print the data, if any */
354
perror("gpgme_data_read");
355
plaintext_length = -1;
358
plaintext_length += ret;
362
fprintf(stderr, "Decrypted password is: ");
363
for(ssize_t i = 0; i < plaintext_length; i++){
364
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
366
fprintf(stderr, "\n");
371
/* Delete the GPGME cryptotext data buffer */
372
gpgme_data_release(dh_crypto);
374
/* Delete the GPGME plaintext data buffer */
375
gpgme_data_release(dh_plain);
376
return plaintext_length;
379
static const char * safer_gnutls_strerror(int value){
380
const char *ret = gnutls_strerror(value); /* Spurious warning from
381
-Wunreachable-code */
387
/* GnuTLS log function callback */
388
static void debuggnutls(__attribute__((unused)) int level,
390
fprintf(stderr, "GnuTLS: %s", string);
393
static int init_gnutls_global(const char *pubkeyfilename,
394
const char *seckeyfilename){
398
fprintf(stderr, "Initializing GnuTLS\n");
401
ret = gnutls_global_init();
402
if(ret != GNUTLS_E_SUCCESS){
403
fprintf(stderr, "GnuTLS global_init: %s\n",
404
safer_gnutls_strerror(ret));
409
/* "Use a log level over 10 to enable all debugging options."
412
gnutls_global_set_log_level(11);
413
gnutls_global_set_log_function(debuggnutls);
416
/* OpenPGP credentials */
417
gnutls_certificate_allocate_credentials(&mc.cred);
418
if(ret != GNUTLS_E_SUCCESS){
419
fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
423
safer_gnutls_strerror(ret));
424
gnutls_global_deinit();
429
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
430
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
434
ret = gnutls_certificate_set_openpgp_key_file
435
(mc.cred, pubkeyfilename, seckeyfilename,
436
GNUTLS_OPENPGP_FMT_BASE64);
437
if(ret != GNUTLS_E_SUCCESS){
439
"Error[%d] while reading the OpenPGP key pair ('%s',"
440
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
441
fprintf(stderr, "The GnuTLS error is: %s\n",
442
safer_gnutls_strerror(ret));
446
/* GnuTLS server initialization */
447
ret = gnutls_dh_params_init(&mc.dh_params);
448
if(ret != GNUTLS_E_SUCCESS){
449
fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
450
" %s\n", safer_gnutls_strerror(ret));
453
ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
454
if(ret != GNUTLS_E_SUCCESS){
455
fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
456
safer_gnutls_strerror(ret));
460
gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
466
gnutls_certificate_free_credentials(mc.cred);
467
gnutls_global_deinit();
468
gnutls_dh_params_deinit(mc.dh_params);
472
static int init_gnutls_session(gnutls_session_t *session){
474
/* GnuTLS session creation */
475
ret = gnutls_init(session, GNUTLS_SERVER);
476
if(ret != GNUTLS_E_SUCCESS){
477
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
478
safer_gnutls_strerror(ret));
483
ret = gnutls_priority_set_direct(*session, mc.priority, &err);
484
if(ret != GNUTLS_E_SUCCESS){
485
fprintf(stderr, "Syntax error at: %s\n", err);
486
fprintf(stderr, "GnuTLS error: %s\n",
487
safer_gnutls_strerror(ret));
488
gnutls_deinit(*session);
493
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
495
if(ret != GNUTLS_E_SUCCESS){
496
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
497
safer_gnutls_strerror(ret));
498
gnutls_deinit(*session);
502
/* ignore client certificate if any. */
503
gnutls_certificate_server_set_request(*session,
506
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
511
/* Avahi log function callback */
512
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
513
__attribute__((unused)) const char *txt){}
515
/* Called when a Mandos server is found */
516
static int start_mandos_communication(const char *ip, uint16_t port,
517
AvahiIfIndex if_index,
522
struct sockaddr_in in;
523
struct sockaddr_in6 in6;
526
char *decrypted_buffer;
527
size_t buffer_length = 0;
528
size_t buffer_capacity = 0;
529
ssize_t decrypted_buffer_size;
532
gnutls_session_t session;
533
int pf; /* Protocol family */
543
fprintf(stderr, "Bad address family: %d\n", af);
547
ret = init_gnutls_session(&session);
553
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
557
tcp_sd = socket(pf, SOCK_STREAM, 0);
563
memset(&to, 0, sizeof(to));
565
to.in6.sin6_family = (uint16_t)af;
566
ret = inet_pton(af, ip, &to.in6.sin6_addr);
568
to.in.sin_family = (sa_family_t)af;
569
ret = inet_pton(af, ip, &to.in.sin_addr);
576
fprintf(stderr, "Bad address: %s\n", ip);
580
to.in6.sin6_port = htons(port); /* Spurious warnings from
582
-Wunreachable-code */
584
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
585
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
587
if(if_index == AVAHI_IF_UNSPEC){
588
fprintf(stderr, "An IPv6 link-local address is incomplete"
589
" without a network interface\n");
592
/* Set the network interface number as scope */
593
to.in6.sin6_scope_id = (uint32_t)if_index;
596
to.in.sin_port = htons(port); /* Spurious warnings from
598
-Wunreachable-code */
602
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
603
char interface[IF_NAMESIZE];
604
if(if_indextoname((unsigned int)if_index, interface) == NULL){
605
perror("if_indextoname");
607
fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
608
ip, interface, port);
611
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
614
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
615
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
618
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
621
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
627
if(strcmp(addrstr, ip) != 0){
628
fprintf(stderr, "Canonical address form: %s\n", addrstr);
634
ret = connect(tcp_sd, &to.in6, sizeof(to));
636
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
643
const char *out = mandos_protocol_version;
646
size_t out_size = strlen(out);
647
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
648
out_size - written));
654
written += (size_t)ret;
655
if(written < out_size){
658
if(out == mandos_protocol_version){
668
fprintf(stderr, "Establishing TLS session with %s\n", ip);
671
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
674
ret = gnutls_handshake(session);
675
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
677
if(ret != GNUTLS_E_SUCCESS){
679
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
686
/* Read OpenPGP packet that contains the wanted password */
689
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
694
buffer_capacity = incbuffer(&buffer, buffer_length,
696
if(buffer_capacity == 0){
702
sret = gnutls_record_recv(session, buffer+buffer_length,
709
case GNUTLS_E_INTERRUPTED:
712
case GNUTLS_E_REHANDSHAKE:
714
ret = gnutls_handshake(session);
715
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
717
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
724
fprintf(stderr, "Unknown error while reading data from"
725
" encrypted session with Mandos server\n");
727
gnutls_bye(session, GNUTLS_SHUT_RDWR);
731
buffer_length += (size_t) sret;
736
fprintf(stderr, "Closing TLS session\n");
739
gnutls_bye(session, GNUTLS_SHUT_RDWR);
741
if(buffer_length > 0){
742
decrypted_buffer_size = pgp_packet_decrypt(buffer,
745
if(decrypted_buffer_size >= 0){
747
while(written < (size_t) decrypted_buffer_size){
748
ret = (int)fwrite(decrypted_buffer + written, 1,
749
(size_t)decrypted_buffer_size - written,
751
if(ret == 0 and ferror(stdout)){
753
fprintf(stderr, "Error writing encrypted data: %s\n",
759
written += (size_t)ret;
761
free(decrypted_buffer);
769
/* Shutdown procedure */
773
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
777
gnutls_deinit(session);
781
static void resolve_callback(AvahiSServiceResolver *r,
782
AvahiIfIndex interface,
784
AvahiResolverEvent event,
788
const char *host_name,
789
const AvahiAddress *address,
791
AVAHI_GCC_UNUSED AvahiStringList *txt,
792
AVAHI_GCC_UNUSED AvahiLookupResultFlags
794
AVAHI_GCC_UNUSED void* userdata){
797
/* Called whenever a service has been resolved successfully or
802
case AVAHI_RESOLVER_FAILURE:
803
fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
804
" of type '%s' in domain '%s': %s\n", name, type, domain,
805
avahi_strerror(avahi_server_errno(mc.server)));
808
case AVAHI_RESOLVER_FOUND:
810
char ip[AVAHI_ADDRESS_STR_MAX];
811
avahi_address_snprint(ip, sizeof(ip), address);
813
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
814
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
815
ip, (intmax_t)interface, port);
817
int ret = start_mandos_communication(ip, port, interface,
818
avahi_proto_to_af(proto));
820
avahi_simple_poll_quit(mc.simple_poll);
824
avahi_s_service_resolver_free(r);
827
static void browse_callback(AvahiSServiceBrowser *b,
828
AvahiIfIndex interface,
829
AvahiProtocol protocol,
830
AvahiBrowserEvent event,
834
AVAHI_GCC_UNUSED AvahiLookupResultFlags
836
AVAHI_GCC_UNUSED void* userdata){
839
/* Called whenever a new services becomes available on the LAN or
840
is removed from the LAN */
844
case AVAHI_BROWSER_FAILURE:
846
fprintf(stderr, "(Avahi browser) %s\n",
847
avahi_strerror(avahi_server_errno(mc.server)));
848
avahi_simple_poll_quit(mc.simple_poll);
851
case AVAHI_BROWSER_NEW:
852
/* We ignore the returned Avahi resolver object. In the callback
853
function we free it. If the Avahi server is terminated before
854
the callback function is called the Avahi server will free the
857
if(!(avahi_s_service_resolver_new(mc.server, interface,
858
protocol, name, type, domain,
859
AVAHI_PROTO_INET6, 0,
860
resolve_callback, NULL)))
861
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
862
name, avahi_strerror(avahi_server_errno(mc.server)));
865
case AVAHI_BROWSER_REMOVE:
868
case AVAHI_BROWSER_ALL_FOR_NOW:
869
case AVAHI_BROWSER_CACHE_EXHAUSTED:
871
fprintf(stderr, "No Mandos server found, still searching...\n");
877
sig_atomic_t quit_now = 0;
879
/* stop main loop after sigterm has been called */
880
static void handle_sigterm(__attribute__((unused)) int sig){
885
int old_errno = errno;
886
if(mc.simple_poll != NULL){
887
avahi_simple_poll_quit(mc.simple_poll);
892
int main(int argc, char *argv[]){
893
AvahiSServiceBrowser *sb = NULL;
898
int exitcode = EXIT_SUCCESS;
899
const char *interface = "eth0";
900
struct ifreq network;
904
char *connect_to = NULL;
905
char tempdir[] = "/tmp/mandosXXXXXX";
906
bool tempdir_created = false;
907
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
908
const char *seckey = PATHDIR "/" SECKEY;
909
const char *pubkey = PATHDIR "/" PUBKEY;
911
/* Initialize Mandos context */
912
mc = (mandos_context){ .simple_poll = NULL, .server = NULL,
913
.dh_bits = 1024, .priority = "SECURE256"
914
":!CTYPE-X.509:+CTYPE-OPENPGP" };
915
bool gnutls_initialized = false;
916
bool gpgme_initialized = false;
919
struct sigaction old_sigterm_action;
920
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
923
struct argp_option options[] = {
924
{ .name = "debug", .key = 128,
925
.doc = "Debug mode", .group = 3 },
926
{ .name = "connect", .key = 'c',
927
.arg = "ADDRESS:PORT",
928
.doc = "Connect directly to a specific Mandos server",
930
{ .name = "interface", .key = 'i',
932
.doc = "Network interface that will be used to search for"
935
{ .name = "seckey", .key = 's',
937
.doc = "OpenPGP secret key file base name",
939
{ .name = "pubkey", .key = 'p',
941
.doc = "OpenPGP public key file base name",
943
{ .name = "dh-bits", .key = 129,
945
.doc = "Bit length of the prime number used in the"
946
" Diffie-Hellman key exchange",
948
{ .name = "priority", .key = 130,
950
.doc = "GnuTLS priority string for the TLS handshake",
952
{ .name = "delay", .key = 131,
954
.doc = "Maximum delay to wait for interface startup",
959
error_t parse_opt(int key, char *arg,
960
struct argp_state *state){
962
case 128: /* --debug */
965
case 'c': /* --connect */
968
case 'i': /* --interface */
971
case 's': /* --seckey */
974
case 'p': /* --pubkey */
977
case 129: /* --dh-bits */
978
ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars);
979
if(ret < 1 or tmpmax != (typeof(mc.dh_bits))tmpmax
980
or arg[numchars] != '\0'){
981
fprintf(stderr, "Bad number of DH bits\n");
984
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
986
case 130: /* --priority */
989
case 131: /* --delay */
990
ret = sscanf(arg, "%lf%n", &delay, &numchars);
991
if(ret < 1 or arg[numchars] != '\0'){
992
fprintf(stderr, "Bad delay\n");
1001
return ARGP_ERR_UNKNOWN;
1006
struct argp argp = { .options = options, .parser = parse_opt,
1008
.doc = "Mandos client -- Get and decrypt"
1009
" passwords from a Mandos server" };
1010
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
1011
if(ret == ARGP_ERR_UNKNOWN){
1012
fprintf(stderr, "Unknown error while parsing arguments\n");
1013
exitcode = EXIT_FAILURE;
1019
avahi_set_log_function(empty_log);
1022
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1023
from the signal handler */
1024
/* Initialize the pseudo-RNG for Avahi */
1025
srand((unsigned int) time(NULL));
1026
mc.simple_poll = avahi_simple_poll_new();
1027
if(mc.simple_poll == NULL){
1028
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1029
exitcode = EXIT_FAILURE;
1033
sigemptyset(&sigterm_action.sa_mask);
1034
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1036
perror("sigaddset");
1037
exitcode = EXIT_FAILURE;
1040
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1042
perror("sigaddset");
1043
exitcode = EXIT_FAILURE;
1046
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1048
perror("sigaddset");
1049
exitcode = EXIT_FAILURE;
1052
ret = sigaction(SIGTERM, &sigterm_action, &old_sigterm_action);
1054
perror("sigaction");
1055
exitcode = EXIT_FAILURE;
1059
/* If the interface is down, bring it up */
1060
if(interface[0] != '\0'){
1062
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1063
messages to mess up the prompt */
1064
ret = klogctl(8, NULL, 5);
1065
bool restore_loglevel = true;
1067
restore_loglevel = false;
1070
#endif /* __linux__ */
1072
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1075
exitcode = EXIT_FAILURE;
1077
if(restore_loglevel){
1078
ret = klogctl(7, NULL, 0);
1083
#endif /* __linux__ */
1086
strcpy(network.ifr_name, interface);
1087
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1089
perror("ioctl SIOCGIFFLAGS");
1091
if(restore_loglevel){
1092
ret = klogctl(7, NULL, 0);
1097
#endif /* __linux__ */
1098
exitcode = EXIT_FAILURE;
1101
if((network.ifr_flags & IFF_UP) == 0){
1102
network.ifr_flags |= IFF_UP;
1103
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1105
perror("ioctl SIOCSIFFLAGS");
1106
exitcode = EXIT_FAILURE;
1108
if(restore_loglevel){
1109
ret = klogctl(7, NULL, 0);
1114
#endif /* __linux__ */
1118
/* sleep checking until interface is running */
1119
for(int i=0; i < delay * 4; i++){
1120
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1122
perror("ioctl SIOCGIFFLAGS");
1123
} else if(network.ifr_flags & IFF_RUNNING){
1126
struct timespec sleeptime = { .tv_nsec = 250000000 };
1127
ret = nanosleep(&sleeptime, NULL);
1128
if(ret == -1 and errno != EINTR){
1129
perror("nanosleep");
1132
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1137
if(restore_loglevel){
1138
/* Restores kernel loglevel to default */
1139
ret = klogctl(7, NULL, 0);
1144
#endif /* __linux__ */
1161
ret = init_gnutls_global(pubkey, seckey);
1163
fprintf(stderr, "init_gnutls_global failed\n");
1164
exitcode = EXIT_FAILURE;
1167
gnutls_initialized = true;
1170
if(mkdtemp(tempdir) == NULL){
1174
tempdir_created = true;
1176
if(not init_gpgme(pubkey, seckey, tempdir)){
1177
fprintf(stderr, "init_gpgme failed\n");
1178
exitcode = EXIT_FAILURE;
1181
gpgme_initialized = true;
1184
if(interface[0] != '\0'){
1185
if_index = (AvahiIfIndex) if_nametoindex(interface);
1187
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1188
exitcode = EXIT_FAILURE;
1193
if(connect_to != NULL){
1194
/* Connect directly, do not use Zeroconf */
1195
/* (Mainly meant for debugging) */
1196
char *address = strrchr(connect_to, ':');
1197
if(address == NULL){
1198
fprintf(stderr, "No colon in address\n");
1199
exitcode = EXIT_FAILURE;
1203
ret = sscanf(address+1, "%" SCNdMAX "%n", &tmpmax, &numchars);
1204
if(ret < 1 or tmpmax != (uint16_t)tmpmax
1205
or address[numchars+1] != '\0'){
1206
fprintf(stderr, "Bad port number\n");
1207
exitcode = EXIT_FAILURE;
1210
port = (uint16_t)tmpmax;
1212
address = connect_to;
1213
/* Colon in address indicates IPv6 */
1215
if(strchr(address, ':') != NULL){
1220
ret = start_mandos_communication(address, port, if_index, af);
1222
exitcode = EXIT_FAILURE;
1224
exitcode = EXIT_SUCCESS;
1230
AvahiServerConfig config;
1231
/* Do not publish any local Zeroconf records */
1232
avahi_server_config_init(&config);
1233
config.publish_hinfo = 0;
1234
config.publish_addresses = 0;
1235
config.publish_workstation = 0;
1236
config.publish_domain = 0;
1238
/* Allocate a new server */
1239
mc.server = avahi_server_new(avahi_simple_poll_get
1240
(mc.simple_poll), &config, NULL,
1243
/* Free the Avahi configuration data */
1244
avahi_server_config_free(&config);
1247
/* Check if creating the Avahi server object succeeded */
1248
if(mc.server == NULL){
1249
fprintf(stderr, "Failed to create Avahi server: %s\n",
1250
avahi_strerror(error));
1251
exitcode = EXIT_FAILURE;
1255
/* Create the Avahi service browser */
1256
sb = avahi_s_service_browser_new(mc.server, if_index,
1257
AVAHI_PROTO_INET6, "_mandos._tcp",
1258
NULL, 0, browse_callback, NULL);
1260
fprintf(stderr, "Failed to create service browser: %s\n",
1261
avahi_strerror(avahi_server_errno(mc.server)));
1262
exitcode = EXIT_FAILURE;
1266
/* Run the main loop */
1269
fprintf(stderr, "Starting Avahi loop search\n");
1272
avahi_simple_poll_loop(mc.simple_poll);
1277
fprintf(stderr, "%s exiting\n", argv[0]);
1280
/* Cleanup things */
1282
avahi_s_service_browser_free(sb);
1284
if(mc.server != NULL)
1285
avahi_server_free(mc.server);
1287
if(mc.simple_poll != NULL)
1288
avahi_simple_poll_free(mc.simple_poll);
1290
if(gnutls_initialized){
1291
gnutls_certificate_free_credentials(mc.cred);
1292
gnutls_global_deinit();
1293
gnutls_dh_params_deinit(mc.dh_params);
1296
if(gpgme_initialized){
1297
gpgme_release(mc.ctx);
1300
/* Removes the temp directory used by GPGME */
1301
if(tempdir_created){
1303
struct dirent *direntry;
1304
d = opendir(tempdir);
1306
if(errno != ENOENT){
1311
direntry = readdir(d);
1312
if(direntry == NULL){
1315
/* Skip "." and ".." */
1316
if(direntry->d_name[0] == '.'
1317
and (direntry->d_name[1] == '\0'
1318
or (direntry->d_name[1] == '.'
1319
and direntry->d_name[2] == '\0'))){
1322
char *fullname = NULL;
1323
ret = asprintf(&fullname, "%s/%s", tempdir,
1329
ret = remove(fullname);
1331
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1338
ret = rmdir(tempdir);
1339
if(ret == -1 and errno != ENOENT){