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
#include <sys/ioctl.h> // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
41
#include <net/if.h> // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
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
43
94
#include <avahi-core/core.h>
44
95
#include <avahi-core/lookup.h>
45
96
#include <avahi-core/log.h>
47
98
#include <avahi-common/malloc.h>
48
99
#include <avahi-common/error.h>
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() */
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,
71
118
#define BUFFER_SIZE 256
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";
120
#define PATHDIR "/conf/conf.d/mandos"
121
#define SECKEY "seckey.txt"
122
#define PUBKEY "pubkey.txt"
78
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 */
81
131
AvahiSimplePoll *simple_poll;
82
132
AvahiServer *server;
83
133
gnutls_certificate_credentials_t cred;
84
134
unsigned int dh_bits;
135
gnutls_dh_params_t dh_params;
85
136
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;
97
169
gpgme_engine_info_t engine_info;
100
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
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");
104
212
gpgme_check_version(NULL);
105
213
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
106
if (rc != GPG_ERR_NO_ERROR){
214
if(rc != GPG_ERR_NO_ERROR){
107
215
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
108
216
gpgme_strsource(rc), gpgme_strerror(rc));
112
/* Set GPGME home directory */
113
rc = gpgme_get_engine_info (&engine_info);
114
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){
115
223
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
116
224
gpgme_strsource(rc), gpgme_strerror(rc));
119
227
while(engine_info != NULL){
120
228
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
121
229
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
122
engine_info->file_name, homedir);
230
engine_info->file_name, tempdir);
125
233
engine_info = engine_info->next;
127
235
if(engine_info == NULL){
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){
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){
135
276
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
136
277
gpgme_strsource(rc), gpgme_strerror(rc));
198
/* Delete the GPGME FILE pointer cryptotext data buffer */
199
gpgme_data_release(dh_crypto);
327
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
201
330
/* Seek back to the beginning of the GPGME plaintext data buffer */
202
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
203
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;
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;
339
plaintext_capacity = incbuffer(plaintext,
340
(size_t)plaintext_length,
342
if(plaintext_capacity == 0){
344
plaintext_length = -1;
219
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
348
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
221
350
/* Print the data, if any */
226
356
perror("gpgme_data_read");
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"); */
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);
240
376
/* Delete the GPGME plaintext data buffer */
241
377
gpgme_data_release(dh_plain);
242
return new_packet_length;
378
return plaintext_length;
245
static const char * safer_gnutls_strerror (int value) {
246
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 */
248
385
ret = "(unknown)";
389
/* GnuTLS log function callback */
252
390
static void debuggnutls(__attribute__((unused)) int level,
253
391
const char* string){
254
fprintf(stderr, "%s", string);
392
fprintf(stderr, "GnuTLS: %s", string);
257
static int initgnutls(mandos_context *mc){
395
static int init_gnutls_global(const char *pubkeyfilename,
396
const char *seckeyfilename){
262
400
fprintf(stderr, "Initializing GnuTLS\n");
265
if ((ret = gnutls_global_init ())
266
!= GNUTLS_E_SUCCESS) {
267
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."
272
414
gnutls_global_set_log_level(11);
273
415
gnutls_global_set_log_function(debuggnutls);
276
/* openpgp credentials */
277
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
278
!= GNUTLS_E_SUCCESS) {
279
fprintf (stderr, "memory error: %s\n",
280
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();
285
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
286
" 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,
290
436
ret = gnutls_certificate_set_openpgp_key_file
291
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
292
if (ret != GNUTLS_E_SUCCESS) {
294
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
296
ret, certfile, certkey);
297
fprintf(stdout, "The Error is: %s\n",
298
safer_gnutls_strerror(ret));
302
//GnuTLS server initialization
303
if ((ret = gnutls_dh_params_init (&es->dh_params))
304
!= GNUTLS_E_SUCCESS) {
305
fprintf (stderr, "Error in dh parameter initialization: %s\n",
306
safer_gnutls_strerror(ret));
310
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
311
!= GNUTLS_E_SUCCESS) {
312
fprintf (stderr, "Error in prime generation: %s\n",
313
safer_gnutls_strerror(ret));
317
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
319
// GnuTLS session creation
320
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
321
!= 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){
322
479
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
323
480
safer_gnutls_strerror(ret));
326
if ((ret = gnutls_priority_set_direct (es->session, mc->priority, &err))
327
!= GNUTLS_E_SUCCESS) {
328
fprintf(stderr, "Syntax error at: %s\n", err);
329
fprintf(stderr, "GnuTLS error: %s\n",
330
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);
334
if ((ret = gnutls_credentials_set
335
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
336
!= GNUTLS_E_SUCCESS) {
337
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",
338
499
safer_gnutls_strerror(ret));
500
gnutls_deinit(*session);
342
504
/* ignore client certificate if any. */
343
gnutls_certificate_server_set_request (es->session,
505
gnutls_certificate_server_set_request(*session,
346
gnutls_dh_set_prime_bits (es->session, DH_BITS);
508
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
513
/* Avahi log function callback */
351
514
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
352
515
__attribute__((unused)) const char *txt){}
517
/* Called when a Mandos server is found */
354
518
static int start_mandos_communication(const char *ip, uint16_t port,
355
519
AvahiIfIndex if_index,
358
struct sockaddr_in6 to;
359
encrypted_session es;
524
struct sockaddr_in in;
525
struct sockaddr_in6 in6;
360
527
char *buffer = NULL;
361
528
char *decrypted_buffer;
362
529
size_t buffer_length = 0;
363
530
size_t buffer_capacity = 0;
364
531
ssize_t decrypted_buffer_size;
367
char interface[IF_NAMESIZE];
534
gnutls_session_t session;
535
int pf; /* Protocol family */
545
fprintf(stderr, "Bad address family: %d\n", af);
549
ret = init_gnutls_session(&session);
370
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
555
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
374
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
559
tcp_sd = socket(pf, SOCK_STREAM, 0);
376
561
perror("socket");
381
if(if_indextoname((unsigned int)if_index, interface) == NULL){
383
perror("if_indextoname");
388
fprintf(stderr, "Binding to interface %s\n", interface);
391
memset(&to,0,sizeof(to)); /* Spurious warning */
392
to.sin6_family = AF_INET6;
393
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
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);
395
574
perror("inet_pton");
399
578
fprintf(stderr, "Bad address: %s\n", ip);
402
to.sin6_port = htons(port); /* Spurious warning */
404
to.sin6_scope_id = (uint32_t)if_index;
582
to.in6.sin6_port = htons(port); /* Spurious warnings from
584
-Wunreachable-code */
586
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
587
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
589
if(if_index == AVAHI_IF_UNSPEC){
590
fprintf(stderr, "An IPv6 link-local address is incomplete"
591
" without a network interface\n");
594
/* Set the network interface number as scope */
595
to.in6.sin6_scope_id = (uint32_t)if_index;
598
to.in.sin_port = htons(port); /* Spurious warnings from
600
-Wunreachable-code */
407
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
408
/* char addrstr[INET6_ADDRSTRLEN]; */
409
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
410
/* sizeof(addrstr)) == NULL){ */
411
/* perror("inet_ntop"); */
413
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
414
/* addrstr, ntohs(to.sin6_port)); */
604
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
605
char interface[IF_NAMESIZE];
606
if(if_indextoname((unsigned int)if_index, interface) == NULL){
607
perror("if_indextoname");
609
fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
610
ip, interface, port);
613
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
616
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
617
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
620
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
623
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
629
if(strcmp(addrstr, ip) != 0){
630
fprintf(stderr, "Canonical address form: %s\n", addrstr);
418
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
636
ret = connect(tcp_sd, &to.in6, sizeof(to));
638
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
420
641
perror("connect");
424
ret = initgnutls (&es);
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){
430
gnutls_transport_set_ptr (es.session,
431
(gnutls_transport_ptr_t) tcp_sd);
434
670
fprintf(stderr, "Establishing TLS session with %s\n", ip);
437
ret = gnutls_handshake (es.session);
439
if (ret != GNUTLS_E_SUCCESS){
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){
441
fprintf(stderr, "\n*** Handshake failed ***\n");
681
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
448
//Retrieve OpenPGP packet that contains the wanted password
688
/* Read OpenPGP packet that contains the wanted password */
451
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
691
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
456
if (buffer_length + BUFFER_SIZE > buffer_capacity){
457
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
462
buffer_capacity += BUFFER_SIZE;
696
buffer_capacity = incbuffer(&buffer, buffer_length,
698
if(buffer_capacity == 0){
465
ret = gnutls_record_recv
466
(es.session, buffer+buffer_length, BUFFER_SIZE);
704
sret = gnutls_record_recv(session, buffer+buffer_length,
472
711
case GNUTLS_E_INTERRUPTED:
473
712
case GNUTLS_E_AGAIN:
475
714
case GNUTLS_E_REHANDSHAKE:
476
ret = gnutls_handshake (es.session);
478
fprintf(stderr, "\n*** Handshake failed ***\n");
716
ret = gnutls_handshake(session);
717
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
719
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
485
726
fprintf(stderr, "Unknown error while reading data from"
486
" encrypted session with mandos server\n");
727
" encrypted session with Mandos server\n");
488
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
729
gnutls_bye(session, GNUTLS_SHUT_RDWR);
492
buffer_length += (size_t) ret;
733
buffer_length += (size_t) sret;
496
if (buffer_length > 0){
738
fprintf(stderr, "Closing TLS session\n");
741
gnutls_bye(session, GNUTLS_SHUT_RDWR);
743
if(buffer_length > 0){
497
744
decrypted_buffer_size = pgp_packet_decrypt(buffer,
501
if (decrypted_buffer_size >= 0){
747
if(decrypted_buffer_size >= 0){
502
749
while(written < (size_t) decrypted_buffer_size){
503
ret = (int)fwrite (decrypted_buffer + written, 1,
504
(size_t)decrypted_buffer_size - written,
750
ret = (int)fwrite(decrypted_buffer + written, 1,
751
(size_t)decrypted_buffer_size - written,
506
753
if(ret == 0 and ferror(stdout)){
508
755
fprintf(stderr, "Error writing encrypted data: %s\n",
567
818
char ip[AVAHI_ADDRESS_STR_MAX];
568
819
avahi_address_snprint(ip, sizeof(ip), address);
570
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
571
" port %d\n", name, host_name, ip, port);
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);
573
int ret = start_mandos_communication(ip, port, interface, mc);
825
int ret = start_mandos_communication(ip, port, interface,
826
avahi_proto_to_af(proto));
828
avahi_simple_poll_quit(mc.simple_poll);
579
832
avahi_s_service_resolver_free(r);
582
static void browse_callback( AvahiSServiceBrowser *b,
583
AvahiIfIndex interface,
584
AvahiProtocol protocol,
585
AvahiBrowserEvent event,
589
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
591
mandos_context *mc = userdata;
592
assert(b); /* Spurious warning */
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){
594
847
/* Called whenever a new services becomes available on the LAN or
595
848
is removed from the LAN */
599
852
case AVAHI_BROWSER_FAILURE:
601
fprintf(stderr, "(Browser) %s\n",
602
avahi_strerror(avahi_server_errno(mc->server)));
603
avahi_simple_poll_quit(mc->simple_poll);
854
fprintf(stderr, "(Avahi browser) %s\n",
855
avahi_strerror(avahi_server_errno(mc.server)));
856
avahi_simple_poll_quit(mc.simple_poll);
606
859
case AVAHI_BROWSER_NEW:
607
/* We ignore the returned resolver object. In the callback
608
function we free it. If the server is terminated before
609
the callback function is called the server will free
610
the resolver for us. */
612
if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name,
614
AVAHI_PROTO_INET6, 0,
615
resolve_callback, mc)))
616
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
617
avahi_strerror(avahi_server_errno(s)));
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
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)));
620
872
case AVAHI_BROWSER_REMOVE:
623
875
case AVAHI_BROWSER_ALL_FOR_NOW:
624
876
case AVAHI_BROWSER_CACHE_EXHAUSTED:
878
fprintf(stderr, "No Mandos server found, still searching...\n");
629
/* Combines file name and path and returns the malloced new
630
string. some sane checks could/should be added */
631
static const char *combinepath(const char *first, const char *second){
632
size_t f_len = strlen(first);
633
size_t s_len = strlen(second);
634
char *tmp = malloc(f_len + s_len + 2);
639
memcpy(tmp, first, f_len);
643
memcpy(tmp + f_len + 1, second, s_len);
645
tmp[f_len + 1 + s_len] = '\0';
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);
650
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
651
AvahiServerConfig config;
652
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 = 2048, .priority = "SECURE256"};
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",
665
static struct option long_options[] = {
666
{"debug", no_argument, (int *)&debug, 1},
667
{"connect", required_argument, 0, 'C'},
668
{"interface", required_argument, 0, 'i'},
669
{"certdir", required_argument, 0, 'd'},
670
{"certkey", required_argument, 0, 'c'},
671
{"certfile", required_argument, 0, 'k'},
672
{"dh_bits", required_argument, 0, 'D'},
673
{"priority", required_argument, 0, 'p'},
676
int option_index = 0;
677
ret = getopt_long (argc, argv, "i:", long_options,
706
tmp = strtol(optarg, NULL, 10);
707
if (errno == ERANGE){
715
mc.priority = optarg;
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");
1005
return ARGP_ERR_UNKNOWN;
722
certfile = combinepath(certdir, certfile);
723
if (certfile == NULL){
724
perror("combinepath");
725
returncode = EXIT_FAILURE;
729
certkey = combinepath(certdir, certkey);
730
if (certkey == NULL){
731
perror("combinepath");
732
returncode = EXIT_FAILURE;
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'){
736
1065
if_index = (AvahiIfIndex) if_nametoindex(interface);
737
1066
if(if_index == 0){
738
1067
fprintf(stderr, "No such interface: \"%s\"\n", interface);
742
if(connect_to != NULL){
743
/* Connect directly, do not use Zeroconf */
744
/* (Mainly meant for debugging) */
745
char *address = strrchr(connect_to, ':');
747
fprintf(stderr, "No colon in address\n");
751
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
753
perror("Bad port number");
757
address = connect_to;
758
ret = start_mandos_communication(address, port, if_index);
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__ */
766
1087
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
768
1089
perror("socket");
769
returncode = EXIT_FAILURE;
1090
exitcode = EXIT_FAILURE;
1092
if(restore_loglevel){
1093
ret = klogctl(7, NULL, 0);
1098
#endif /* __linux__ */
772
strcpy(network.ifr_name, interface);
1101
strcpy(network.ifr_name, interface);
773
1102
ret = ioctl(sd, SIOCGIFFLAGS, &network);
776
1104
perror("ioctl SIOCGIFFLAGS");
777
returncode = EXIT_FAILURE;
1106
if(restore_loglevel){
1107
ret = klogctl(7, NULL, 0);
1112
#endif /* __linux__ */
1113
exitcode = EXIT_FAILURE;
780
1116
if((network.ifr_flags & IFF_UP) == 0){
781
1117
network.ifr_flags |= IFF_UP;
1118
take_down_interface = true;
782
1119
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1121
take_down_interface = false;
784
1122
perror("ioctl SIOCSIFFLAGS");
785
returncode = EXIT_FAILURE;
792
avahi_set_log_function(empty_log);
795
/* Initialize the psuedo-RNG */
796
srand((unsigned int) time(NULL));
798
/* Allocate main loop object */
799
if (!(mc.simple_poll = avahi_simple_poll_new())) {
800
fprintf(stderr, "Failed to create simple poll object.\n");
801
returncode = EXIT_FAILURE;
805
/* Do not publish any local records */
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 */
806
1284
avahi_server_config_init(&config);
807
1285
config.publish_hinfo = 0;
808
1286
config.publish_addresses = 0;
809
1287
config.publish_workstation = 0;
810
1288
config.publish_domain = 0;
812
1290
/* Allocate a new server */
813
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
814
&config, NULL, NULL, &error);
816
/* Free the configuration data */
1291
mc.server = avahi_server_new(avahi_simple_poll_get
1292
(mc.simple_poll), &config, NULL,
1295
/* Free the Avahi configuration data */
817
1296
avahi_server_config_free(&config);
819
/* Check if creating the server object succeeded */
821
fprintf(stderr, "Failed to create server: %s\n",
822
avahi_strerror(error));
823
returncode = EXIT_FAILURE;
827
/* Create the service browser */
828
sb = avahi_s_service_browser_new(mc.server, if_index,
830
"_mandos._tcp", NULL, 0,
831
browse_callback, &mc);
833
fprintf(stderr, "Failed to create service browser: %s\n",
834
avahi_strerror(avahi_server_errno(mc.server)));
835
returncode = EXIT_FAILURE;
839
/* Run the main loop */
842
fprintf(stderr, "Starting avahi loop search\n");
845
avahi_simple_poll_loop(simple_poll);
850
fprintf(stderr, "%s exiting\n", argv[0]);
855
avahi_s_service_browser_free(sb);
858
avahi_server_free(mc.server);
861
avahi_simple_poll_free(simple_poll);
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){