32
31
/* Needed by GPGME, specifically gpgme_data_seek() */
33
#ifndef _LARGEFILE_SOURCE
34
32
#define _LARGEFILE_SOURCE
36
#ifndef _FILE_OFFSET_BITS
37
33
#define _FILE_OFFSET_BITS 64
40
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
42
#include <stdio.h> /* fprintf(), stderr, fwrite(),
43
stdout, ferror(), remove() */
44
#include <stdint.h> /* uint16_t, uint32_t */
45
#include <stddef.h> /* NULL, size_t, ssize_t */
46
#include <stdlib.h> /* free(), EXIT_SUCCESS, EXIT_FAILURE,
48
#include <stdbool.h> /* bool, false, true */
49
#include <string.h> /* memset(), strcmp(), strlen(),
50
strerror(), asprintf(), strcpy() */
51
#include <sys/ioctl.h> /* ioctl */
52
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
53
sockaddr_in6, PF_INET6,
54
SOCK_STREAM, uid_t, gid_t, open(),
56
#include <sys/stat.h> /* open() */
57
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
58
inet_pton(), connect() */
59
#include <fcntl.h> /* open() */
60
#include <dirent.h> /* opendir(), struct dirent, readdir()
62
#include <inttypes.h> /* PRIu16, PRIdMAX, intmax_t,
64
#include <assert.h> /* assert() */
65
#include <errno.h> /* perror(), errno */
66
#include <time.h> /* nanosleep(), time() */
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(), setuid(),
76
#include <arpa/inet.h> /* inet_pton(), htons */
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, sigaction,
87
#include <sys/klog.h> /* klogctl() */
88
#endif /* __linux__ */
91
/* All Avahi types, constants and functions
39
#include <net/if.h> /* if_nametoindex */
40
#include <sys/ioctl.h> // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
41
#include <net/if.h> // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
94
43
#include <avahi-core/core.h>
95
44
#include <avahi-core/lookup.h>
96
45
#include <avahi-core/log.h>
98
47
#include <avahi-common/malloc.h>
99
48
#include <avahi-common/error.h>
102
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
105
init_gnutls_session(),
107
#include <gnutls/openpgp.h>
108
/* gnutls_certificate_set_openpgp_key_file(),
109
GNUTLS_OPENPGP_FMT_BASE64 */
112
#include <gpgme.h> /* All GPGME types, constants and
115
GPGME_PROTOCOL_OpenPGP,
51
#include <sys/types.h> /* socket(), inet_pton() */
52
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
53
struct in6_addr, inet_pton() */
54
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
55
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
57
#include <unistd.h> /* close() */
58
#include <netinet/in.h>
59
#include <stdbool.h> /* true */
60
#include <string.h> /* memset */
61
#include <arpa/inet.h> /* inet_pton() */
62
#include <iso646.h> /* not */
65
#include <errno.h> /* perror() */
118
71
#define BUFFER_SIZE 256
120
#define PATHDIR "/conf/conf.d/mandos"
121
#define SECKEY "seckey.txt"
122
#define PUBKEY "pubkey.txt"
73
static const char *keydir = "/conf/conf.d/mandos";
74
static const char *pubkeyfile = "pubkey.txt";
75
static const char *seckeyfile = "seckey.txt";
124
77
bool debug = false;
125
static const char mandos_protocol_version[] = "1";
126
const char *argp_program_version = "mandos-client " VERSION;
127
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
129
/* Used for passing in values through the Avahi callback functions */
79
/* Used for passing in values through all the callback functions */
131
81
AvahiSimplePoll *simple_poll;
132
82
AvahiServer *server;
133
83
gnutls_certificate_credentials_t cred;
134
84
unsigned int dh_bits;
135
gnutls_dh_params_t dh_params;
136
85
const char *priority;
88
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
91
gpgme_data_t dh_crypto, dh_plain;
140
/* global context so signal handler can reach it*/
141
mandos_context mc = { .simple_poll = NULL, .server = NULL,
142
.dh_bits = 1024, .priority = "SECURE256"
143
":!CTYPE-X.509:+CTYPE-OPENPGP" };
146
* Make additional room in "buffer" for at least BUFFER_SIZE more
147
* bytes. "buffer_capacity" is how much is currently allocated,
148
* "buffer_length" is how much is already used.
150
size_t incbuffer(char **buffer, size_t buffer_length,
151
size_t buffer_capacity){
152
if(buffer_length + BUFFER_SIZE > buffer_capacity){
153
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
157
buffer_capacity += BUFFER_SIZE;
159
return buffer_capacity;
165
static bool init_gpgme(const char *seckey,
166
const char *pubkey, const char *tempdir){
95
ssize_t new_packet_capacity = 0;
96
ssize_t new_packet_length = 0;
169
97
gpgme_engine_info_t engine_info;
173
* Helper function to insert pub and seckey to the engine keyring.
175
bool import_key(const char *filename){
177
gpgme_data_t pgp_data;
179
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
185
rc = gpgme_data_new_from_fd(&pgp_data, fd);
186
if(rc != GPG_ERR_NO_ERROR){
187
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
188
gpgme_strsource(rc), gpgme_strerror(rc));
192
rc = gpgme_op_import(mc.ctx, pgp_data);
193
if(rc != GPG_ERR_NO_ERROR){
194
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
195
gpgme_strsource(rc), gpgme_strerror(rc));
199
ret = (int)TEMP_FAILURE_RETRY(close(fd));
203
gpgme_data_release(pgp_data);
208
fprintf(stderr, "Initializing GPGME\n");
100
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
212
104
gpgme_check_version(NULL);
213
105
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
214
if(rc != GPG_ERR_NO_ERROR){
106
if (rc != GPG_ERR_NO_ERROR){
215
107
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
216
108
gpgme_strsource(rc), gpgme_strerror(rc));
220
/* Set GPGME home directory for the OpenPGP engine only */
221
rc = gpgme_get_engine_info(&engine_info);
222
if(rc != GPG_ERR_NO_ERROR){
112
/* Set GPGME home directory */
113
rc = gpgme_get_engine_info (&engine_info);
114
if (rc != GPG_ERR_NO_ERROR){
223
115
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
224
116
gpgme_strsource(rc), gpgme_strerror(rc));
227
119
while(engine_info != NULL){
228
120
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
229
121
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
230
engine_info->file_name, tempdir);
122
engine_info->file_name, homedir);
233
125
engine_info = engine_info->next;
235
127
if(engine_info == NULL){
236
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
240
/* Create new GPGME "context" */
241
rc = gpgme_new(&(mc.ctx));
242
if(rc != GPG_ERR_NO_ERROR){
243
fprintf(stderr, "bad gpgme_new: %s: %s\n",
244
gpgme_strsource(rc), gpgme_strerror(rc));
248
if(not import_key(pubkey) or not import_key(seckey)){
256
* Decrypt OpenPGP data.
257
* Returns -1 on error
259
static ssize_t pgp_packet_decrypt(const char *cryptotext,
262
gpgme_data_t dh_crypto, dh_plain;
265
size_t plaintext_capacity = 0;
266
ssize_t plaintext_length = 0;
269
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
272
/* Create new GPGME data buffer from memory cryptotext */
273
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
275
if(rc != GPG_ERR_NO_ERROR){
128
fprintf(stderr, "Could not set home dir to %s\n", homedir);
132
/* Create new GPGME data buffer from packet buffer */
133
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
134
if (rc != GPG_ERR_NO_ERROR){
276
135
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
277
136
gpgme_strsource(rc), gpgme_strerror(rc));
327
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
198
/* Delete the GPGME FILE pointer cryptotext data buffer */
199
gpgme_data_release(dh_crypto);
330
201
/* Seek back to the beginning of the GPGME plaintext data buffer */
331
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
332
perror("gpgme_data_seek");
333
plaintext_length = -1;
202
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
203
perror("pgpme_data_seek");
339
plaintext_capacity = incbuffer(plaintext,
340
(size_t)plaintext_length,
342
if(plaintext_capacity == 0){
344
plaintext_length = -1;
208
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
209
*new_packet = realloc(*new_packet,
210
(unsigned int)new_packet_capacity
212
if (*new_packet == NULL){
216
new_packet_capacity += BUFFER_SIZE;
348
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
219
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
350
221
/* Print the data, if any */
356
226
perror("gpgme_data_read");
357
plaintext_length = -1;
360
plaintext_length += ret;
364
fprintf(stderr, "Decrypted password is: ");
365
for(ssize_t i = 0; i < plaintext_length; i++){
366
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
368
fprintf(stderr, "\n");
373
/* Delete the GPGME cryptotext data buffer */
374
gpgme_data_release(dh_crypto);
229
new_packet_length += ret;
232
/* FIXME: check characters before printing to screen so to not print
233
terminal control characters */
235
/* fprintf(stderr, "decrypted password is: "); */
236
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
237
/* fprintf(stderr, "\n"); */
376
240
/* Delete the GPGME plaintext data buffer */
377
241
gpgme_data_release(dh_plain);
378
return plaintext_length;
242
return new_packet_length;
381
static const char * safer_gnutls_strerror(int value){
382
const char *ret = gnutls_strerror(value); /* Spurious warning from
383
-Wunreachable-code */
245
static const char * safer_gnutls_strerror (int value) {
246
const char *ret = gnutls_strerror (value);
385
248
ret = "(unknown)";
389
/* GnuTLS log function callback */
390
252
static void debuggnutls(__attribute__((unused)) int level,
391
253
const char* string){
392
fprintf(stderr, "GnuTLS: %s", string);
254
fprintf(stderr, "%s", string);
395
static int init_gnutls_global(const char *pubkeyfilename,
396
const char *seckeyfilename){
257
static int initgnutls(mandos_context *mc, gnutls_session_t *session,
258
gnutls_dh_params_t *dh_params){
400
263
fprintf(stderr, "Initializing GnuTLS\n");
403
ret = gnutls_global_init();
404
if(ret != GNUTLS_E_SUCCESS){
405
fprintf(stderr, "GnuTLS global_init: %s\n",
406
safer_gnutls_strerror(ret));
266
if ((ret = gnutls_global_init ())
267
!= GNUTLS_E_SUCCESS) {
268
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
411
/* "Use a log level over 10 to enable all debugging options."
414
273
gnutls_global_set_log_level(11);
415
274
gnutls_global_set_log_function(debuggnutls);
418
/* OpenPGP credentials */
419
gnutls_certificate_allocate_credentials(&mc.cred);
420
if(ret != GNUTLS_E_SUCCESS){
421
fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
425
safer_gnutls_strerror(ret));
426
gnutls_global_deinit();
277
/* openpgp credentials */
278
if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
279
!= GNUTLS_E_SUCCESS) {
280
fprintf (stderr, "memory error: %s\n",
281
safer_gnutls_strerror(ret));
431
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
432
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
286
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
287
" and keyfile %s as GnuTLS credentials\n", pubkeyfile,
436
291
ret = gnutls_certificate_set_openpgp_key_file
437
(mc.cred, pubkeyfilename, seckeyfilename,
438
GNUTLS_OPENPGP_FMT_BASE64);
439
if(ret != GNUTLS_E_SUCCESS){
441
"Error[%d] while reading the OpenPGP key pair ('%s',"
442
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
443
fprintf(stderr, "The GnuTLS error is: %s\n",
444
safer_gnutls_strerror(ret));
448
/* GnuTLS server initialization */
449
ret = gnutls_dh_params_init(&mc.dh_params);
450
if(ret != GNUTLS_E_SUCCESS){
451
fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
452
" %s\n", safer_gnutls_strerror(ret));
455
ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
456
if(ret != GNUTLS_E_SUCCESS){
457
fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
458
safer_gnutls_strerror(ret));
462
gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
468
gnutls_certificate_free_credentials(mc.cred);
469
gnutls_global_deinit();
470
gnutls_dh_params_deinit(mc.dh_params);
474
static int init_gnutls_session(gnutls_session_t *session){
476
/* GnuTLS session creation */
477
ret = gnutls_init(session, GNUTLS_SERVER);
478
if(ret != GNUTLS_E_SUCCESS){
292
(mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
293
if (ret != GNUTLS_E_SUCCESS) {
295
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
297
ret, pubkeyfile, seckeyfile);
298
fprintf(stdout, "The Error is: %s\n",
299
safer_gnutls_strerror(ret));
303
//GnuTLS server initialization
304
if ((ret = gnutls_dh_params_init(dh_params))
305
!= GNUTLS_E_SUCCESS) {
306
fprintf (stderr, "Error in dh parameter initialization: %s\n",
307
safer_gnutls_strerror(ret));
311
if ((ret = gnutls_dh_params_generate2(*dh_params, mc->dh_bits))
312
!= GNUTLS_E_SUCCESS) {
313
fprintf (stderr, "Error in prime generation: %s\n",
314
safer_gnutls_strerror(ret));
318
gnutls_certificate_set_dh_params(mc->cred, *dh_params);
320
// GnuTLS session creation
321
if ((ret = gnutls_init(session, GNUTLS_SERVER))
322
!= GNUTLS_E_SUCCESS){
479
323
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
480
324
safer_gnutls_strerror(ret));
485
ret = gnutls_priority_set_direct(*session, mc.priority, &err);
486
if(ret != GNUTLS_E_SUCCESS){
487
fprintf(stderr, "Syntax error at: %s\n", err);
488
fprintf(stderr, "GnuTLS error: %s\n",
489
safer_gnutls_strerror(ret));
490
gnutls_deinit(*session);
327
if ((ret = gnutls_priority_set_direct(*session, mc->priority, &err))
328
!= GNUTLS_E_SUCCESS) {
329
fprintf(stderr, "Syntax error at: %s\n", err);
330
fprintf(stderr, "GnuTLS error: %s\n",
331
safer_gnutls_strerror(ret));
495
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
497
if(ret != GNUTLS_E_SUCCESS){
498
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
335
if ((ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
337
!= GNUTLS_E_SUCCESS) {
338
fprintf(stderr, "Error setting a credentials set: %s\n",
499
339
safer_gnutls_strerror(ret));
500
gnutls_deinit(*session);
504
343
/* ignore client certificate if any. */
505
gnutls_certificate_server_set_request(*session,
344
gnutls_certificate_server_set_request (*session,
508
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
347
gnutls_dh_set_prime_bits (*session, mc->dh_bits);
513
/* Avahi log function callback */
514
352
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
515
353
__attribute__((unused)) const char *txt){}
517
/* Called when a Mandos server is found */
518
355
static int start_mandos_communication(const char *ip, uint16_t port,
519
356
AvahiIfIndex if_index,
524
struct sockaddr_in in;
525
struct sockaddr_in6 in6;
359
struct sockaddr_in6 to;
527
360
char *buffer = NULL;
528
361
char *decrypted_buffer;
529
362
size_t buffer_length = 0;
530
363
size_t buffer_capacity = 0;
531
364
ssize_t decrypted_buffer_size;
367
char interface[IF_NAMESIZE];
534
368
gnutls_session_t session;
535
int pf; /* Protocol family */
545
fprintf(stderr, "Bad address family: %d\n", af);
549
ret = init_gnutls_session(&session);
369
gnutls_dh_params_t dh_params;
555
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
372
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
559
tcp_sd = socket(pf, SOCK_STREAM, 0);
376
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
561
378
perror("socket");
383
if(if_indextoname((unsigned int)if_index, interface) == NULL){
384
perror("if_indextoname");
387
fprintf(stderr, "Binding to interface %s\n", interface);
565
memset(&to, 0, sizeof(to));
567
to.in6.sin6_family = (sa_family_t)af;
568
ret = inet_pton(af, ip, &to.in6.sin6_addr);
570
to.in.sin_family = (sa_family_t)af;
571
ret = inet_pton(af, ip, &to.in.sin_addr);
390
memset(&to,0,sizeof(to)); /* Spurious warning */
391
to.sin6_family = AF_INET6;
392
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
574
394
perror("inet_pton");
636
ret = connect(tcp_sd, &to.in6, sizeof(to));
638
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
418
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
641
420
perror("connect");
645
const char *out = mandos_protocol_version;
648
size_t out_size = strlen(out);
649
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
650
out_size - written));
656
written += (size_t)ret;
657
if(written < out_size){
660
if(out == mandos_protocol_version){
424
ret = initgnutls (mc, &session, &dh_params);
430
gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
670
433
fprintf(stderr, "Establishing TLS session with %s\n", ip);
673
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
676
ret = gnutls_handshake(session);
677
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
679
if(ret != GNUTLS_E_SUCCESS){
436
ret = gnutls_handshake (session);
438
if (ret != GNUTLS_E_SUCCESS){
681
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
440
fprintf(stderr, "\n*** Handshake failed ***\n");
688
/* Read OpenPGP packet that contains the wanted password */
447
//Retrieve OpenPGP packet that contains the wanted password
691
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
450
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
696
buffer_capacity = incbuffer(&buffer, buffer_length,
698
if(buffer_capacity == 0){
455
if (buffer_length + BUFFER_SIZE > buffer_capacity){
456
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
461
buffer_capacity += BUFFER_SIZE;
704
sret = gnutls_record_recv(session, buffer+buffer_length,
464
ret = gnutls_record_recv(session, buffer+buffer_length,
711
471
case GNUTLS_E_INTERRUPTED:
712
472
case GNUTLS_E_AGAIN:
714
474
case GNUTLS_E_REHANDSHAKE:
716
ret = gnutls_handshake(session);
717
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
719
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
475
ret = gnutls_handshake (session);
477
fprintf(stderr, "\n*** Handshake failed ***\n");
726
484
fprintf(stderr, "Unknown error while reading data from"
727
" encrypted session with Mandos server\n");
485
" encrypted session with mandos server\n");
729
gnutls_bye(session, GNUTLS_SHUT_RDWR);
487
gnutls_bye (session, GNUTLS_SHUT_RDWR);
733
buffer_length += (size_t) sret;
491
buffer_length += (size_t) ret;
738
fprintf(stderr, "Closing TLS session\n");
741
gnutls_bye(session, GNUTLS_SHUT_RDWR);
743
if(buffer_length > 0){
495
if (buffer_length > 0){
744
496
decrypted_buffer_size = pgp_packet_decrypt(buffer,
747
if(decrypted_buffer_size >= 0){
500
if (decrypted_buffer_size >= 0){
749
501
while(written < (size_t) decrypted_buffer_size){
750
ret = (int)fwrite(decrypted_buffer + written, 1,
751
(size_t)decrypted_buffer_size - written,
502
ret = (int)fwrite (decrypted_buffer + written, 1,
503
(size_t)decrypted_buffer_size - written,
753
505
if(ret == 0 and ferror(stdout)){
755
507
fprintf(stderr, "Error writing encrypted data: %s\n",
818
566
char ip[AVAHI_ADDRESS_STR_MAX];
819
567
avahi_address_snprint(ip, sizeof(ip), address);
821
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
822
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
823
ip, (intmax_t)interface, port);
569
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
570
" port %d\n", name, host_name, ip, port);
825
int ret = start_mandos_communication(ip, port, interface,
826
avahi_proto_to_af(proto));
828
avahi_simple_poll_quit(mc.simple_poll);
572
int ret = start_mandos_communication(ip, port, interface, mc);
832
578
avahi_s_service_resolver_free(r);
835
static void browse_callback(AvahiSServiceBrowser *b,
836
AvahiIfIndex interface,
837
AvahiProtocol protocol,
838
AvahiBrowserEvent event,
842
AVAHI_GCC_UNUSED AvahiLookupResultFlags
844
AVAHI_GCC_UNUSED void* userdata){
581
static void browse_callback( AvahiSServiceBrowser *b,
582
AvahiIfIndex interface,
583
AvahiProtocol protocol,
584
AvahiBrowserEvent event,
588
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
590
mandos_context *mc = userdata;
591
assert(b); /* Spurious warning */
847
593
/* Called whenever a new services becomes available on the LAN or
848
594
is removed from the LAN */
852
598
case AVAHI_BROWSER_FAILURE:
854
fprintf(stderr, "(Avahi browser) %s\n",
855
avahi_strerror(avahi_server_errno(mc.server)));
856
avahi_simple_poll_quit(mc.simple_poll);
600
fprintf(stderr, "(Browser) %s\n",
601
avahi_strerror(avahi_server_errno(mc->server)));
602
avahi_simple_poll_quit(mc->simple_poll);
859
605
case AVAHI_BROWSER_NEW:
860
/* We ignore the returned Avahi resolver object. In the callback
861
function we free it. If the Avahi server is terminated before
862
the callback function is called the Avahi server will free the
606
/* We ignore the returned resolver object. In the callback
607
function we free it. If the server is terminated before
608
the callback function is called the server will free
609
the resolver for us. */
865
if(avahi_s_service_resolver_new(mc.server, interface, protocol,
866
name, type, domain, protocol, 0,
867
resolve_callback, NULL) == NULL)
868
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
869
name, avahi_strerror(avahi_server_errno(mc.server)));
611
if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name,
613
AVAHI_PROTO_INET6, 0,
614
resolve_callback, mc)))
615
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
616
avahi_strerror(avahi_server_errno(mc->server)));
872
619
case AVAHI_BROWSER_REMOVE:
875
622
case AVAHI_BROWSER_ALL_FOR_NOW:
876
623
case AVAHI_BROWSER_CACHE_EXHAUSTED:
878
fprintf(stderr, "No Mandos server found, still searching...\n");
884
/* stop main loop after sigterm has been called */
885
static void handle_sigterm(__attribute__((unused)) int sig){
890
int old_errno = errno;
891
if(mc.simple_poll != NULL){
892
avahi_simple_poll_quit(mc.simple_poll);
628
/* Combines file name and path and returns the malloced new
629
string. some sane checks could/should be added */
630
static const char *combinepath(const char *first, const char *second){
631
size_t f_len = strlen(first);
632
size_t s_len = strlen(second);
633
char *tmp = malloc(f_len + s_len + 2);
638
memcpy(tmp, first, f_len); /* Spurious warning */
642
memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
644
tmp[f_len + 1 + s_len] = '\0';
897
int main(int argc, char *argv[]){
898
AvahiSServiceBrowser *sb = NULL;
903
int exitcode = EXIT_SUCCESS;
904
const char *interface = "eth0";
905
struct ifreq network;
907
bool take_down_interface = false;
910
char *connect_to = NULL;
911
char tempdir[] = "/tmp/mandosXXXXXX";
912
bool tempdir_created = false;
913
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
914
const char *seckey = PATHDIR "/" SECKEY;
915
const char *pubkey = PATHDIR "/" PUBKEY;
917
bool gnutls_initialized = false;
918
bool gpgme_initialized = false;
921
struct sigaction old_sigterm_action;
922
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
925
struct argp_option options[] = {
926
{ .name = "debug", .key = 128,
927
.doc = "Debug mode", .group = 3 },
928
{ .name = "connect", .key = 'c',
929
.arg = "ADDRESS:PORT",
930
.doc = "Connect directly to a specific Mandos server",
932
{ .name = "interface", .key = 'i',
934
.doc = "Network interface that will be used to search for"
937
{ .name = "seckey", .key = 's',
939
.doc = "OpenPGP secret key file base name",
941
{ .name = "pubkey", .key = 'p',
943
.doc = "OpenPGP public key file base name",
945
{ .name = "dh-bits", .key = 129,
947
.doc = "Bit length of the prime number used in the"
948
" Diffie-Hellman key exchange",
950
{ .name = "priority", .key = 130,
952
.doc = "GnuTLS priority string for the TLS handshake",
954
{ .name = "delay", .key = 131,
956
.doc = "Maximum delay to wait for interface startup",
649
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
650
AvahiServerConfig config;
651
AvahiSServiceBrowser *sb = NULL;
655
int returncode = EXIT_SUCCESS;
656
const char *interface = "eth0";
657
struct ifreq network;
659
char *connect_to = NULL;
660
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
661
mandos_context mc = { .simple_poll = NULL, .server = NULL,
662
.dh_bits = 1024, .priority = "SECURE256"};
961
error_t parse_opt(int key, char *arg,
962
struct argp_state *state){
964
case 128: /* --debug */
967
case 'c': /* --connect */
970
case 'i': /* --interface */
973
case 's': /* --seckey */
976
case 'p': /* --pubkey */
979
case 129: /* --dh-bits */
981
tmpmax = strtoimax(arg, &tmp, 10);
982
if(errno != 0 or tmp == arg or *tmp != '\0'
983
or tmpmax != (typeof(mc.dh_bits))tmpmax){
984
fprintf(stderr, "Bad number of DH bits\n");
987
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
989
case 130: /* --priority */
992
case 131: /* --delay */
994
delay = strtof(arg, &tmp);
995
if(errno != 0 or tmp == arg or *tmp != '\0'){
996
fprintf(stderr, "Bad delay\n");
664
debug_int = debug ? 1 : 0;
666
struct option long_options[] = {
667
{"debug", no_argument, &debug_int, 1},
668
{"connect", required_argument, NULL, 'c'},
669
{"interface", required_argument, NULL, 'i'},
670
{"keydir", required_argument, NULL, 'd'},
671
{"seckey", required_argument, NULL, 's'},
672
{"pubkey", required_argument, NULL, 'p'},
673
{"dh-bits", required_argument, NULL, 'D'},
674
{"priority", required_argument, NULL, 'P'},
677
int option_index = 0;
678
ret = getopt_long (argc, argv, "i:", long_options,
705
mc.dh_bits = (unsigned int) strtol(optarg, NULL, 10);
712
mc.priority = optarg;
1005
return ARGP_ERR_UNKNOWN;
1010
struct argp argp = { .options = options, .parser = parse_opt,
1012
.doc = "Mandos client -- Get and decrypt"
1013
" passwords from a Mandos server" };
1014
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
1015
if(ret == ARGP_ERR_UNKNOWN){
1016
fprintf(stderr, "Unknown error while parsing arguments\n");
1017
exitcode = EXIT_FAILURE;
1023
avahi_set_log_function(empty_log);
1026
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1027
from the signal handler */
1028
/* Initialize the pseudo-RNG for Avahi */
1029
srand((unsigned int) time(NULL));
1030
mc.simple_poll = avahi_simple_poll_new();
1031
if(mc.simple_poll == NULL){
1032
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1033
exitcode = EXIT_FAILURE;
1037
sigemptyset(&sigterm_action.sa_mask);
1038
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1040
perror("sigaddset");
1041
exitcode = EXIT_FAILURE;
1044
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1046
perror("sigaddset");
1047
exitcode = EXIT_FAILURE;
1050
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1052
perror("sigaddset");
1053
exitcode = EXIT_FAILURE;
1056
ret = sigaction(SIGTERM, &sigterm_action, &old_sigterm_action);
1058
perror("sigaction");
1059
exitcode = EXIT_FAILURE;
1063
/* If the interface is down, bring it up */
1064
if(interface[0] != '\0'){
719
debug = debug_int ? true : false;
721
pubkeyfile = combinepath(keydir, pubkeyfile);
722
if (pubkeyfile == NULL){
723
perror("combinepath");
724
returncode = EXIT_FAILURE;
728
seckeyfile = combinepath(keydir, seckeyfile);
729
if (seckeyfile == NULL){
730
perror("combinepath");
1065
734
if_index = (AvahiIfIndex) if_nametoindex(interface);
1066
735
if(if_index == 0){
1067
736
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1068
exitcode = EXIT_FAILURE;
1077
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1078
messages to mess up the prompt */
1079
ret = klogctl(8, NULL, 5);
1080
bool restore_loglevel = true;
1082
restore_loglevel = false;
1085
#endif /* __linux__ */
740
if(connect_to != NULL){
741
/* Connect directly, do not use Zeroconf */
742
/* (Mainly meant for debugging) */
743
char *address = strrchr(connect_to, ':');
745
fprintf(stderr, "No colon in address\n");
749
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
751
perror("Bad port number");
755
address = connect_to;
756
ret = start_mandos_communication(address, port, if_index, &mc);
1087
764
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1089
766
perror("socket");
1090
exitcode = EXIT_FAILURE;
1092
if(restore_loglevel){
1093
ret = klogctl(7, NULL, 0);
1098
#endif /* __linux__ */
767
returncode = EXIT_FAILURE;
1101
strcpy(network.ifr_name, interface);
770
strcpy(network.ifr_name, interface); /* Spurious warning */
1102
771
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1104
774
perror("ioctl SIOCGIFFLAGS");
1106
if(restore_loglevel){
1107
ret = klogctl(7, NULL, 0);
1112
#endif /* __linux__ */
1113
exitcode = EXIT_FAILURE;
775
returncode = EXIT_FAILURE;
1116
778
if((network.ifr_flags & IFF_UP) == 0){
1117
779
network.ifr_flags |= IFF_UP;
1118
take_down_interface = true;
1119
780
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1121
take_down_interface = false;
1122
782
perror("ioctl SIOCSIFFLAGS");
1123
exitcode = EXIT_FAILURE;
1125
if(restore_loglevel){
1126
ret = klogctl(7, NULL, 0);
1131
#endif /* __linux__ */
1135
/* sleep checking until interface is running */
1136
for(int i=0; i < delay * 4; i++){
1137
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1139
perror("ioctl SIOCGIFFLAGS");
1140
} else if(network.ifr_flags & IFF_RUNNING){
1143
struct timespec sleeptime = { .tv_nsec = 250000000 };
1144
ret = nanosleep(&sleeptime, NULL);
1145
if(ret == -1 and errno != EINTR){
1146
perror("nanosleep");
1149
if(not take_down_interface){
1150
/* We won't need the socket anymore */
1151
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1157
if(restore_loglevel){
1158
/* Restores kernel loglevel to default */
1159
ret = klogctl(7, NULL, 0);
1164
#endif /* __linux__ */
1189
ret = init_gnutls_global(pubkey, seckey);
1191
fprintf(stderr, "init_gnutls_global failed\n");
1192
exitcode = EXIT_FAILURE;
1195
gnutls_initialized = true;
1202
tempdir_created = true;
1203
if(mkdtemp(tempdir) == NULL){
1204
tempdir_created = false;
1213
if(not init_gpgme(pubkey, seckey, tempdir)){
1214
fprintf(stderr, "init_gpgme failed\n");
1215
exitcode = EXIT_FAILURE;
1218
gpgme_initialized = true;
1225
if(connect_to != NULL){
1226
/* Connect directly, do not use Zeroconf */
1227
/* (Mainly meant for debugging) */
1228
char *address = strrchr(connect_to, ':');
1229
if(address == NULL){
1230
fprintf(stderr, "No colon in address\n");
1231
exitcode = EXIT_FAILURE;
1241
tmpmax = strtoimax(address+1, &tmp, 10);
1242
if(errno != 0 or tmp == address+1 or *tmp != '\0'
1243
or tmpmax != (uint16_t)tmpmax){
1244
fprintf(stderr, "Bad port number\n");
1245
exitcode = EXIT_FAILURE;
1253
port = (uint16_t)tmpmax;
1255
address = connect_to;
1256
/* Colon in address indicates IPv6 */
1258
if(strchr(address, ':') != NULL){
1268
ret = start_mandos_communication(address, port, if_index, af);
1270
exitcode = EXIT_FAILURE;
1272
exitcode = EXIT_SUCCESS;
1282
AvahiServerConfig config;
1283
/* Do not publish any local Zeroconf records */
783
returncode = EXIT_FAILURE;
790
avahi_set_log_function(empty_log);
793
/* Initialize the psuedo-RNG */
794
srand((unsigned int) time(NULL));
796
/* Allocate main loop object */
797
if (!(mc.simple_poll = avahi_simple_poll_new())) {
798
fprintf(stderr, "Failed to create simple poll object.\n");
799
returncode = EXIT_FAILURE;
803
/* Do not publish any local records */
1284
804
avahi_server_config_init(&config);
1285
805
config.publish_hinfo = 0;
1286
806
config.publish_addresses = 0;
1287
807
config.publish_workstation = 0;
1288
808
config.publish_domain = 0;
1290
810
/* Allocate a new server */
1291
mc.server = avahi_server_new(avahi_simple_poll_get
1292
(mc.simple_poll), &config, NULL,
811
mc.server=avahi_server_new(avahi_simple_poll_get(mc.simple_poll),
812
&config, NULL, NULL, &error);
1295
/* Free the Avahi configuration data */
814
/* Free the configuration data */
1296
815
avahi_server_config_free(&config);
1299
/* Check if creating the Avahi server object succeeded */
1300
if(mc.server == NULL){
1301
fprintf(stderr, "Failed to create Avahi server: %s\n",
1302
avahi_strerror(error));
1303
exitcode = EXIT_FAILURE;
1311
/* Create the Avahi service browser */
1312
sb = avahi_s_service_browser_new(mc.server, if_index,
1313
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
1314
NULL, 0, browse_callback, NULL);
1316
fprintf(stderr, "Failed to create service browser: %s\n",
1317
avahi_strerror(avahi_server_errno(mc.server)));
1318
exitcode = EXIT_FAILURE;
1326
/* Run the main loop */
1329
fprintf(stderr, "Starting Avahi loop search\n");
1332
avahi_simple_poll_loop(mc.simple_poll);
1337
fprintf(stderr, "%s exiting\n", argv[0]);
1340
/* Cleanup things */
1342
avahi_s_service_browser_free(sb);
1344
if(mc.server != NULL)
1345
avahi_server_free(mc.server);
1347
if(mc.simple_poll != NULL)
1348
avahi_simple_poll_free(mc.simple_poll);
1350
if(gnutls_initialized){
1351
gnutls_certificate_free_credentials(mc.cred);
1352
gnutls_global_deinit();
1353
gnutls_dh_params_deinit(mc.dh_params);
1356
if(gpgme_initialized){
1357
gpgme_release(mc.ctx);
1360
/* Take down the network interface */
1361
if(take_down_interface){
1362
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1364
perror("ioctl SIOCGIFFLAGS");
1365
} else if(network.ifr_flags & IFF_UP) {
1366
network.ifr_flags &= ~IFF_UP; /* clear flag */
1367
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1369
perror("ioctl SIOCSIFFLAGS");
1372
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1378
/* Removes the temp directory used by GPGME */
1379
if(tempdir_created){
1381
struct dirent *direntry;
1382
d = opendir(tempdir);
1384
if(errno != ENOENT){
1389
direntry = readdir(d);
1390
if(direntry == NULL){
1393
/* Skip "." and ".." */
1394
if(direntry->d_name[0] == '.'
1395
and (direntry->d_name[1] == '\0'
1396
or (direntry->d_name[1] == '.'
1397
and direntry->d_name[2] == '\0'))){
1400
char *fullname = NULL;
1401
ret = asprintf(&fullname, "%s/%s", tempdir,
1407
ret = remove(fullname);
1409
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1416
ret = rmdir(tempdir);
1417
if(ret == -1 and errno != ENOENT){
817
/* Check if creating the server object succeeded */
819
fprintf(stderr, "Failed to create server: %s\n",
820
avahi_strerror(error));
821
returncode = EXIT_FAILURE;
825
/* Create the service browser */
826
sb = avahi_s_service_browser_new(mc.server, if_index,
828
"_mandos._tcp", NULL, 0,
829
browse_callback, &mc);
831
fprintf(stderr, "Failed to create service browser: %s\n",
832
avahi_strerror(avahi_server_errno(mc.server)));
833
returncode = EXIT_FAILURE;
837
/* Run the main loop */
840
fprintf(stderr, "Starting avahi loop search\n");
843
avahi_simple_poll_loop(mc.simple_poll);
848
fprintf(stderr, "%s exiting\n", argv[0]);
853
avahi_s_service_browser_free(sb);
856
avahi_server_free(mc.server);
859
avahi_simple_poll_free(mc.simple_poll);