32
31
/* Needed by GPGME, specifically gpgme_data_seek() */
33
#ifndef _LARGEFILE_SOURCE
34
32
#define _LARGEFILE_SOURCE
36
#ifndef _FILE_OFFSET_BITS
37
33
#define _FILE_OFFSET_BITS 64
40
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
42
#include <stdio.h> /* fprintf(), stderr, fwrite(),
43
stdout, ferror(), remove() */
44
#include <stdint.h> /* uint16_t, uint32_t */
45
#include <stddef.h> /* NULL, size_t, ssize_t */
46
#include <stdlib.h> /* free(), EXIT_SUCCESS, EXIT_FAILURE,
48
#include <stdbool.h> /* bool, false, true */
49
#include <string.h> /* memset(), strcmp(), strlen(),
50
strerror(), asprintf(), strcpy() */
51
#include <sys/ioctl.h> /* ioctl */
52
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
53
sockaddr_in6, PF_INET6,
54
SOCK_STREAM, uid_t, gid_t, open(),
56
#include <sys/stat.h> /* open() */
57
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
58
inet_pton(), connect() */
59
#include <fcntl.h> /* open() */
60
#include <dirent.h> /* opendir(), struct dirent, readdir()
62
#include <inttypes.h> /* PRIu16, PRIdMAX, intmax_t,
64
#include <assert.h> /* assert() */
65
#include <errno.h> /* perror(), errno */
66
#include <time.h> /* nanosleep(), time() */
67
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
68
SIOCSIFFLAGS, if_indextoname(),
69
if_nametoindex(), IF_NAMESIZE */
70
#include <netinet/in.h> /* IN6_IS_ADDR_LINKLOCAL,
71
INET_ADDRSTRLEN, INET6_ADDRSTRLEN
73
#include <unistd.h> /* close(), SEEK_SET, off_t, write(),
74
getuid(), getgid(), setuid(),
76
#include <arpa/inet.h> /* inet_pton(), htons */
77
#include <iso646.h> /* not, or, and */
78
#include <argp.h> /* struct argp_option, error_t, struct
79
argp_state, struct argp,
80
argp_parse(), ARGP_KEY_ARG,
81
ARGP_KEY_END, ARGP_ERR_UNKNOWN */
82
#include <signal.h> /* sigemptyset(), sigaddset(),
83
sigaction(), SIGTERM, sigaction,
87
#include <sys/klog.h> /* klogctl() */
88
#endif /* __linux__ */
91
/* All Avahi types, constants and functions
39
#include <net/if.h> /* if_nametoindex */
40
#include <sys/ioctl.h> // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
41
#include <net/if.h> // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
94
43
#include <avahi-core/core.h>
95
44
#include <avahi-core/lookup.h>
96
45
#include <avahi-core/log.h>
98
47
#include <avahi-common/malloc.h>
99
48
#include <avahi-common/error.h>
102
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
105
init_gnutls_session(),
107
#include <gnutls/openpgp.h>
108
/* gnutls_certificate_set_openpgp_key_file(),
109
GNUTLS_OPENPGP_FMT_BASE64 */
112
#include <gpgme.h> /* All GPGME types, constants and
115
GPGME_PROTOCOL_OpenPGP,
51
#include <sys/types.h> /* socket(), inet_pton() */
52
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
53
struct in6_addr, inet_pton() */
54
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
55
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
57
#include <unistd.h> /* close() */
58
#include <netinet/in.h>
59
#include <stdbool.h> /* true */
60
#include <string.h> /* memset */
61
#include <arpa/inet.h> /* inet_pton() */
62
#include <iso646.h> /* not */
65
#include <errno.h> /* perror() */
118
71
#define BUFFER_SIZE 256
120
#define PATHDIR "/conf/conf.d/mandos"
121
#define SECKEY "seckey.txt"
122
#define PUBKEY "pubkey.txt"
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
81
AvahiSimplePoll *simple_poll;
132
82
AvahiServer *server;
133
83
gnutls_certificate_credentials_t cred;
134
84
unsigned int dh_bits;
135
gnutls_dh_params_t dh_params;
136
85
const char *priority;
88
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
91
gpgme_data_t dh_crypto, dh_plain;
140
/* global context so signal handler can reach it*/
141
mandos_context mc = { .simple_poll = NULL, .server = NULL,
142
.dh_bits = 1024, .priority = "SECURE256"
143
":!CTYPE-X.509:+CTYPE-OPENPGP" };
146
* Make additional room in "buffer" for at least BUFFER_SIZE more
147
* bytes. "buffer_capacity" is how much is currently allocated,
148
* "buffer_length" is how much is already used.
150
size_t incbuffer(char **buffer, size_t buffer_length,
151
size_t buffer_capacity){
152
if(buffer_length + BUFFER_SIZE > buffer_capacity){
153
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
157
buffer_capacity += BUFFER_SIZE;
159
return buffer_capacity;
165
static bool init_gpgme(const char *seckey,
166
const char *pubkey, const char *tempdir){
95
ssize_t new_packet_capacity = 0;
96
ssize_t new_packet_length = 0;
169
97
gpgme_engine_info_t engine_info;
173
* Helper function to insert pub and seckey to the engine keyring.
175
bool import_key(const char *filename){
177
gpgme_data_t pgp_data;
179
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
185
rc = gpgme_data_new_from_fd(&pgp_data, fd);
186
if(rc != GPG_ERR_NO_ERROR){
187
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
188
gpgme_strsource(rc), gpgme_strerror(rc));
192
rc = gpgme_op_import(mc.ctx, pgp_data);
193
if(rc != GPG_ERR_NO_ERROR){
194
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
195
gpgme_strsource(rc), gpgme_strerror(rc));
199
ret = (int)TEMP_FAILURE_RETRY(close(fd));
203
gpgme_data_release(pgp_data);
208
fprintf(stderr, "Initializing GPGME\n");
100
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
212
104
gpgme_check_version(NULL);
213
105
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
214
if(rc != GPG_ERR_NO_ERROR){
106
if (rc != GPG_ERR_NO_ERROR){
215
107
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
216
108
gpgme_strsource(rc), gpgme_strerror(rc));
220
/* Set GPGME home directory for the OpenPGP engine only */
221
rc = gpgme_get_engine_info(&engine_info);
222
if(rc != GPG_ERR_NO_ERROR){
112
/* Set GPGME home directory */
113
rc = gpgme_get_engine_info (&engine_info);
114
if (rc != GPG_ERR_NO_ERROR){
223
115
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
224
116
gpgme_strsource(rc), gpgme_strerror(rc));
227
119
while(engine_info != NULL){
228
120
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
229
121
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
230
engine_info->file_name, tempdir);
122
engine_info->file_name, homedir);
233
125
engine_info = engine_info->next;
235
127
if(engine_info == NULL){
236
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
240
/* Create new GPGME "context" */
241
rc = gpgme_new(&(mc.ctx));
242
if(rc != GPG_ERR_NO_ERROR){
243
fprintf(stderr, "bad gpgme_new: %s: %s\n",
244
gpgme_strsource(rc), gpgme_strerror(rc));
248
if(not import_key(pubkey) or not import_key(seckey)){
256
* Decrypt OpenPGP data.
257
* Returns -1 on error
259
static ssize_t pgp_packet_decrypt(const char *cryptotext,
262
gpgme_data_t dh_crypto, dh_plain;
265
size_t plaintext_capacity = 0;
266
ssize_t plaintext_length = 0;
269
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
272
/* Create new GPGME data buffer from memory cryptotext */
273
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
275
if(rc != GPG_ERR_NO_ERROR){
128
fprintf(stderr, "Could not set home dir to %s\n", homedir);
132
/* Create new GPGME data buffer from packet buffer */
133
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
134
if (rc != GPG_ERR_NO_ERROR){
276
135
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
277
136
gpgme_strsource(rc), gpgme_strerror(rc));
327
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
198
/* Delete the GPGME FILE pointer cryptotext data buffer */
199
gpgme_data_release(dh_crypto);
330
201
/* Seek back to the beginning of the GPGME plaintext data buffer */
331
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
332
perror("gpgme_data_seek");
333
plaintext_length = -1;
202
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
203
perror("pgpme_data_seek");
339
plaintext_capacity = incbuffer(plaintext,
340
(size_t)plaintext_length,
342
if(plaintext_capacity == 0){
344
plaintext_length = -1;
208
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
209
*new_packet = realloc(*new_packet,
210
(unsigned int)new_packet_capacity
212
if (*new_packet == NULL){
216
new_packet_capacity += BUFFER_SIZE;
348
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
219
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
350
221
/* Print the data, if any */
356
226
perror("gpgme_data_read");
357
plaintext_length = -1;
360
plaintext_length += ret;
364
fprintf(stderr, "Decrypted password is: ");
365
for(ssize_t i = 0; i < plaintext_length; i++){
366
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
368
fprintf(stderr, "\n");
373
/* Delete the GPGME cryptotext data buffer */
374
gpgme_data_release(dh_crypto);
229
new_packet_length += ret;
232
/* FIXME: check characters before printing to screen so to not print
233
terminal control characters */
235
/* fprintf(stderr, "decrypted password is: "); */
236
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
237
/* fprintf(stderr, "\n"); */
376
240
/* Delete the GPGME plaintext data buffer */
377
241
gpgme_data_release(dh_plain);
378
return plaintext_length;
242
return new_packet_length;
381
static const char * safer_gnutls_strerror(int value){
382
const char *ret = gnutls_strerror(value); /* Spurious warning from
383
-Wunreachable-code */
245
static const char * safer_gnutls_strerror (int value) {
246
const char *ret = gnutls_strerror (value);
385
248
ret = "(unknown)";
389
/* GnuTLS log function callback */
390
252
static void debuggnutls(__attribute__((unused)) int level,
391
253
const char* string){
392
fprintf(stderr, "GnuTLS: %s", string);
254
fprintf(stderr, "%s", string);
395
static int init_gnutls_global(const char *pubkeyfilename,
396
const char *seckeyfilename){
257
static int initgnutls(mandos_context *mc){
400
262
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));
265
if ((ret = gnutls_global_init ())
266
!= GNUTLS_E_SUCCESS) {
267
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
411
/* "Use a log level over 10 to enable all debugging options."
414
272
gnutls_global_set_log_level(11);
415
273
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();
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));
431
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
432
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
285
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
286
" and keyfile %s as GnuTLS credentials\n", certfile,
436
290
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){
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){
479
322
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
480
323
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);
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));
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
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",
499
338
safer_gnutls_strerror(ret));
500
gnutls_deinit(*session);
504
342
/* ignore client certificate if any. */
505
gnutls_certificate_server_set_request(*session,
343
gnutls_certificate_server_set_request (es->session,
508
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
346
gnutls_dh_set_prime_bits (es->session, DH_BITS);
513
/* Avahi log function callback */
514
351
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
515
352
__attribute__((unused)) const char *txt){}
517
/* Called when a Mandos server is found */
518
354
static int start_mandos_communication(const char *ip, uint16_t port,
519
355
AvahiIfIndex if_index,
524
struct sockaddr_in in;
525
struct sockaddr_in6 in6;
358
struct sockaddr_in6 to;
359
encrypted_session es;
527
360
char *buffer = NULL;
528
361
char *decrypted_buffer;
529
362
size_t buffer_length = 0;
530
363
size_t buffer_capacity = 0;
531
364
ssize_t decrypted_buffer_size;
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);
367
char interface[IF_NAMESIZE];
555
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
370
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
559
tcp_sd = socket(pf, SOCK_STREAM, 0);
374
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
561
376
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);
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);
391
memset(&to,0,sizeof(to)); /* Spurious warning */
392
to.sin6_family = AF_INET6;
393
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
574
395
perror("inet_pton");
578
399
fprintf(stderr, "Bad address: %s\n", ip);
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 */
402
to.sin6_port = htons(port); /* Spurious warning */
404
to.sin6_scope_id = (uint32_t)if_index;
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);
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)); */
636
ret = connect(tcp_sd, &to.in6, sizeof(to));
638
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
418
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
641
420
perror("connect");
645
const char *out = mandos_protocol_version;
648
size_t out_size = strlen(out);
649
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
650
out_size - written));
656
written += (size_t)ret;
657
if(written < out_size){
660
if(out == mandos_protocol_version){
424
ret = initgnutls (&es);
430
gnutls_transport_set_ptr (es.session,
431
(gnutls_transport_ptr_t) tcp_sd);
670
434
fprintf(stderr, "Establishing TLS session with %s\n", ip);
673
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
676
ret = gnutls_handshake(session);
677
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
679
if(ret != GNUTLS_E_SUCCESS){
437
ret = gnutls_handshake (es.session);
439
if (ret != GNUTLS_E_SUCCESS){
681
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
441
fprintf(stderr, "\n*** Handshake failed ***\n");
688
/* Read OpenPGP packet that contains the wanted password */
448
//Retrieve OpenPGP packet that contains the wanted password
691
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
451
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
696
buffer_capacity = incbuffer(&buffer, buffer_length,
698
if(buffer_capacity == 0){
456
if (buffer_length + BUFFER_SIZE > buffer_capacity){
457
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
462
buffer_capacity += BUFFER_SIZE;
704
sret = gnutls_record_recv(session, buffer+buffer_length,
465
ret = gnutls_record_recv
466
(es.session, buffer+buffer_length, BUFFER_SIZE);
711
472
case GNUTLS_E_INTERRUPTED:
712
473
case GNUTLS_E_AGAIN:
714
475
case GNUTLS_E_REHANDSHAKE:
716
ret = gnutls_handshake(session);
717
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
719
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
476
ret = gnutls_handshake (es.session);
478
fprintf(stderr, "\n*** Handshake failed ***\n");
726
485
fprintf(stderr, "Unknown error while reading data from"
727
" encrypted session with Mandos server\n");
486
" encrypted session with mandos server\n");
729
gnutls_bye(session, GNUTLS_SHUT_RDWR);
488
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
733
buffer_length += (size_t) sret;
492
buffer_length += (size_t) ret;
738
fprintf(stderr, "Closing TLS session\n");
741
gnutls_bye(session, GNUTLS_SHUT_RDWR);
743
if(buffer_length > 0){
496
if (buffer_length > 0){
744
497
decrypted_buffer_size = pgp_packet_decrypt(buffer,
747
if(decrypted_buffer_size >= 0){
501
if (decrypted_buffer_size >= 0){
749
502
while(written < (size_t) decrypted_buffer_size){
750
ret = (int)fwrite(decrypted_buffer + written, 1,
751
(size_t)decrypted_buffer_size - written,
503
ret = (int)fwrite (decrypted_buffer + written, 1,
504
(size_t)decrypted_buffer_size - written,
753
506
if(ret == 0 and ferror(stdout)){
755
508
fprintf(stderr, "Error writing encrypted data: %s\n",
812
567
char ip[AVAHI_ADDRESS_STR_MAX];
813
568
avahi_address_snprint(ip, sizeof(ip), address);
815
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
816
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
817
ip, (intmax_t)interface, port);
570
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
571
" port %d\n", name, host_name, ip, port);
819
int ret = start_mandos_communication(ip, port, interface,
820
avahi_proto_to_af(proto));
822
avahi_simple_poll_quit(mc.simple_poll);
573
int ret = start_mandos_communication(ip, port, interface, mc);
826
579
avahi_s_service_resolver_free(r);
829
static void browse_callback(AvahiSServiceBrowser *b,
830
AvahiIfIndex interface,
831
AvahiProtocol protocol,
832
AvahiBrowserEvent event,
836
AVAHI_GCC_UNUSED AvahiLookupResultFlags
838
AVAHI_GCC_UNUSED void* userdata){
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 */
841
594
/* Called whenever a new services becomes available on the LAN or
842
595
is removed from the LAN */
846
599
case AVAHI_BROWSER_FAILURE:
848
fprintf(stderr, "(Avahi browser) %s\n",
849
avahi_strerror(avahi_server_errno(mc.server)));
850
avahi_simple_poll_quit(mc.simple_poll);
601
fprintf(stderr, "(Browser) %s\n",
602
avahi_strerror(avahi_server_errno(mc->server)));
603
avahi_simple_poll_quit(mc->simple_poll);
853
606
case AVAHI_BROWSER_NEW:
854
/* We ignore the returned Avahi resolver object. In the callback
855
function we free it. If the Avahi server is terminated before
856
the callback function is called the Avahi server will free the
859
if(avahi_s_service_resolver_new(mc.server, interface, protocol,
860
name, type, domain, protocol, 0,
861
resolve_callback, NULL) == NULL)
862
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
863
name, avahi_strerror(avahi_server_errno(mc.server)));
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)));
866
620
case AVAHI_BROWSER_REMOVE:
869
623
case AVAHI_BROWSER_ALL_FOR_NOW:
870
624
case AVAHI_BROWSER_CACHE_EXHAUSTED:
872
fprintf(stderr, "No Mandos server found, still searching...\n");
878
sig_atomic_t quit_now = 0;
880
/* stop main loop after sigterm has been called */
881
static void handle_sigterm(__attribute__((unused)) int sig){
886
int old_errno = errno;
887
if(mc.simple_poll != NULL){
888
avahi_simple_poll_quit(mc.simple_poll);
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';
893
int main(int argc, char *argv[]){
894
AvahiSServiceBrowser *sb = NULL;
899
int exitcode = EXIT_SUCCESS;
900
const char *interface = "eth0";
901
struct ifreq network;
903
bool interface_taken_up = false;
906
char *connect_to = NULL;
907
char tempdir[] = "/tmp/mandosXXXXXX";
908
bool tempdir_created = false;
909
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
910
const char *seckey = PATHDIR "/" SECKEY;
911
const char *pubkey = PATHDIR "/" PUBKEY;
913
bool gnutls_initialized = false;
914
bool gpgme_initialized = false;
917
struct sigaction old_sigterm_action;
918
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
921
struct argp_option options[] = {
922
{ .name = "debug", .key = 128,
923
.doc = "Debug mode", .group = 3 },
924
{ .name = "connect", .key = 'c',
925
.arg = "ADDRESS:PORT",
926
.doc = "Connect directly to a specific Mandos server",
928
{ .name = "interface", .key = 'i',
930
.doc = "Network interface that will be used to search for"
933
{ .name = "seckey", .key = 's',
935
.doc = "OpenPGP secret key file base name",
937
{ .name = "pubkey", .key = 'p',
939
.doc = "OpenPGP public key file base name",
941
{ .name = "dh-bits", .key = 129,
943
.doc = "Bit length of the prime number used in the"
944
" Diffie-Hellman key exchange",
946
{ .name = "priority", .key = 130,
948
.doc = "GnuTLS priority string for the TLS handshake",
950
{ .name = "delay", .key = 131,
952
.doc = "Maximum delay to wait for interface startup",
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"};
957
error_t parse_opt(int key, char *arg,
958
struct argp_state *state){
960
case 128: /* --debug */
963
case 'c': /* --connect */
966
case 'i': /* --interface */
969
case 's': /* --seckey */
972
case 'p': /* --pubkey */
975
case 129: /* --dh-bits */
977
tmpmax = strtoimax(arg, &tmp, 10);
978
if(errno != 0 or tmp == arg or *tmp != '\0'
979
or tmpmax != (typeof(mc.dh_bits))tmpmax){
980
fprintf(stderr, "Bad number of DH bits\n");
983
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
985
case 130: /* --priority */
988
case 131: /* --delay */
990
delay = strtof(arg, &tmp);
991
if(errno != 0 or tmp == arg or *tmp != '\0'){
992
fprintf(stderr, "Bad delay\n");
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;
1001
return ARGP_ERR_UNKNOWN;
1006
struct argp argp = { .options = options, .parser = parse_opt,
1008
.doc = "Mandos client -- Get and decrypt"
1009
" passwords from a Mandos server" };
1010
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
1011
if(ret == ARGP_ERR_UNKNOWN){
1012
fprintf(stderr, "Unknown error while parsing arguments\n");
1013
exitcode = EXIT_FAILURE;
1019
avahi_set_log_function(empty_log);
1022
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1023
from the signal handler */
1024
/* Initialize the pseudo-RNG for Avahi */
1025
srand((unsigned int) time(NULL));
1026
mc.simple_poll = avahi_simple_poll_new();
1027
if(mc.simple_poll == NULL){
1028
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1029
exitcode = EXIT_FAILURE;
1033
sigemptyset(&sigterm_action.sa_mask);
1034
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1036
perror("sigaddset");
1037
exitcode = EXIT_FAILURE;
1040
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1042
perror("sigaddset");
1043
exitcode = EXIT_FAILURE;
1046
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1048
perror("sigaddset");
1049
exitcode = EXIT_FAILURE;
1052
ret = sigaction(SIGTERM, &sigterm_action, &old_sigterm_action);
1054
perror("sigaction");
1055
exitcode = EXIT_FAILURE;
1059
/* If the interface is down, bring it up */
1060
if(interface[0] != '\0'){
1062
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1063
messages to mess up the prompt */
1064
ret = klogctl(8, NULL, 5);
1065
bool restore_loglevel = true;
1067
restore_loglevel = false;
1070
#endif /* __linux__ */
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;
736
if_index = (AvahiIfIndex) if_nametoindex(interface);
738
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);
1072
766
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1074
768
perror("socket");
1075
exitcode = EXIT_FAILURE;
1077
if(restore_loglevel){
1078
ret = klogctl(7, NULL, 0);
1083
#endif /* __linux__ */
769
returncode = EXIT_FAILURE;
1086
strcpy(network.ifr_name, interface);
772
strcpy(network.ifr_name, interface);
1087
773
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1089
776
perror("ioctl SIOCGIFFLAGS");
1091
if(restore_loglevel){
1092
ret = klogctl(7, NULL, 0);
1097
#endif /* __linux__ */
1098
exitcode = EXIT_FAILURE;
777
returncode = EXIT_FAILURE;
1101
780
if((network.ifr_flags & IFF_UP) == 0){
1102
781
network.ifr_flags |= IFF_UP;
1103
782
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1105
784
perror("ioctl SIOCSIFFLAGS");
1106
exitcode = EXIT_FAILURE;
1108
if(restore_loglevel){
1109
ret = klogctl(7, NULL, 0);
1114
#endif /* __linux__ */
1117
interface_taken_up = true;
1119
/* sleep checking until interface is running */
1120
for(int i=0; i < delay * 4; i++){
1121
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1123
perror("ioctl SIOCGIFFLAGS");
1124
} else if(network.ifr_flags & IFF_RUNNING){
1127
struct timespec sleeptime = { .tv_nsec = 250000000 };
1128
ret = nanosleep(&sleeptime, NULL);
1129
if(ret == -1 and errno != EINTR){
1130
perror("nanosleep");
1133
if(not interface_taken_up){
1134
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1140
if(restore_loglevel){
1141
/* Restores kernel loglevel to default */
1142
ret = klogctl(7, NULL, 0);
1147
#endif /* __linux__ */
1164
ret = init_gnutls_global(pubkey, seckey);
1166
fprintf(stderr, "init_gnutls_global failed\n");
1167
exitcode = EXIT_FAILURE;
1170
gnutls_initialized = true;
1173
if(mkdtemp(tempdir) == NULL){
1177
tempdir_created = true;
1179
if(not init_gpgme(pubkey, seckey, tempdir)){
1180
fprintf(stderr, "init_gpgme failed\n");
1181
exitcode = EXIT_FAILURE;
1184
gpgme_initialized = true;
1187
if(interface[0] != '\0'){
1188
if_index = (AvahiIfIndex) if_nametoindex(interface);
1190
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1191
exitcode = EXIT_FAILURE;
1196
if(connect_to != NULL){
1197
/* Connect directly, do not use Zeroconf */
1198
/* (Mainly meant for debugging) */
1199
char *address = strrchr(connect_to, ':');
1200
if(address == NULL){
1201
fprintf(stderr, "No colon in address\n");
1202
exitcode = EXIT_FAILURE;
1207
tmpmax = strtoimax(address+1, &tmp, 10);
1208
if(errno != 0 or tmp == address+1 or *tmp != '\0'
1209
or tmpmax != (uint16_t)tmpmax){
1210
fprintf(stderr, "Bad port number\n");
1211
exitcode = EXIT_FAILURE;
1214
port = (uint16_t)tmpmax;
1216
address = connect_to;
1217
/* Colon in address indicates IPv6 */
1219
if(strchr(address, ':') != NULL){
1224
ret = start_mandos_communication(address, port, if_index, af);
1226
exitcode = EXIT_FAILURE;
1228
exitcode = EXIT_SUCCESS;
1234
AvahiServerConfig config;
1235
/* Do not publish any local Zeroconf records */
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 */
1236
806
avahi_server_config_init(&config);
1237
807
config.publish_hinfo = 0;
1238
808
config.publish_addresses = 0;
1239
809
config.publish_workstation = 0;
1240
810
config.publish_domain = 0;
1242
812
/* Allocate a new server */
1243
mc.server = avahi_server_new(avahi_simple_poll_get
1244
(mc.simple_poll), &config, NULL,
1247
/* Free the Avahi configuration data */
813
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
814
&config, NULL, NULL, &error);
816
/* Free the configuration data */
1248
817
avahi_server_config_free(&config);
1251
/* Check if creating the Avahi server object succeeded */
1252
if(mc.server == NULL){
1253
fprintf(stderr, "Failed to create Avahi server: %s\n",
1254
avahi_strerror(error));
1255
exitcode = EXIT_FAILURE;
1259
/* Create the Avahi service browser */
1260
sb = avahi_s_service_browser_new(mc.server, if_index,
1261
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
1262
NULL, 0, browse_callback, NULL);
1264
fprintf(stderr, "Failed to create service browser: %s\n",
1265
avahi_strerror(avahi_server_errno(mc.server)));
1266
exitcode = EXIT_FAILURE;
1270
/* Run the main loop */
1273
fprintf(stderr, "Starting Avahi loop search\n");
1276
avahi_simple_poll_loop(mc.simple_poll);
1281
fprintf(stderr, "%s exiting\n", argv[0]);
1284
/* Cleanup things */
1286
avahi_s_service_browser_free(sb);
1288
if(mc.server != NULL)
1289
avahi_server_free(mc.server);
1291
if(mc.simple_poll != NULL)
1292
avahi_simple_poll_free(mc.simple_poll);
1294
if(gnutls_initialized){
1295
gnutls_certificate_free_credentials(mc.cred);
1296
gnutls_global_deinit();
1297
gnutls_dh_params_deinit(mc.dh_params);
1300
if(gpgme_initialized){
1301
gpgme_release(mc.ctx);
1304
/* Take down the network interface */
1305
if(interface_taken_up){
1306
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1308
perror("ioctl SIOCGIFFLAGS");
1309
} else if(network.ifr_flags & IFF_UP) {
1310
network.ifr_flags &= ~IFF_UP; /* clear flag */
1311
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1313
perror("ioctl SIOCSIFFLAGS");
1316
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1322
/* Removes the temp directory used by GPGME */
1323
if(tempdir_created){
1325
struct dirent *direntry;
1326
d = opendir(tempdir);
1328
if(errno != ENOENT){
1333
direntry = readdir(d);
1334
if(direntry == NULL){
1337
/* Skip "." and ".." */
1338
if(direntry->d_name[0] == '.'
1339
and (direntry->d_name[1] == '\0'
1340
or (direntry->d_name[1] == '.'
1341
and direntry->d_name[2] == '\0'))){
1344
char *fullname = NULL;
1345
ret = asprintf(&fullname, "%s/%s", tempdir,
1351
ret = remove(fullname);
1353
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1360
ret = rmdir(tempdir);
1361
if(ret == -1 and errno != ENOENT){
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);