31
32
/* Needed by GPGME, specifically gpgme_data_seek() */
33
#ifndef _LARGEFILE_SOURCE
32
34
#define _LARGEFILE_SOURCE
36
#ifndef _FILE_OFFSET_BITS
33
37
#define _FILE_OFFSET_BITS 64
39
#include <net/if.h> /* if_nametoindex */
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
41
94
#include <avahi-core/core.h>
42
95
#include <avahi-core/lookup.h>
43
96
#include <avahi-core/log.h>
45
98
#include <avahi-common/malloc.h>
46
99
#include <avahi-common/error.h>
49
#include <sys/types.h> /* socket(), inet_pton() */
50
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
51
struct in6_addr, inet_pton() */
52
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
53
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
55
#include <unistd.h> /* close() */
56
#include <netinet/in.h>
57
#include <stdbool.h> /* true */
58
#include <string.h> /* memset */
59
#include <arpa/inet.h> /* inet_pton() */
60
#include <iso646.h> /* not */
63
#include <errno.h> /* perror() */
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,
69
118
#define BUFFER_SIZE 256
72
const char *certdir = "/conf/conf.d/cryptkeyreq/";
73
const char *certfile = "openpgp-client.txt";
74
const char *certkey = "openpgp-client-key.txt";
120
#define PATHDIR "/conf/conf.d/mandos"
121
#define SECKEY "seckey.txt"
122
#define PUBKEY "pubkey.txt"
76
124
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
gnutls_session_t session;
131
AvahiSimplePoll *simple_poll;
80
133
gnutls_certificate_credentials_t cred;
134
unsigned int dh_bits;
81
135
gnutls_dh_params_t dh_params;
85
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
86
char **new_packet, const char *homedir){
87
gpgme_data_t dh_crypto, dh_plain;
136
const char *priority;
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){
91
ssize_t new_packet_capacity = 0;
92
ssize_t new_packet_length = 0;
93
168
gpgme_engine_info_t engine_info;
96
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
172
* Helper function to insert pub and seckey to the engine keyring.
174
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
212
gpgme_check_version(NULL);
101
213
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
102
if (rc != GPG_ERR_NO_ERROR){
214
if(rc != GPG_ERR_NO_ERROR){
103
215
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
104
216
gpgme_strsource(rc), gpgme_strerror(rc));
108
/* Set GPGME home directory */
109
rc = gpgme_get_engine_info (&engine_info);
110
if (rc != GPG_ERR_NO_ERROR){
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
223
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
112
224
gpgme_strsource(rc), gpgme_strerror(rc));
115
227
while(engine_info != NULL){
116
228
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
117
229
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
118
engine_info->file_name, homedir);
230
engine_info->file_name, tempdir);
121
233
engine_info = engine_info->next;
123
235
if(engine_info == NULL){
124
fprintf(stderr, "Could not set home dir to %s\n", homedir);
128
/* Create new GPGME data buffer from packet buffer */
129
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
130
if (rc != GPG_ERR_NO_ERROR){
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){
131
276
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
132
277
gpgme_strsource(rc), gpgme_strerror(rc));
194
/* Delete the GPGME FILE pointer cryptotext data buffer */
195
gpgme_data_release(dh_crypto);
327
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
197
330
/* Seek back to the beginning of the GPGME plaintext data buffer */
198
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
199
perror("pgpme_data_seek");
331
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
332
perror("gpgme_data_seek");
333
plaintext_length = -1;
204
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
205
*new_packet = realloc(*new_packet,
206
(unsigned int)new_packet_capacity
208
if (*new_packet == NULL){
212
new_packet_capacity += BUFFER_SIZE;
339
plaintext_capacity = incbuffer(plaintext,
340
(size_t)plaintext_length,
342
if(plaintext_capacity == 0){
344
plaintext_length = -1;
215
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
348
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
217
350
/* Print the data, if any */
222
356
perror("gpgme_data_read");
225
new_packet_length += ret;
228
/* FIXME: check characters before printing to screen so to not print
229
terminal control characters */
231
/* fprintf(stderr, "decrypted password is: "); */
232
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
233
/* fprintf(stderr, "\n"); */
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);
236
376
/* Delete the GPGME plaintext data buffer */
237
377
gpgme_data_release(dh_plain);
238
return new_packet_length;
378
return plaintext_length;
241
static const char * safer_gnutls_strerror (int value) {
242
const char *ret = gnutls_strerror (value);
381
static const char * safer_gnutls_strerror(int value){
382
const char *ret = gnutls_strerror(value); /* Spurious warning from
383
-Wunreachable-code */
244
385
ret = "(unknown)";
248
void debuggnutls(__attribute__((unused)) int level,
250
fprintf(stderr, "%s", string);
389
/* GnuTLS log function callback */
390
static void debuggnutls(__attribute__((unused)) int level,
392
fprintf(stderr, "GnuTLS: %s", string);
253
int initgnutls(encrypted_session *es){
395
static int init_gnutls_global(const char *pubkeyfilename,
396
const char *seckeyfilename){
258
400
fprintf(stderr, "Initializing GnuTLS\n");
261
if ((ret = gnutls_global_init ())
262
!= GNUTLS_E_SUCCESS) {
263
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
403
ret = gnutls_global_init();
404
if(ret != GNUTLS_E_SUCCESS){
405
fprintf(stderr, "GnuTLS global_init: %s\n",
406
safer_gnutls_strerror(ret));
411
/* "Use a log level over 10 to enable all debugging options."
268
414
gnutls_global_set_log_level(11);
269
415
gnutls_global_set_log_function(debuggnutls);
272
/* openpgp credentials */
273
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
274
!= GNUTLS_E_SUCCESS) {
275
fprintf (stderr, "memory error: %s\n",
276
safer_gnutls_strerror(ret));
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();
281
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
282
" and keyfile %s as GnuTLS credentials\n", certfile,
431
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
432
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
286
436
ret = gnutls_certificate_set_openpgp_key_file
287
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
288
if (ret != GNUTLS_E_SUCCESS) {
290
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
292
ret, certfile, certkey);
293
fprintf(stdout, "The Error is: %s\n",
294
safer_gnutls_strerror(ret));
298
//GnuTLS server initialization
299
if ((ret = gnutls_dh_params_init (&es->dh_params))
300
!= GNUTLS_E_SUCCESS) {
301
fprintf (stderr, "Error in dh parameter initialization: %s\n",
302
safer_gnutls_strerror(ret));
306
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
307
!= GNUTLS_E_SUCCESS) {
308
fprintf (stderr, "Error in prime generation: %s\n",
309
safer_gnutls_strerror(ret));
313
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
315
// GnuTLS session creation
316
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
317
!= GNUTLS_E_SUCCESS){
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){
318
479
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
319
480
safer_gnutls_strerror(ret));
322
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
323
!= GNUTLS_E_SUCCESS) {
324
fprintf(stderr, "Syntax error at: %s\n", err);
325
fprintf(stderr, "GnuTLS error: %s\n",
326
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);
330
if ((ret = gnutls_credentials_set
331
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
332
!= GNUTLS_E_SUCCESS) {
333
fprintf(stderr, "Error setting a credentials set: %s\n",
495
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
497
if(ret != GNUTLS_E_SUCCESS){
498
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
334
499
safer_gnutls_strerror(ret));
500
gnutls_deinit(*session);
338
504
/* ignore client certificate if any. */
339
gnutls_certificate_server_set_request (es->session,
505
gnutls_certificate_server_set_request(*session,
342
gnutls_dh_set_prime_bits (es->session, DH_BITS);
508
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
347
void empty_log(__attribute__((unused)) AvahiLogLevel level,
348
__attribute__((unused)) const char *txt){}
350
int start_mandos_communication(const char *ip, uint16_t port,
351
AvahiIfIndex if_index){
353
struct sockaddr_in6 to;
354
encrypted_session es;
513
/* Avahi log function callback */
514
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
515
__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
static int start_mandos_communication(const char *ip, uint16_t port,
522
AvahiIfIndex if_index,
524
int ret, tcp_sd = -1;
527
struct sockaddr_in in;
528
struct sockaddr_in6 in6;
355
530
char *buffer = NULL;
356
531
char *decrypted_buffer;
357
532
size_t buffer_length = 0;
358
533
size_t buffer_capacity = 0;
359
ssize_t decrypted_buffer_size;
362
char interface[IF_NAMESIZE];
536
gnutls_session_t session;
537
int pf; /* Protocol family */
551
fprintf(stderr, "Bad address family: %d\n", af);
555
ret = init_gnutls_session(&session);
365
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
561
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
369
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
565
tcp_sd = socket(pf, SOCK_STREAM, 0);
371
567
perror("socket");
375
if(if_indextoname((unsigned int)if_index, interface) == NULL){
377
perror("if_indextoname");
383
fprintf(stderr, "Binding to interface %s\n", interface);
386
memset(&to,0,sizeof(to)); /* Spurious warning */
387
to.sin6_family = AF_INET6;
388
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
576
memset(&to, 0, sizeof(to));
578
to.in6.sin6_family = (sa_family_t)af;
579
ret = inet_pton(af, ip, &to.in6.sin6_addr);
581
to.in.sin_family = (sa_family_t)af;
582
ret = inet_pton(af, ip, &to.in.sin_addr);
390
585
perror("inet_pton");
394
590
fprintf(stderr, "Bad address: %s\n", ip);
397
to.sin6_port = htons(port); /* Spurious warning */
595
to.in6.sin6_port = htons(port); /* Spurious warnings from
597
-Wunreachable-code */
599
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
600
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
602
if(if_index == AVAHI_IF_UNSPEC){
603
fprintf(stderr, "An IPv6 link-local address is incomplete"
604
" without a network interface\n");
608
/* Set the network interface number as scope */
609
to.in6.sin6_scope_id = (uint32_t)if_index;
612
to.in.sin_port = htons(port); /* Spurious warnings from
614
-Wunreachable-code */
399
to.sin6_scope_id = (uint32_t)if_index;
402
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
403
/* char addrstr[INET6_ADDRSTRLEN]; */
404
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
405
/* sizeof(addrstr)) == NULL){ */
406
/* perror("inet_ntop"); */
408
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
409
/* addrstr, ntohs(to.sin6_port)); */
413
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
622
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
623
char interface[IF_NAMESIZE];
624
if(if_indextoname((unsigned int)if_index, interface) == NULL){
625
perror("if_indextoname");
627
fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
628
ip, interface, port);
631
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
634
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
635
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
638
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
641
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
647
if(strcmp(addrstr, ip) != 0){
648
fprintf(stderr, "Canonical address form: %s\n", addrstr);
658
ret = connect(tcp_sd, &to.in6, sizeof(to));
660
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
415
663
perror("connect");
419
ret = initgnutls (&es);
425
gnutls_transport_set_ptr (es.session,
426
(gnutls_transport_ptr_t) tcp_sd);
672
const char *out = mandos_protocol_version;
675
size_t out_size = strlen(out);
676
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
677
out_size - written));
683
written += (size_t)ret;
684
if(written < out_size){
687
if(out == mandos_protocol_version){
429
701
fprintf(stderr, "Establishing TLS session with %s\n", ip);
432
ret = gnutls_handshake (es.session);
434
if (ret != GNUTLS_E_SUCCESS){
708
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
715
ret = gnutls_handshake(session);
719
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
721
if(ret != GNUTLS_E_SUCCESS){
436
fprintf(stderr, "\n*** Handshake failed ***\n");
723
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
443
//Retrieve OpenPGP packet that contains the wanted password
730
/* Read OpenPGP packet that contains the wanted password */
446
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
733
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
451
if (buffer_length + BUFFER_SIZE > buffer_capacity){
452
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
457
buffer_capacity += BUFFER_SIZE;
460
ret = gnutls_record_recv
461
(es.session, buffer+buffer_length, BUFFER_SIZE);
743
buffer_capacity = incbuffer(&buffer, buffer_length,
745
if(buffer_capacity == 0){
755
sret = gnutls_record_recv(session, buffer+buffer_length,
467
762
case GNUTLS_E_INTERRUPTED:
468
763
case GNUTLS_E_AGAIN:
470
765
case GNUTLS_E_REHANDSHAKE:
471
ret = gnutls_handshake (es.session);
473
fprintf(stderr, "\n*** Handshake failed ***\n");
767
ret = gnutls_handshake(session);
772
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
774
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
480
781
fprintf(stderr, "Unknown error while reading data from"
481
" encrypted session with mandos server\n");
782
" encrypted session with Mandos server\n");
483
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
784
gnutls_bye(session, GNUTLS_SHUT_RDWR);
487
buffer_length += (size_t) ret;
788
buffer_length += (size_t) sret;
491
if (buffer_length > 0){
793
fprintf(stderr, "Closing TLS session\n");
800
gnutls_bye(session, GNUTLS_SHUT_RDWR);
806
if(buffer_length > 0){
807
ssize_t decrypted_buffer_size;
492
808
decrypted_buffer_size = pgp_packet_decrypt(buffer,
496
if (decrypted_buffer_size >= 0){
811
if(decrypted_buffer_size >= 0){
497
814
while(written < (size_t) decrypted_buffer_size){
498
ret = (int)fwrite (decrypted_buffer + written, 1,
499
(size_t)decrypted_buffer_size - written,
819
ret = (int)fwrite(decrypted_buffer + written, 1,
820
(size_t)decrypted_buffer_size - written,
501
822
if(ret == 0 and ferror(stdout)){
503
824
fprintf(stderr, "Error writing encrypted data: %s\n",
566
890
char ip[AVAHI_ADDRESS_STR_MAX];
567
891
avahi_address_snprint(ip, sizeof(ip), address);
569
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
570
" port %d\n", name, host_name, ip, port);
893
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
894
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
895
ip, (intmax_t)interface, port);
572
int ret = start_mandos_communication(ip, port, interface);
897
int ret = start_mandos_communication(ip, port, interface,
898
avahi_proto_to_af(proto));
900
avahi_simple_poll_quit(mc.simple_poll);
578
904
avahi_s_service_resolver_free(r);
581
static void browse_callback(
582
AvahiSServiceBrowser *b,
583
AvahiIfIndex interface,
584
AvahiProtocol protocol,
585
AvahiBrowserEvent event,
589
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
592
AvahiServer *s = userdata;
593
assert(b); /* Spurious warning */
595
/* Called whenever a new services becomes available on the LAN or
596
is removed from the LAN */
600
case AVAHI_BROWSER_FAILURE:
602
fprintf(stderr, "(Browser) %s\n",
603
avahi_strerror(avahi_server_errno(server)));
604
avahi_simple_poll_quit(simple_poll);
607
case AVAHI_BROWSER_NEW:
608
/* We ignore the returned resolver object. In the callback
609
function we free it. If the server is terminated before
610
the callback function is called the server will free
611
the resolver for us. */
613
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
615
AVAHI_PROTO_INET6, 0,
616
resolve_callback, s)))
617
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
618
avahi_strerror(avahi_server_errno(s)));
621
case AVAHI_BROWSER_REMOVE:
624
case AVAHI_BROWSER_ALL_FOR_NOW:
625
case AVAHI_BROWSER_CACHE_EXHAUSTED:
907
static void browse_callback(AvahiSServiceBrowser *b,
908
AvahiIfIndex interface,
909
AvahiProtocol protocol,
910
AvahiBrowserEvent event,
914
AVAHI_GCC_UNUSED AvahiLookupResultFlags
916
AVAHI_GCC_UNUSED void* userdata){
919
/* Called whenever a new services becomes available on the LAN or
920
is removed from the LAN */
928
case AVAHI_BROWSER_FAILURE:
930
fprintf(stderr, "(Avahi browser) %s\n",
931
avahi_strerror(avahi_server_errno(mc.server)));
932
avahi_simple_poll_quit(mc.simple_poll);
935
case AVAHI_BROWSER_NEW:
936
/* We ignore the returned Avahi resolver object. In the callback
937
function we free it. If the Avahi server is terminated before
938
the callback function is called the Avahi server will free the
941
if(avahi_s_service_resolver_new(mc.server, interface, protocol,
942
name, type, domain, protocol, 0,
943
resolve_callback, NULL) == NULL)
944
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
945
name, avahi_strerror(avahi_server_errno(mc.server)));
948
case AVAHI_BROWSER_REMOVE:
951
case AVAHI_BROWSER_ALL_FOR_NOW:
952
case AVAHI_BROWSER_CACHE_EXHAUSTED:
954
fprintf(stderr, "No Mandos server found, still searching...\n");
630
/* combinds file name and path and returns the malloced new string. som sane checks could/should be added */
631
const char *combinepath(const char *first, const char *second){
960
/* stop main loop after sigterm has been called */
961
static void handle_sigterm(int sig){
966
signal_received = sig;
967
int old_errno = errno;
968
if(mc.simple_poll != NULL){
969
avahi_simple_poll_quit(mc.simple_poll);
974
int main(int argc, char *argv[]){
975
AvahiSServiceBrowser *sb = NULL;
633
tmp = malloc(strlen(first) + strlen(second) + 2);
639
if (first[0] != '\0' and first[strlen(first) - 1] != '/'){
647
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
980
int exitcode = EXIT_SUCCESS;
981
const char *interface = "eth0";
982
struct ifreq network;
984
bool take_down_interface = false;
987
char *connect_to = NULL;
988
char tempdir[] = "/tmp/mandosXXXXXX";
989
bool tempdir_created = false;
990
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
991
const char *seckey = PATHDIR "/" SECKEY;
992
const char *pubkey = PATHDIR "/" PUBKEY;
994
bool gnutls_initialized = false;
995
bool gpgme_initialized = false;
998
struct sigaction old_sigterm_action;
999
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
1002
struct argp_option options[] = {
1003
{ .name = "debug", .key = 128,
1004
.doc = "Debug mode", .group = 3 },
1005
{ .name = "connect", .key = 'c',
1006
.arg = "ADDRESS:PORT",
1007
.doc = "Connect directly to a specific Mandos server",
1009
{ .name = "interface", .key = 'i',
1011
.doc = "Network interface that will be used to search for"
1014
{ .name = "seckey", .key = 's',
1016
.doc = "OpenPGP secret key file base name",
1018
{ .name = "pubkey", .key = 'p',
1020
.doc = "OpenPGP public key file base name",
1022
{ .name = "dh-bits", .key = 129,
1024
.doc = "Bit length of the prime number used in the"
1025
" Diffie-Hellman key exchange",
1027
{ .name = "priority", .key = 130,
1029
.doc = "GnuTLS priority string for the TLS handshake",
1031
{ .name = "delay", .key = 131,
1033
.doc = "Maximum delay to wait for interface startup",
1038
error_t parse_opt(int key, char *arg,
1039
struct argp_state *state){
1041
case 128: /* --debug */
1044
case 'c': /* --connect */
1047
case 'i': /* --interface */
1050
case 's': /* --seckey */
1053
case 'p': /* --pubkey */
1056
case 129: /* --dh-bits */
1058
tmpmax = strtoimax(arg, &tmp, 10);
1059
if(errno != 0 or tmp == arg or *tmp != '\0'
1060
or tmpmax != (typeof(mc.dh_bits))tmpmax){
1061
fprintf(stderr, "Bad number of DH bits\n");
1064
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
1066
case 130: /* --priority */
1069
case 131: /* --delay */
1071
delay = strtof(arg, &tmp);
1072
if(errno != 0 or tmp == arg or *tmp != '\0'){
1073
fprintf(stderr, "Bad delay\n");
1082
return ARGP_ERR_UNKNOWN;
1087
struct argp argp = { .options = options, .parser = parse_opt,
1089
.doc = "Mandos client -- Get and decrypt"
1090
" passwords from a Mandos server" };
1091
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
1092
if(ret == ARGP_ERR_UNKNOWN){
1093
fprintf(stderr, "Unknown error while parsing arguments\n");
1094
exitcode = EXIT_FAILURE;
1100
avahi_set_log_function(empty_log);
1103
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1104
from the signal handler */
1105
/* Initialize the pseudo-RNG for Avahi */
1106
srand((unsigned int) time(NULL));
1107
mc.simple_poll = avahi_simple_poll_new();
1108
if(mc.simple_poll == NULL){
1109
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1110
exitcode = EXIT_FAILURE;
1114
sigemptyset(&sigterm_action.sa_mask);
1115
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1117
perror("sigaddset");
1118
exitcode = EXIT_FAILURE;
1121
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1123
perror("sigaddset");
1124
exitcode = EXIT_FAILURE;
1127
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1129
perror("sigaddset");
1130
exitcode = EXIT_FAILURE;
1133
/* Need to check if the handler is SIG_IGN before handling:
1134
| [[info:libc:Initial Signal Actions]] |
1135
| [[info:libc:Basic Signal Handling]] |
1137
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
1139
perror("sigaction");
1140
return EXIT_FAILURE;
1142
if(old_sigterm_action.sa_handler != SIG_IGN){
1143
ret = sigaction(SIGINT, &sigterm_action, NULL);
1145
perror("sigaction");
1146
exitcode = EXIT_FAILURE;
1150
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
1152
perror("sigaction");
1153
return EXIT_FAILURE;
1155
if(old_sigterm_action.sa_handler != SIG_IGN){
1156
ret = sigaction(SIGHUP, &sigterm_action, NULL);
1158
perror("sigaction");
1159
exitcode = EXIT_FAILURE;
1163
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
1165
perror("sigaction");
1166
return EXIT_FAILURE;
1168
if(old_sigterm_action.sa_handler != SIG_IGN){
1169
ret = sigaction(SIGTERM, &sigterm_action, NULL);
1171
perror("sigaction");
1172
exitcode = EXIT_FAILURE;
1177
/* If the interface is down, bring it up */
1178
if(interface[0] != '\0'){
1179
if_index = (AvahiIfIndex) if_nametoindex(interface);
1181
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1182
exitcode = EXIT_FAILURE;
1191
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1192
messages to mess up the prompt */
1193
ret = klogctl(8, NULL, 5);
1194
bool restore_loglevel = true;
1196
restore_loglevel = false;
1199
#endif /* __linux__ */
1201
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1204
exitcode = EXIT_FAILURE;
1206
if(restore_loglevel){
1207
ret = klogctl(7, NULL, 0);
1212
#endif /* __linux__ */
1215
strcpy(network.ifr_name, interface);
1216
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1218
perror("ioctl SIOCGIFFLAGS");
1220
if(restore_loglevel){
1221
ret = klogctl(7, NULL, 0);
1226
#endif /* __linux__ */
1227
exitcode = EXIT_FAILURE;
1230
if((network.ifr_flags & IFF_UP) == 0){
1231
network.ifr_flags |= IFF_UP;
1232
take_down_interface = true;
1233
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1235
take_down_interface = false;
1236
perror("ioctl SIOCSIFFLAGS");
1237
exitcode = EXIT_FAILURE;
1239
if(restore_loglevel){
1240
ret = klogctl(7, NULL, 0);
1245
#endif /* __linux__ */
1249
/* sleep checking until interface is running */
1250
for(int i=0; i < delay * 4; i++){
1251
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1253
perror("ioctl SIOCGIFFLAGS");
1254
} else if(network.ifr_flags & IFF_RUNNING){
1257
struct timespec sleeptime = { .tv_nsec = 250000000 };
1258
ret = nanosleep(&sleeptime, NULL);
1259
if(ret == -1 and errno != EINTR){
1260
perror("nanosleep");
1263
if(not take_down_interface){
1264
/* We won't need the socket anymore */
1265
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1271
if(restore_loglevel){
1272
/* Restores kernel loglevel to default */
1273
ret = klogctl(7, NULL, 0);
1278
#endif /* __linux__ */
1303
ret = init_gnutls_global(pubkey, seckey);
1305
fprintf(stderr, "init_gnutls_global failed\n");
1306
exitcode = EXIT_FAILURE;
1309
gnutls_initialized = true;
1316
tempdir_created = true;
1317
if(mkdtemp(tempdir) == NULL){
1318
tempdir_created = false;
1327
if(not init_gpgme(pubkey, seckey, tempdir)){
1328
fprintf(stderr, "init_gpgme failed\n");
1329
exitcode = EXIT_FAILURE;
1332
gpgme_initialized = true;
1339
if(connect_to != NULL){
1340
/* Connect directly, do not use Zeroconf */
1341
/* (Mainly meant for debugging) */
1342
char *address = strrchr(connect_to, ':');
1343
if(address == NULL){
1344
fprintf(stderr, "No colon in address\n");
1345
exitcode = EXIT_FAILURE;
1355
tmpmax = strtoimax(address+1, &tmp, 10);
1356
if(errno != 0 or tmp == address+1 or *tmp != '\0'
1357
or tmpmax != (uint16_t)tmpmax){
1358
fprintf(stderr, "Bad port number\n");
1359
exitcode = EXIT_FAILURE;
1367
port = (uint16_t)tmpmax;
1369
address = connect_to;
1370
/* Colon in address indicates IPv6 */
1372
if(strchr(address, ':') != NULL){
1382
ret = start_mandos_communication(address, port, if_index, af);
1384
exitcode = EXIT_FAILURE;
1386
exitcode = EXIT_SUCCESS;
648
1396
AvahiServerConfig config;
649
AvahiSServiceBrowser *sb = NULL;
652
int returncode = EXIT_SUCCESS;
653
const char *interface = NULL;
654
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
655
char *connect_to = NULL;
658
static struct option long_options[] = {
659
{"debug", no_argument, (int *)&debug, 1},
660
{"connect", required_argument, 0, 'C'},
661
{"interface", required_argument, 0, 'i'},
662
{"certdir", required_argument, 0, 'd'},
663
{"certkey", required_argument, 0, 'c'},
664
{"certfile", required_argument, 0, 'k'},
667
int option_index = 0;
668
ret = getopt_long (argc, argv, "i:", long_options,
698
certfile = combinepath(certdir, certfile);
699
if (certfile == NULL){
703
if(interface != NULL){
704
if_index = (AvahiIfIndex) if_nametoindex(interface);
706
fprintf(stderr, "No such interface: \"%s\"\n", interface);
711
if(connect_to != NULL){
712
/* Connect directly, do not use Zeroconf */
713
/* (Mainly meant for debugging) */
714
char *address = strrchr(connect_to, ':');
716
fprintf(stderr, "No colon in address\n");
720
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
722
perror("Bad port number");
726
address = connect_to;
727
ret = start_mandos_communication(address, port, if_index);
735
certkey = combinepath(certdir, certkey);
736
if (certkey == NULL){
741
avahi_set_log_function(empty_log);
744
/* Initialize the psuedo-RNG */
745
srand((unsigned int) time(NULL));
747
/* Allocate main loop object */
748
if (!(simple_poll = avahi_simple_poll_new())) {
749
fprintf(stderr, "Failed to create simple poll object.\n");
754
/* Do not publish any local records */
1397
/* Do not publish any local Zeroconf records */
755
1398
avahi_server_config_init(&config);
756
1399
config.publish_hinfo = 0;
757
1400
config.publish_addresses = 0;
758
1401
config.publish_workstation = 0;
759
1402
config.publish_domain = 0;
761
1404
/* Allocate a new server */
762
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
763
&config, NULL, NULL, &error);
765
/* Free the configuration data */
1405
mc.server = avahi_server_new(avahi_simple_poll_get
1406
(mc.simple_poll), &config, NULL,
1409
/* Free the Avahi configuration data */
766
1410
avahi_server_config_free(&config);
768
/* Check if creating the server object succeeded */
770
fprintf(stderr, "Failed to create server: %s\n",
771
avahi_strerror(error));
772
returncode = EXIT_FAILURE;
776
/* Create the service browser */
777
sb = avahi_s_service_browser_new(server, if_index,
779
"_mandos._tcp", NULL, 0,
780
browse_callback, server);
782
fprintf(stderr, "Failed to create service browser: %s\n",
783
avahi_strerror(avahi_server_errno(server)));
784
returncode = EXIT_FAILURE;
788
/* Run the main loop */
791
fprintf(stderr, "Starting avahi loop search\n");
794
avahi_simple_poll_loop(simple_poll);
799
fprintf(stderr, "%s exiting\n", argv[0]);
804
avahi_s_service_browser_free(sb);
807
avahi_server_free(server);
810
avahi_simple_poll_free(simple_poll);
1413
/* Check if creating the Avahi server object succeeded */
1414
if(mc.server == NULL){
1415
fprintf(stderr, "Failed to create Avahi server: %s\n",
1416
avahi_strerror(error));
1417
exitcode = EXIT_FAILURE;
1425
/* Create the Avahi service browser */
1426
sb = avahi_s_service_browser_new(mc.server, if_index,
1427
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
1428
NULL, 0, browse_callback, NULL);
1430
fprintf(stderr, "Failed to create service browser: %s\n",
1431
avahi_strerror(avahi_server_errno(mc.server)));
1432
exitcode = EXIT_FAILURE;
1440
/* Run the main loop */
1443
fprintf(stderr, "Starting Avahi loop search\n");
1446
avahi_simple_poll_loop(mc.simple_poll);
1451
fprintf(stderr, "%s exiting\n", argv[0]);
1454
/* Cleanup things */
1456
avahi_s_service_browser_free(sb);
1458
if(mc.server != NULL)
1459
avahi_server_free(mc.server);
1461
if(mc.simple_poll != NULL)
1462
avahi_simple_poll_free(mc.simple_poll);
1464
if(gnutls_initialized){
1465
gnutls_certificate_free_credentials(mc.cred);
1466
gnutls_global_deinit();
1467
gnutls_dh_params_deinit(mc.dh_params);
1470
if(gpgme_initialized){
1471
gpgme_release(mc.ctx);
1474
/* Take down the network interface */
1475
if(take_down_interface){
1476
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1478
perror("ioctl SIOCGIFFLAGS");
1479
} else if(network.ifr_flags & IFF_UP) {
1480
network.ifr_flags &= ~IFF_UP; /* clear flag */
1481
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1483
perror("ioctl SIOCSIFFLAGS");
1486
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1492
/* Removes the temp directory used by GPGME */
1493
if(tempdir_created){
1495
struct dirent *direntry;
1496
d = opendir(tempdir);
1498
if(errno != ENOENT){
1503
direntry = readdir(d);
1504
if(direntry == NULL){
1507
/* Skip "." and ".." */
1508
if(direntry->d_name[0] == '.'
1509
and (direntry->d_name[1] == '\0'
1510
or (direntry->d_name[1] == '.'
1511
and direntry->d_name[2] == '\0'))){
1514
char *fullname = NULL;
1515
ret = asprintf(&fullname, "%s/%s", tempdir,
1521
ret = remove(fullname);
1523
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1530
ret = rmdir(tempdir);
1531
if(ret == -1 and errno != ENOENT){
1537
sigemptyset(&old_sigterm_action.sa_mask);
1538
old_sigterm_action.sa_handler = SIG_DFL;
1539
ret = sigaction(signal_received, &old_sigterm_action, NULL);
1541
perror("sigaction");
1543
raise(signal_received);