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, sig_atomic_t,
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"
74
static const char *certdir = "/conf/conf.d/mandos";
75
static const char *certfile = "openpgp-client.txt";
76
static const char *certkey = "openpgp-client-key.txt";
124
78
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 */
131
AvahiSimplePoll *simple_poll;
81
gnutls_session_t session;
133
82
gnutls_certificate_credentials_t cred;
134
unsigned int dh_bits;
135
83
gnutls_dh_params_t dh_params;
136
const char *priority;
87
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
90
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){
94
ssize_t new_packet_capacity = 0;
95
ssize_t new_packet_length = 0;
169
96
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");
99
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
212
103
gpgme_check_version(NULL);
213
104
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
214
if(rc != GPG_ERR_NO_ERROR){
105
if (rc != GPG_ERR_NO_ERROR){
215
106
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
216
107
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){
111
/* Set GPGME home directory */
112
rc = gpgme_get_engine_info (&engine_info);
113
if (rc != GPG_ERR_NO_ERROR){
223
114
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
224
115
gpgme_strsource(rc), gpgme_strerror(rc));
227
118
while(engine_info != NULL){
228
119
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
229
120
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
230
engine_info->file_name, tempdir);
121
engine_info->file_name, homedir);
233
124
engine_info = engine_info->next;
235
126
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){
127
fprintf(stderr, "Could not set home dir to %s\n", homedir);
131
/* Create new GPGME data buffer from packet buffer */
132
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
133
if (rc != GPG_ERR_NO_ERROR){
276
134
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
277
135
gpgme_strsource(rc), gpgme_strerror(rc));
327
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
197
/* Delete the GPGME FILE pointer cryptotext data buffer */
198
gpgme_data_release(dh_crypto);
330
200
/* 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;
201
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
202
perror("pgpme_data_seek");
339
plaintext_capacity = incbuffer(plaintext,
340
(size_t)plaintext_length,
342
if(plaintext_capacity == 0){
344
plaintext_length = -1;
207
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
208
*new_packet = realloc(*new_packet,
209
(unsigned int)new_packet_capacity
211
if (*new_packet == NULL){
215
new_packet_capacity += BUFFER_SIZE;
348
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
218
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
350
220
/* Print the data, if any */
356
225
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);
228
new_packet_length += ret;
231
/* FIXME: check characters before printing to screen so to not print
232
terminal control characters */
234
/* fprintf(stderr, "decrypted password is: "); */
235
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
236
/* fprintf(stderr, "\n"); */
376
239
/* Delete the GPGME plaintext data buffer */
377
240
gpgme_data_release(dh_plain);
378
return plaintext_length;
241
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 */
244
static const char * safer_gnutls_strerror (int value) {
245
const char *ret = gnutls_strerror (value);
385
247
ret = "(unknown)";
389
/* GnuTLS log function callback */
390
251
static void debuggnutls(__attribute__((unused)) int level,
391
252
const char* string){
392
fprintf(stderr, "GnuTLS: %s", string);
253
fprintf(stderr, "%s", string);
395
static int init_gnutls_global(const char *pubkeyfilename,
396
const char *seckeyfilename){
256
static int initgnutls(encrypted_session *es){
400
261
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));
264
if ((ret = gnutls_global_init ())
265
!= GNUTLS_E_SUCCESS) {
266
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
411
/* "Use a log level over 10 to enable all debugging options."
414
271
gnutls_global_set_log_level(11);
415
272
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();
275
/* openpgp credentials */
276
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
277
!= GNUTLS_E_SUCCESS) {
278
fprintf (stderr, "memory error: %s\n",
279
safer_gnutls_strerror(ret));
431
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
432
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
284
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
285
" and keyfile %s as GnuTLS credentials\n", certfile,
436
289
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){
290
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
291
if (ret != GNUTLS_E_SUCCESS) {
293
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
295
ret, certfile, certkey);
296
fprintf(stdout, "The Error is: %s\n",
297
safer_gnutls_strerror(ret));
301
//GnuTLS server initialization
302
if ((ret = gnutls_dh_params_init (&es->dh_params))
303
!= GNUTLS_E_SUCCESS) {
304
fprintf (stderr, "Error in dh parameter initialization: %s\n",
305
safer_gnutls_strerror(ret));
309
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
310
!= GNUTLS_E_SUCCESS) {
311
fprintf (stderr, "Error in prime generation: %s\n",
312
safer_gnutls_strerror(ret));
316
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
318
// GnuTLS session creation
319
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
320
!= GNUTLS_E_SUCCESS){
479
321
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
480
322
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);
325
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
326
!= GNUTLS_E_SUCCESS) {
327
fprintf(stderr, "Syntax error at: %s\n", err);
328
fprintf(stderr, "GnuTLS error: %s\n",
329
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",
333
if ((ret = gnutls_credentials_set
334
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
335
!= GNUTLS_E_SUCCESS) {
336
fprintf(stderr, "Error setting a credentials set: %s\n",
499
337
safer_gnutls_strerror(ret));
500
gnutls_deinit(*session);
504
341
/* ignore client certificate if any. */
505
gnutls_certificate_server_set_request(*session,
342
gnutls_certificate_server_set_request (es->session,
508
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
345
gnutls_dh_set_prime_bits (es->session, DH_BITS);
513
/* Avahi log function callback */
514
350
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
515
351
__attribute__((unused)) const char *txt){}
517
sig_atomic_t quit_now = 0;
518
int signal_received = 0;
520
/* Called when a Mandos server is found */
521
353
static int start_mandos_communication(const char *ip, uint16_t port,
522
AvahiIfIndex if_index,
354
AvahiIfIndex if_index){
527
struct sockaddr_in in;
528
struct sockaddr_in6 in6;
356
struct sockaddr_in6 to;
357
encrypted_session es;
530
358
char *buffer = NULL;
531
359
char *decrypted_buffer;
532
360
size_t buffer_length = 0;
533
361
size_t buffer_capacity = 0;
534
362
ssize_t decrypted_buffer_size;
537
gnutls_session_t session;
538
int pf; /* Protocol family */
548
fprintf(stderr, "Bad address family: %d\n", af);
552
ret = init_gnutls_session(&session);
365
char interface[IF_NAMESIZE];
558
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
368
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
562
tcp_sd = socket(pf, SOCK_STREAM, 0);
372
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
564
374
perror("socket");
379
if(if_indextoname((unsigned int)if_index, interface) == NULL){
381
perror("if_indextoname");
386
fprintf(stderr, "Binding to interface %s\n", interface);
568
memset(&to, 0, sizeof(to));
570
to.in6.sin6_family = (sa_family_t)af;
571
ret = inet_pton(af, ip, &to.in6.sin6_addr);
573
to.in.sin_family = (sa_family_t)af;
574
ret = inet_pton(af, ip, &to.in.sin_addr);
389
memset(&to,0,sizeof(to)); /* Spurious warning */
390
to.sin6_family = AF_INET6;
391
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
577
393
perror("inet_pton");
581
397
fprintf(stderr, "Bad address: %s\n", ip);
585
to.in6.sin6_port = htons(port); /* Spurious warnings from
587
-Wunreachable-code */
589
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
590
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
592
if(if_index == AVAHI_IF_UNSPEC){
593
fprintf(stderr, "An IPv6 link-local address is incomplete"
594
" without a network interface\n");
597
/* Set the network interface number as scope */
598
to.in6.sin6_scope_id = (uint32_t)if_index;
601
to.in.sin_port = htons(port); /* Spurious warnings from
603
-Wunreachable-code */
400
to.sin6_port = htons(port); /* Spurious warning */
402
to.sin6_scope_id = (uint32_t)if_index;
607
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
608
char interface[IF_NAMESIZE];
609
if(if_indextoname((unsigned int)if_index, interface) == NULL){
610
perror("if_indextoname");
612
fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
613
ip, interface, port);
616
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
619
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
620
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
623
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
626
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
632
if(strcmp(addrstr, ip) != 0){
633
fprintf(stderr, "Canonical address form: %s\n", addrstr);
405
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
406
/* char addrstr[INET6_ADDRSTRLEN]; */
407
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
408
/* sizeof(addrstr)) == NULL){ */
409
/* perror("inet_ntop"); */
411
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
412
/* addrstr, ntohs(to.sin6_port)); */
639
ret = connect(tcp_sd, &to.in6, sizeof(to));
641
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
416
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
644
418
perror("connect");
648
const char *out = mandos_protocol_version;
651
size_t out_size = strlen(out);
652
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
653
out_size - written));
659
written += (size_t)ret;
660
if(written < out_size){
663
if(out == mandos_protocol_version){
422
ret = initgnutls (&es);
428
gnutls_transport_set_ptr (es.session,
429
(gnutls_transport_ptr_t) tcp_sd);
673
432
fprintf(stderr, "Establishing TLS session with %s\n", ip);
676
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
679
ret = gnutls_handshake(session);
680
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
682
if(ret != GNUTLS_E_SUCCESS){
435
ret = gnutls_handshake (es.session);
437
if (ret != GNUTLS_E_SUCCESS){
684
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
439
fprintf(stderr, "\n*** Handshake failed ***\n");
691
/* Read OpenPGP packet that contains the wanted password */
446
//Retrieve OpenPGP packet that contains the wanted password
694
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
449
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
699
buffer_capacity = incbuffer(&buffer, buffer_length,
701
if(buffer_capacity == 0){
454
if (buffer_length + BUFFER_SIZE > buffer_capacity){
455
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
460
buffer_capacity += BUFFER_SIZE;
707
sret = gnutls_record_recv(session, buffer+buffer_length,
463
ret = gnutls_record_recv
464
(es.session, buffer+buffer_length, BUFFER_SIZE);
714
470
case GNUTLS_E_INTERRUPTED:
715
471
case GNUTLS_E_AGAIN:
717
473
case GNUTLS_E_REHANDSHAKE:
719
ret = gnutls_handshake(session);
720
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
722
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
474
ret = gnutls_handshake (es.session);
476
fprintf(stderr, "\n*** Handshake failed ***\n");
729
483
fprintf(stderr, "Unknown error while reading data from"
730
" encrypted session with Mandos server\n");
484
" encrypted session with mandos server\n");
732
gnutls_bye(session, GNUTLS_SHUT_RDWR);
486
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
736
buffer_length += (size_t) sret;
490
buffer_length += (size_t) ret;
741
fprintf(stderr, "Closing TLS session\n");
744
gnutls_bye(session, GNUTLS_SHUT_RDWR);
746
if(buffer_length > 0){
494
if (buffer_length > 0){
747
495
decrypted_buffer_size = pgp_packet_decrypt(buffer,
750
if(decrypted_buffer_size >= 0){
499
if (decrypted_buffer_size >= 0){
752
500
while(written < (size_t) decrypted_buffer_size){
753
ret = (int)fwrite(decrypted_buffer + written, 1,
754
(size_t)decrypted_buffer_size - written,
501
ret = (int)fwrite (decrypted_buffer + written, 1,
502
(size_t)decrypted_buffer_size - written,
756
504
if(ret == 0 and ferror(stdout)){
758
506
fprintf(stderr, "Error writing encrypted data: %s\n",
819
569
char ip[AVAHI_ADDRESS_STR_MAX];
820
570
avahi_address_snprint(ip, sizeof(ip), address);
822
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
823
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
824
ip, (intmax_t)interface, port);
572
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
573
" port %d\n", name, host_name, ip, port);
826
int ret = start_mandos_communication(ip, port, interface,
827
avahi_proto_to_af(proto));
829
avahi_simple_poll_quit(mc.simple_poll);
575
int ret = start_mandos_communication(ip, port, interface);
833
581
avahi_s_service_resolver_free(r);
836
static void browse_callback(AvahiSServiceBrowser *b,
837
AvahiIfIndex interface,
838
AvahiProtocol protocol,
839
AvahiBrowserEvent event,
843
AVAHI_GCC_UNUSED AvahiLookupResultFlags
845
AVAHI_GCC_UNUSED void* userdata){
848
/* Called whenever a new services becomes available on the LAN or
849
is removed from the LAN */
853
case AVAHI_BROWSER_FAILURE:
855
fprintf(stderr, "(Avahi browser) %s\n",
856
avahi_strerror(avahi_server_errno(mc.server)));
857
avahi_simple_poll_quit(mc.simple_poll);
860
case AVAHI_BROWSER_NEW:
861
/* We ignore the returned Avahi resolver object. In the callback
862
function we free it. If the Avahi server is terminated before
863
the callback function is called the Avahi server will free the
866
if(avahi_s_service_resolver_new(mc.server, interface, protocol,
867
name, type, domain, protocol, 0,
868
resolve_callback, NULL) == NULL)
869
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
870
name, avahi_strerror(avahi_server_errno(mc.server)));
873
case AVAHI_BROWSER_REMOVE:
876
case AVAHI_BROWSER_ALL_FOR_NOW:
877
case AVAHI_BROWSER_CACHE_EXHAUSTED:
879
fprintf(stderr, "No Mandos server found, still searching...\n");
584
static void browse_callback(
585
AvahiSServiceBrowser *b,
586
AvahiIfIndex interface,
587
AvahiProtocol protocol,
588
AvahiBrowserEvent event,
592
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
595
AvahiServer *s = userdata;
596
assert(b); /* Spurious warning */
598
/* Called whenever a new services becomes available on the LAN or
599
is removed from the LAN */
603
case AVAHI_BROWSER_FAILURE:
605
fprintf(stderr, "(Browser) %s\n",
606
avahi_strerror(avahi_server_errno(server)));
607
avahi_simple_poll_quit(simple_poll);
610
case AVAHI_BROWSER_NEW:
611
/* We ignore the returned resolver object. In the callback
612
function we free it. If the server is terminated before
613
the callback function is called the server will free
614
the resolver for us. */
616
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
618
AVAHI_PROTO_INET6, 0,
619
resolve_callback, s)))
620
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
621
avahi_strerror(avahi_server_errno(s)));
624
case AVAHI_BROWSER_REMOVE:
627
case AVAHI_BROWSER_ALL_FOR_NOW:
628
case AVAHI_BROWSER_CACHE_EXHAUSTED:
885
/* stop main loop after sigterm has been called */
886
static void handle_sigterm(int sig){
891
signal_received = sig;
892
int old_errno = errno;
893
if(mc.simple_poll != NULL){
894
avahi_simple_poll_quit(mc.simple_poll);
899
int main(int argc, char *argv[]){
900
AvahiSServiceBrowser *sb = NULL;
905
int exitcode = EXIT_SUCCESS;
906
const char *interface = "eth0";
907
struct ifreq network;
909
bool take_down_interface = false;
912
char *connect_to = NULL;
913
char tempdir[] = "/tmp/mandosXXXXXX";
914
bool tempdir_created = false;
915
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
916
const char *seckey = PATHDIR "/" SECKEY;
917
const char *pubkey = PATHDIR "/" PUBKEY;
919
bool gnutls_initialized = false;
920
bool gpgme_initialized = false;
923
struct sigaction old_sigterm_action;
924
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
927
struct argp_option options[] = {
928
{ .name = "debug", .key = 128,
929
.doc = "Debug mode", .group = 3 },
930
{ .name = "connect", .key = 'c',
931
.arg = "ADDRESS:PORT",
932
.doc = "Connect directly to a specific Mandos server",
934
{ .name = "interface", .key = 'i',
936
.doc = "Network interface that will be used to search for"
939
{ .name = "seckey", .key = 's',
941
.doc = "OpenPGP secret key file base name",
943
{ .name = "pubkey", .key = 'p',
945
.doc = "OpenPGP public key file base name",
947
{ .name = "dh-bits", .key = 129,
949
.doc = "Bit length of the prime number used in the"
950
" Diffie-Hellman key exchange",
952
{ .name = "priority", .key = 130,
954
.doc = "GnuTLS priority string for the TLS handshake",
956
{ .name = "delay", .key = 131,
958
.doc = "Maximum delay to wait for interface startup",
633
/* Combines file name and path and returns the malloced new
634
string. some sane checks could/should be added */
635
static const char *combinepath(const char *first, const char *second){
636
size_t f_len = strlen(first);
637
size_t s_len = strlen(second);
638
char *tmp = malloc(f_len + s_len + 2);
643
memcpy(tmp, first, f_len);
647
memcpy(tmp + f_len + 1, second, s_len);
649
tmp[f_len + 1 + s_len] = '\0';
654
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
655
AvahiServerConfig config;
656
AvahiSServiceBrowser *sb = NULL;
659
int returncode = EXIT_SUCCESS;
660
const char *interface = "eth0";
661
struct ifreq network;
663
char *connect_to = NULL;
664
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
963
error_t parse_opt(int key, char *arg,
964
struct argp_state *state){
966
case 128: /* --debug */
969
case 'c': /* --connect */
972
case 'i': /* --interface */
975
case 's': /* --seckey */
978
case 'p': /* --pubkey */
981
case 129: /* --dh-bits */
983
tmpmax = strtoimax(arg, &tmp, 10);
984
if(errno != 0 or tmp == arg or *tmp != '\0'
985
or tmpmax != (typeof(mc.dh_bits))tmpmax){
986
fprintf(stderr, "Bad number of DH bits\n");
989
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
991
case 130: /* --priority */
994
case 131: /* --delay */
996
delay = strtof(arg, &tmp);
997
if(errno != 0 or tmp == arg or *tmp != '\0'){
998
fprintf(stderr, "Bad delay\n");
667
static struct option long_options[] = {
668
{"debug", no_argument, (int *)&debug, 1},
669
{"connect", required_argument, 0, 'C'},
670
{"interface", required_argument, 0, 'i'},
671
{"certdir", required_argument, 0, 'd'},
672
{"certkey", required_argument, 0, 'c'},
673
{"certfile", required_argument, 0, 'k'},
676
int option_index = 0;
677
ret = getopt_long (argc, argv, "i:", long_options,
1007
return ARGP_ERR_UNKNOWN;
1012
struct argp argp = { .options = options, .parser = parse_opt,
1014
.doc = "Mandos client -- Get and decrypt"
1015
" passwords from a Mandos server" };
1016
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
1017
if(ret == ARGP_ERR_UNKNOWN){
1018
fprintf(stderr, "Unknown error while parsing arguments\n");
1019
exitcode = EXIT_FAILURE;
1025
avahi_set_log_function(empty_log);
1028
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1029
from the signal handler */
1030
/* Initialize the pseudo-RNG for Avahi */
1031
srand((unsigned int) time(NULL));
1032
mc.simple_poll = avahi_simple_poll_new();
1033
if(mc.simple_poll == NULL){
1034
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1035
exitcode = EXIT_FAILURE;
1039
sigemptyset(&sigterm_action.sa_mask);
1040
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1042
perror("sigaddset");
1043
exitcode = EXIT_FAILURE;
1046
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1048
perror("sigaddset");
1049
exitcode = EXIT_FAILURE;
1052
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1054
perror("sigaddset");
1055
exitcode = EXIT_FAILURE;
1058
/* Need to check if the handler is SIG_IGN before handling:
1059
| [[info:libc:Initial Signal Actions]] |
1060
| [[info:libc:Basic Signal Handling]] |
1062
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
1064
perror("sigaction");
1065
return EXIT_FAILURE;
1067
if(old_sigterm_action.sa_handler != SIG_IGN){
1068
ret = sigaction(SIGINT, &sigterm_action, NULL);
1070
perror("sigaction");
1071
exitcode = EXIT_FAILURE;
1075
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
1077
perror("sigaction");
1078
return EXIT_FAILURE;
1080
if(old_sigterm_action.sa_handler != SIG_IGN){
1081
ret = sigaction(SIGHUP, &sigterm_action, NULL);
1083
perror("sigaction");
1084
exitcode = EXIT_FAILURE;
1088
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
1090
perror("sigaction");
1091
return EXIT_FAILURE;
1093
if(old_sigterm_action.sa_handler != SIG_IGN){
1094
ret = sigaction(SIGTERM, &sigterm_action, NULL);
1096
perror("sigaction");
1097
exitcode = EXIT_FAILURE;
1102
/* If the interface is down, bring it up */
1103
if(interface[0] != '\0'){
707
certfile = combinepath(certdir, certfile);
708
if (certfile == NULL){
709
perror("combinepath");
710
returncode = EXIT_FAILURE;
714
certkey = combinepath(certdir, certkey);
715
if (certkey == NULL){
716
perror("combinepath");
717
returncode = EXIT_FAILURE;
1104
721
if_index = (AvahiIfIndex) if_nametoindex(interface);
1105
722
if(if_index == 0){
1106
723
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1107
exitcode = EXIT_FAILURE;
1116
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1117
messages to mess up the prompt */
1118
ret = klogctl(8, NULL, 5);
1119
bool restore_loglevel = true;
1121
restore_loglevel = false;
1124
#endif /* __linux__ */
727
if(connect_to != NULL){
728
/* Connect directly, do not use Zeroconf */
729
/* (Mainly meant for debugging) */
730
char *address = strrchr(connect_to, ':');
732
fprintf(stderr, "No colon in address\n");
736
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
738
perror("Bad port number");
742
address = connect_to;
743
ret = start_mandos_communication(address, port, if_index);
1126
751
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1128
753
perror("socket");
1129
exitcode = EXIT_FAILURE;
1131
if(restore_loglevel){
1132
ret = klogctl(7, NULL, 0);
1137
#endif /* __linux__ */
754
returncode = EXIT_FAILURE;
1140
strcpy(network.ifr_name, interface);
757
strcpy(network.ifr_name, interface);
1141
758
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1143
761
perror("ioctl SIOCGIFFLAGS");
1145
if(restore_loglevel){
1146
ret = klogctl(7, NULL, 0);
1151
#endif /* __linux__ */
1152
exitcode = EXIT_FAILURE;
762
returncode = EXIT_FAILURE;
1155
765
if((network.ifr_flags & IFF_UP) == 0){
1156
766
network.ifr_flags |= IFF_UP;
1157
take_down_interface = true;
1158
767
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1160
take_down_interface = false;
1161
769
perror("ioctl SIOCSIFFLAGS");
1162
exitcode = EXIT_FAILURE;
1164
if(restore_loglevel){
1165
ret = klogctl(7, NULL, 0);
1170
#endif /* __linux__ */
1174
/* sleep checking until interface is running */
1175
for(int i=0; i < delay * 4; i++){
1176
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1178
perror("ioctl SIOCGIFFLAGS");
1179
} else if(network.ifr_flags & IFF_RUNNING){
1182
struct timespec sleeptime = { .tv_nsec = 250000000 };
1183
ret = nanosleep(&sleeptime, NULL);
1184
if(ret == -1 and errno != EINTR){
1185
perror("nanosleep");
1188
if(not take_down_interface){
1189
/* We won't need the socket anymore */
1190
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1196
if(restore_loglevel){
1197
/* Restores kernel loglevel to default */
1198
ret = klogctl(7, NULL, 0);
1203
#endif /* __linux__ */
1228
ret = init_gnutls_global(pubkey, seckey);
1230
fprintf(stderr, "init_gnutls_global failed\n");
1231
exitcode = EXIT_FAILURE;
1234
gnutls_initialized = true;
1241
tempdir_created = true;
1242
if(mkdtemp(tempdir) == NULL){
1243
tempdir_created = false;
1252
if(not init_gpgme(pubkey, seckey, tempdir)){
1253
fprintf(stderr, "init_gpgme failed\n");
1254
exitcode = EXIT_FAILURE;
1257
gpgme_initialized = true;
1264
if(connect_to != NULL){
1265
/* Connect directly, do not use Zeroconf */
1266
/* (Mainly meant for debugging) */
1267
char *address = strrchr(connect_to, ':');
1268
if(address == NULL){
1269
fprintf(stderr, "No colon in address\n");
1270
exitcode = EXIT_FAILURE;
1280
tmpmax = strtoimax(address+1, &tmp, 10);
1281
if(errno != 0 or tmp == address+1 or *tmp != '\0'
1282
or tmpmax != (uint16_t)tmpmax){
1283
fprintf(stderr, "Bad port number\n");
1284
exitcode = EXIT_FAILURE;
1292
port = (uint16_t)tmpmax;
1294
address = connect_to;
1295
/* Colon in address indicates IPv6 */
1297
if(strchr(address, ':') != NULL){
1307
ret = start_mandos_communication(address, port, if_index, af);
1309
exitcode = EXIT_FAILURE;
1311
exitcode = EXIT_SUCCESS;
1321
AvahiServerConfig config;
1322
/* Do not publish any local Zeroconf records */
770
returncode = EXIT_FAILURE;
777
avahi_set_log_function(empty_log);
780
/* Initialize the psuedo-RNG */
781
srand((unsigned int) time(NULL));
783
/* Allocate main loop object */
784
if (!(simple_poll = avahi_simple_poll_new())) {
785
fprintf(stderr, "Failed to create simple poll object.\n");
786
returncode = EXIT_FAILURE;
790
/* Do not publish any local records */
1323
791
avahi_server_config_init(&config);
1324
792
config.publish_hinfo = 0;
1325
793
config.publish_addresses = 0;
1326
794
config.publish_workstation = 0;
1327
795
config.publish_domain = 0;
1329
797
/* Allocate a new server */
1330
mc.server = avahi_server_new(avahi_simple_poll_get
1331
(mc.simple_poll), &config, NULL,
1334
/* Free the Avahi configuration data */
798
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
799
&config, NULL, NULL, &error);
801
/* Free the configuration data */
1335
802
avahi_server_config_free(&config);
1338
/* Check if creating the Avahi server object succeeded */
1339
if(mc.server == NULL){
1340
fprintf(stderr, "Failed to create Avahi server: %s\n",
1341
avahi_strerror(error));
1342
exitcode = EXIT_FAILURE;
1350
/* Create the Avahi service browser */
1351
sb = avahi_s_service_browser_new(mc.server, if_index,
1352
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
1353
NULL, 0, browse_callback, NULL);
1355
fprintf(stderr, "Failed to create service browser: %s\n",
1356
avahi_strerror(avahi_server_errno(mc.server)));
1357
exitcode = EXIT_FAILURE;
1365
/* Run the main loop */
1368
fprintf(stderr, "Starting Avahi loop search\n");
1371
avahi_simple_poll_loop(mc.simple_poll);
1376
fprintf(stderr, "%s exiting\n", argv[0]);
1379
/* Cleanup things */
1381
avahi_s_service_browser_free(sb);
1383
if(mc.server != NULL)
1384
avahi_server_free(mc.server);
1386
if(mc.simple_poll != NULL)
1387
avahi_simple_poll_free(mc.simple_poll);
1389
if(gnutls_initialized){
1390
gnutls_certificate_free_credentials(mc.cred);
1391
gnutls_global_deinit();
1392
gnutls_dh_params_deinit(mc.dh_params);
1395
if(gpgme_initialized){
1396
gpgme_release(mc.ctx);
1399
/* Take down the network interface */
1400
if(take_down_interface){
1401
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1403
perror("ioctl SIOCGIFFLAGS");
1404
} else if(network.ifr_flags & IFF_UP) {
1405
network.ifr_flags &= ~IFF_UP; /* clear flag */
1406
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1408
perror("ioctl SIOCSIFFLAGS");
1411
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1417
/* Removes the temp directory used by GPGME */
1418
if(tempdir_created){
1420
struct dirent *direntry;
1421
d = opendir(tempdir);
1423
if(errno != ENOENT){
1428
direntry = readdir(d);
1429
if(direntry == NULL){
1432
/* Skip "." and ".." */
1433
if(direntry->d_name[0] == '.'
1434
and (direntry->d_name[1] == '\0'
1435
or (direntry->d_name[1] == '.'
1436
and direntry->d_name[2] == '\0'))){
1439
char *fullname = NULL;
1440
ret = asprintf(&fullname, "%s/%s", tempdir,
1446
ret = remove(fullname);
1448
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1455
ret = rmdir(tempdir);
1456
if(ret == -1 and errno != ENOENT){
1462
sigemptyset(&old_sigterm_action.sa_mask);
1463
old_sigterm_action.sa_handler = SIG_DFL;
1464
ret = sigaction(signal_received, &old_sigterm_action, NULL);
1466
perror("sigaction");
1468
raise(signal_received);
804
/* Check if creating the server object succeeded */
806
fprintf(stderr, "Failed to create server: %s\n",
807
avahi_strerror(error));
808
returncode = EXIT_FAILURE;
812
/* Create the service browser */
813
sb = avahi_s_service_browser_new(server, if_index,
815
"_mandos._tcp", NULL, 0,
816
browse_callback, server);
818
fprintf(stderr, "Failed to create service browser: %s\n",
819
avahi_strerror(avahi_server_errno(server)));
820
returncode = EXIT_FAILURE;
824
/* Run the main loop */
827
fprintf(stderr, "Starting avahi loop search\n");
830
avahi_simple_poll_loop(simple_poll);
835
fprintf(stderr, "%s exiting\n", argv[0]);
840
avahi_s_service_browser_free(sb);
843
avahi_server_free(server);
846
avahi_simple_poll_free(simple_poll);