25
26
* along with this program. If not, see
26
27
* <http://www.gnu.org/licenses/>.
28
* Contact the authors at <https://www.fukt.bsnet.se/~belorn/> and
29
* <https://www.fukt.bsnet.se/~teddy/>.
29
* Contact the authors at <mandos@fukt.bsnet.se>.
32
#define _FORTIFY_SOURCE 2
32
/* Needed by GPGME, specifically gpgme_data_seek() */
33
#ifndef _LARGEFILE_SOURCE
34
34
#define _LARGEFILE_SOURCE
36
#ifndef _FILE_OFFSET_BITS
35
37
#define _FILE_OFFSET_BITS 64
41
#include <net/if.h> /* if_nametoindex */
42
#include <sys/ioctl.h> // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
43
#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(), seteuid(),
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
45
94
#include <avahi-core/core.h>
46
95
#include <avahi-core/lookup.h>
47
96
#include <avahi-core/log.h>
49
98
#include <avahi-common/malloc.h>
50
99
#include <avahi-common/error.h>
53
#include <sys/types.h> /* socket(), inet_pton() */
54
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
55
struct in6_addr, inet_pton() */
56
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
57
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
59
#include <unistd.h> /* close() */
60
#include <netinet/in.h>
61
#include <stdbool.h> /* true */
62
#include <string.h> /* memset */
63
#include <arpa/inet.h> /* inet_pton() */
64
#include <iso646.h> /* not */
67
#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,
73
118
#define BUFFER_SIZE 256
76
const char *certdir = "/conf/conf.d/cryptkeyreq/";
77
const char *certfile = "openpgp-client.txt";
78
const char *certkey = "openpgp-client-key.txt";
120
#define PATHDIR "/conf/conf.d/mandos"
121
#define SECKEY "seckey.txt"
122
#define PUBKEY "pubkey.txt"
80
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 */
83
gnutls_session_t session;
131
AvahiSimplePoll *simple_poll;
84
133
gnutls_certificate_credentials_t cred;
134
unsigned int dh_bits;
85
135
gnutls_dh_params_t dh_params;
89
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
90
char **new_packet, const char *homedir){
91
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){
95
ssize_t new_packet_capacity = 0;
96
ssize_t new_packet_length = 0;
97
168
gpgme_engine_info_t engine_info;
100
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");
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)";
252
void debuggnutls(__attribute__((unused)) int level,
254
fprintf(stderr, "%s", string);
389
/* GnuTLS log function callback */
390
static void debuggnutls(__attribute__((unused)) int level,
392
fprintf(stderr, "GnuTLS: %s", string);
257
int initgnutls(encrypted_session *es){
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 */
478
ret = gnutls_init(session, GNUTLS_SERVER);
479
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
480
if(ret != GNUTLS_E_SUCCESS){
322
481
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
323
482
safer_gnutls_strerror(ret));
326
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &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));
488
ret = gnutls_priority_set_direct(*session, mc.priority, &err);
489
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
490
if(ret != GNUTLS_E_SUCCESS){
491
fprintf(stderr, "Syntax error at: %s\n", err);
492
fprintf(stderr, "GnuTLS error: %s\n",
493
safer_gnutls_strerror(ret));
494
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",
500
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
502
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
503
if(ret != GNUTLS_E_SUCCESS){
504
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
338
505
safer_gnutls_strerror(ret));
506
gnutls_deinit(*session);
342
510
/* ignore client certificate if any. */
343
gnutls_certificate_server_set_request (es->session,
511
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
346
gnutls_dh_set_prime_bits (es->session, DH_BITS);
513
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
351
void empty_log(__attribute__((unused)) AvahiLogLevel level,
352
__attribute__((unused)) const char *txt){}
354
int start_mandos_communication(const char *ip, uint16_t port,
355
unsigned int if_index){
357
struct sockaddr_in6 to;
358
encrypted_session es;
518
/* Avahi log function callback */
519
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
520
__attribute__((unused)) const char *txt){}
522
sig_atomic_t quit_now = 0;
523
int signal_received = 0;
525
/* Called when a Mandos server is found */
526
static int start_mandos_communication(const char *ip, uint16_t port,
527
AvahiIfIndex if_index,
529
int ret, tcp_sd = -1;
532
struct sockaddr_in in;
533
struct sockaddr_in6 in6;
359
535
char *buffer = NULL;
360
536
char *decrypted_buffer;
361
537
size_t buffer_length = 0;
362
538
size_t buffer_capacity = 0;
363
ssize_t decrypted_buffer_size;
366
char interface[IF_NAMESIZE];
541
gnutls_session_t session;
542
int pf; /* Protocol family */
556
fprintf(stderr, "Bad address family: %d\n", af);
560
ret = init_gnutls_session(&session);
369
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
566
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
372
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
570
tcp_sd = socket(pf, SOCK_STREAM, 0);
374
572
perror("socket");
379
if(if_indextoname(if_index, interface) == NULL){
381
perror("if_indextoname");
386
fprintf(stderr, "Binding to interface %s\n", interface);
389
memset(&to,0,sizeof(to)); /* Spurious warning */
390
to.sin6_family = AF_INET6;
391
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
581
memset(&to, 0, sizeof(to));
583
to.in6.sin6_family = (sa_family_t)af;
584
ret = inet_pton(af, ip, &to.in6.sin6_addr);
586
to.in.sin_family = (sa_family_t)af;
587
ret = inet_pton(af, ip, &to.in.sin_addr);
393
590
perror("inet_pton");
397
595
fprintf(stderr, "Bad address: %s\n", ip);
400
to.sin6_port = htons(port); /* Spurious warning */
600
to.in6.sin6_port = htons(port); /* Spurious warnings from
602
-Wunreachable-code */
604
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
605
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
607
if(if_index == AVAHI_IF_UNSPEC){
608
fprintf(stderr, "An IPv6 link-local address is incomplete"
609
" without a network interface\n");
613
/* Set the network interface number as scope */
614
to.in6.sin6_scope_id = (uint32_t)if_index;
617
to.in.sin_port = htons(port); /* Spurious warnings from
619
-Wunreachable-code */
402
to.sin6_scope_id = (uint32_t)if_index;
405
fprintf(stderr, "Connection to: %s\n", ip);
408
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
627
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
628
char interface[IF_NAMESIZE];
629
if(if_indextoname((unsigned int)if_index, interface) == NULL){
630
perror("if_indextoname");
632
fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
633
ip, interface, port);
636
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
639
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
640
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
643
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
646
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
652
if(strcmp(addrstr, ip) != 0){
653
fprintf(stderr, "Canonical address form: %s\n", addrstr);
663
ret = connect(tcp_sd, &to.in6, sizeof(to));
665
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
410
668
perror("connect");
414
ret = initgnutls (&es);
420
gnutls_transport_set_ptr (es.session,
421
(gnutls_transport_ptr_t) tcp_sd);
677
const char *out = mandos_protocol_version;
680
size_t out_size = strlen(out);
681
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
682
out_size - written));
688
written += (size_t)ret;
689
if(written < out_size){
692
if(out == mandos_protocol_version){
424
706
fprintf(stderr, "Establishing TLS session with %s\n", ip);
427
ret = gnutls_handshake (es.session);
429
if (ret != GNUTLS_E_SUCCESS){
713
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
720
ret = gnutls_handshake(session);
724
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
726
if(ret != GNUTLS_E_SUCCESS){
431
fprintf(stderr, "\n*** Handshake failed ***\n");
728
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
438
//Retrieve OpenPGP packet that contains the wanted password
735
/* Read OpenPGP packet that contains the wanted password */
441
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
738
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
446
if (buffer_length + BUFFER_SIZE > buffer_capacity){
447
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
452
buffer_capacity += BUFFER_SIZE;
455
ret = gnutls_record_recv
456
(es.session, buffer+buffer_length, BUFFER_SIZE);
748
buffer_capacity = incbuffer(&buffer, buffer_length,
750
if(buffer_capacity == 0){
760
sret = gnutls_record_recv(session, buffer+buffer_length,
462
767
case GNUTLS_E_INTERRUPTED:
463
768
case GNUTLS_E_AGAIN:
465
770
case GNUTLS_E_REHANDSHAKE:
466
ret = gnutls_handshake (es.session);
468
fprintf(stderr, "\n*** Handshake failed ***\n");
772
ret = gnutls_handshake(session);
777
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
779
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
475
786
fprintf(stderr, "Unknown error while reading data from"
476
" encrypted session with mandos server\n");
787
" encrypted session with Mandos server\n");
478
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
789
gnutls_bye(session, GNUTLS_SHUT_RDWR);
482
buffer_length += (size_t) ret;
793
buffer_length += (size_t) sret;
486
if (buffer_length > 0){
798
fprintf(stderr, "Closing TLS session\n");
805
gnutls_bye(session, GNUTLS_SHUT_RDWR);
811
if(buffer_length > 0){
812
ssize_t decrypted_buffer_size;
487
813
decrypted_buffer_size = pgp_packet_decrypt(buffer,
491
if (decrypted_buffer_size >= 0){
492
while(written < (size_t)decrypted_buffer_size){
493
ret = (int)fwrite (decrypted_buffer + written, 1,
494
(size_t)decrypted_buffer_size - written,
816
if(decrypted_buffer_size >= 0){
819
while(written < (size_t) decrypted_buffer_size){
824
ret = (int)fwrite(decrypted_buffer + written, 1,
825
(size_t)decrypted_buffer_size - written,
496
827
if(ret == 0 and ferror(stdout)){
498
829
fprintf(stderr, "Error writing encrypted data: %s\n",
561
895
char ip[AVAHI_ADDRESS_STR_MAX];
562
896
avahi_address_snprint(ip, sizeof(ip), address);
564
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
565
" port %d\n", name, host_name, ip, port);
898
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
899
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
900
ip, (intmax_t)interface, port);
567
int ret = start_mandos_communication(ip, port,
568
(unsigned int) interface);
902
int ret = start_mandos_communication(ip, port, interface,
903
avahi_proto_to_af(proto));
905
avahi_simple_poll_quit(mc.simple_poll);
574
909
avahi_s_service_resolver_free(r);
577
static void browse_callback(
578
AvahiSServiceBrowser *b,
579
AvahiIfIndex interface,
580
AvahiProtocol protocol,
581
AvahiBrowserEvent event,
585
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
588
AvahiServer *s = userdata;
589
assert(b); /* Spurious warning */
591
/* Called whenever a new services becomes available on the LAN or
592
is removed from the LAN */
596
case AVAHI_BROWSER_FAILURE:
598
fprintf(stderr, "(Browser) %s\n",
599
avahi_strerror(avahi_server_errno(server)));
600
avahi_simple_poll_quit(simple_poll);
603
case AVAHI_BROWSER_NEW:
604
/* We ignore the returned resolver object. In the callback
605
function we free it. If the server is terminated before
606
the callback function is called the server will free
607
the resolver for us. */
609
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
611
AVAHI_PROTO_INET6, 0,
612
resolve_callback, s)))
613
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
614
avahi_strerror(avahi_server_errno(s)));
617
case AVAHI_BROWSER_REMOVE:
620
case AVAHI_BROWSER_ALL_FOR_NOW:
621
case AVAHI_BROWSER_CACHE_EXHAUSTED:
912
static void browse_callback(AvahiSServiceBrowser *b,
913
AvahiIfIndex interface,
914
AvahiProtocol protocol,
915
AvahiBrowserEvent event,
919
AVAHI_GCC_UNUSED AvahiLookupResultFlags
921
AVAHI_GCC_UNUSED void* userdata){
924
/* Called whenever a new services becomes available on the LAN or
925
is removed from the LAN */
933
case AVAHI_BROWSER_FAILURE:
935
fprintf(stderr, "(Avahi browser) %s\n",
936
avahi_strerror(avahi_server_errno(mc.server)));
937
avahi_simple_poll_quit(mc.simple_poll);
940
case AVAHI_BROWSER_NEW:
941
/* We ignore the returned Avahi resolver object. In the callback
942
function we free it. If the Avahi server is terminated before
943
the callback function is called the Avahi server will free the
946
if(avahi_s_service_resolver_new(mc.server, interface, protocol,
947
name, type, domain, protocol, 0,
948
resolve_callback, NULL) == NULL)
949
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
950
name, avahi_strerror(avahi_server_errno(mc.server)));
953
case AVAHI_BROWSER_REMOVE:
956
case AVAHI_BROWSER_ALL_FOR_NOW:
957
case AVAHI_BROWSER_CACHE_EXHAUSTED:
959
fprintf(stderr, "No Mandos server found, still searching...\n");
626
/* combinds file name and path and returns the malloced new string. som sane checks could/should be added */
627
const char *combinepath(const char *first, const char *second){
965
/* stop main loop after sigterm has been called */
966
static void handle_sigterm(int sig){
971
signal_received = sig;
972
int old_errno = errno;
973
if(mc.simple_poll != NULL){
974
avahi_simple_poll_quit(mc.simple_poll);
979
int main(int argc, char *argv[]){
980
AvahiSServiceBrowser *sb = NULL;
629
tmp = malloc(strlen(first) + strlen(second) + 2);
635
if (first[0] != '\0' and first[strlen(first) - 1] != '/'){
643
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
644
AvahiServerConfig config;
645
AvahiSServiceBrowser *sb = NULL;
648
int returncode = EXIT_SUCCESS;
649
const char *interface = "eth0";
650
struct ifreq network;
985
int exitcode = EXIT_SUCCESS;
986
const char *interface = "eth0";
987
struct ifreq network;
989
bool take_down_interface = false;
992
char *connect_to = NULL;
993
char tempdir[] = "/tmp/mandosXXXXXX";
994
bool tempdir_created = false;
995
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
996
const char *seckey = PATHDIR "/" SECKEY;
997
const char *pubkey = PATHDIR "/" PUBKEY;
999
bool gnutls_initialized = false;
1000
bool gpgme_initialized = false;
1003
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
1004
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
1009
/* Lower any group privileges we might have, just to be safe */
1016
/* Lower user privileges (temporarily) */
1028
struct argp_option options[] = {
1029
{ .name = "debug", .key = 128,
1030
.doc = "Debug mode", .group = 3 },
1031
{ .name = "connect", .key = 'c',
1032
.arg = "ADDRESS:PORT",
1033
.doc = "Connect directly to a specific Mandos server",
1035
{ .name = "interface", .key = 'i',
1037
.doc = "Network interface that will be used to search for"
1040
{ .name = "seckey", .key = 's',
1042
.doc = "OpenPGP secret key file base name",
1044
{ .name = "pubkey", .key = 'p',
1046
.doc = "OpenPGP public key file base name",
1048
{ .name = "dh-bits", .key = 129,
1050
.doc = "Bit length of the prime number used in the"
1051
" Diffie-Hellman key exchange",
1053
{ .name = "priority", .key = 130,
1055
.doc = "GnuTLS priority string for the TLS handshake",
1057
{ .name = "delay", .key = 131,
1059
.doc = "Maximum delay to wait for interface startup",
654
static struct option long_options[] = {
655
{"debug", no_argument, (int *)&debug, 1},
656
{"interface", required_argument, 0, 'i'},
657
{"certdir", required_argument, 0, 'd'},
658
{"certkey", required_argument, 0, 'c'},
659
{"certfile", required_argument, 0, 'k'},
662
int option_index = 0;
663
ret = getopt_long (argc, argv, "i:", long_options,
1064
error_t parse_opt(int key, char *arg,
1065
struct argp_state *state){
1067
case 128: /* --debug */
1070
case 'c': /* --connect */
1073
case 'i': /* --interface */
1076
case 's': /* --seckey */
1079
case 'p': /* --pubkey */
1082
case 129: /* --dh-bits */
1084
tmpmax = strtoimax(arg, &tmp, 10);
1085
if(errno != 0 or tmp == arg or *tmp != '\0'
1086
or tmpmax != (typeof(mc.dh_bits))tmpmax){
1087
fprintf(stderr, "Bad number of DH bits\n");
1090
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
1092
case 130: /* --priority */
1095
case 131: /* --delay */
1097
delay = strtof(arg, &tmp);
1098
if(errno != 0 or tmp == arg or *tmp != '\0'){
1099
fprintf(stderr, "Bad delay\n");
1108
return ARGP_ERR_UNKNOWN;
690
certfile = combinepath(certdir, certfile);
691
if (certfile == NULL){
692
returncode = EXIT_FAILURE;
696
certkey = combinepath(certdir, certkey);
697
if (certkey == NULL){
698
returncode = EXIT_FAILURE;
1113
struct argp argp = { .options = options, .parser = parse_opt,
1115
.doc = "Mandos client -- Get and decrypt"
1116
" passwords from a Mandos server" };
1117
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
1118
if(ret == ARGP_ERR_UNKNOWN){
1119
fprintf(stderr, "Unknown error while parsing arguments\n");
1120
exitcode = EXIT_FAILURE;
1126
avahi_set_log_function(empty_log);
1129
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1130
from the signal handler */
1131
/* Initialize the pseudo-RNG for Avahi */
1132
srand((unsigned int) time(NULL));
1133
mc.simple_poll = avahi_simple_poll_new();
1134
if(mc.simple_poll == NULL){
1135
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1136
exitcode = EXIT_FAILURE;
1140
sigemptyset(&sigterm_action.sa_mask);
1141
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1143
perror("sigaddset");
1144
exitcode = EXIT_FAILURE;
1147
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1149
perror("sigaddset");
1150
exitcode = EXIT_FAILURE;
1153
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1155
perror("sigaddset");
1156
exitcode = EXIT_FAILURE;
1159
/* Need to check if the handler is SIG_IGN before handling:
1160
| [[info:libc:Initial Signal Actions]] |
1161
| [[info:libc:Basic Signal Handling]] |
1163
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
1165
perror("sigaction");
1166
return EXIT_FAILURE;
1168
if(old_sigterm_action.sa_handler != SIG_IGN){
1169
ret = sigaction(SIGINT, &sigterm_action, NULL);
1171
perror("sigaction");
1172
exitcode = EXIT_FAILURE;
1176
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
1178
perror("sigaction");
1179
return EXIT_FAILURE;
1181
if(old_sigterm_action.sa_handler != SIG_IGN){
1182
ret = sigaction(SIGHUP, &sigterm_action, NULL);
1184
perror("sigaction");
1185
exitcode = EXIT_FAILURE;
1189
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
1191
perror("sigaction");
1192
return EXIT_FAILURE;
1194
if(old_sigterm_action.sa_handler != SIG_IGN){
1195
ret = sigaction(SIGTERM, &sigterm_action, NULL);
1197
perror("sigaction");
1198
exitcode = EXIT_FAILURE;
1203
/* If the interface is down, bring it up */
1204
if(interface[0] != '\0'){
1205
if_index = (AvahiIfIndex) if_nametoindex(interface);
1207
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1208
exitcode = EXIT_FAILURE;
1216
/* Re-raise priviliges */
1224
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1225
messages to mess up the prompt */
1226
ret = klogctl(8, NULL, 5);
1227
bool restore_loglevel = true;
1229
restore_loglevel = false;
1232
#endif /* __linux__ */
702
1234
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
704
1236
perror("socket");
705
returncode = EXIT_FAILURE;
1237
exitcode = EXIT_FAILURE;
1239
if(restore_loglevel){
1240
ret = klogctl(7, NULL, 0);
1245
#endif /* __linux__ */
1246
/* Lower privileges */
708
strcpy(network.ifr_name, interface);
1254
strcpy(network.ifr_name, interface);
709
1255
ret = ioctl(sd, SIOCGIFFLAGS, &network);
712
1257
perror("ioctl SIOCGIFFLAGS");
713
returncode = EXIT_FAILURE;
1259
if(restore_loglevel){
1260
ret = klogctl(7, NULL, 0);
1265
#endif /* __linux__ */
1266
exitcode = EXIT_FAILURE;
1267
/* Lower privileges */
716
1275
if((network.ifr_flags & IFF_UP) == 0){
717
1276
network.ifr_flags |= IFF_UP;
1277
take_down_interface = true;
718
1278
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1280
take_down_interface = false;
720
1281
perror("ioctl SIOCSIFFLAGS");
721
returncode = EXIT_FAILURE;
728
avahi_set_log_function(empty_log);
731
/* Initialize the psuedo-RNG */
732
srand((unsigned int) time(NULL));
734
/* Allocate main loop object */
735
if (!(simple_poll = avahi_simple_poll_new())) {
736
fprintf(stderr, "Failed to create simple poll object.\n");
737
returncode = EXIT_FAILURE;
741
/* Do not publish any local records */
1282
exitcode = EXIT_FAILURE;
1284
if(restore_loglevel){
1285
ret = klogctl(7, NULL, 0);
1290
#endif /* __linux__ */
1291
/* Lower privileges */
1300
/* sleep checking until interface is running */
1301
for(int i=0; i < delay * 4; i++){
1302
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1304
perror("ioctl SIOCGIFFLAGS");
1305
} else if(network.ifr_flags & IFF_RUNNING){
1308
struct timespec sleeptime = { .tv_nsec = 250000000 };
1309
ret = nanosleep(&sleeptime, NULL);
1310
if(ret == -1 and errno != EINTR){
1311
perror("nanosleep");
1314
if(not take_down_interface){
1315
/* We won't need the socket anymore */
1316
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1322
if(restore_loglevel){
1323
/* Restores kernel loglevel to default */
1324
ret = klogctl(7, NULL, 0);
1329
#endif /* __linux__ */
1330
/* Lower privileges */
1332
if(take_down_interface){
1333
/* Lower privileges */
1339
/* Lower privileges permanently */
1351
ret = init_gnutls_global(pubkey, seckey);
1353
fprintf(stderr, "init_gnutls_global failed\n");
1354
exitcode = EXIT_FAILURE;
1357
gnutls_initialized = true;
1364
tempdir_created = true;
1365
if(mkdtemp(tempdir) == NULL){
1366
tempdir_created = false;
1375
if(not init_gpgme(pubkey, seckey, tempdir)){
1376
fprintf(stderr, "init_gpgme failed\n");
1377
exitcode = EXIT_FAILURE;
1380
gpgme_initialized = true;
1387
if(connect_to != NULL){
1388
/* Connect directly, do not use Zeroconf */
1389
/* (Mainly meant for debugging) */
1390
char *address = strrchr(connect_to, ':');
1391
if(address == NULL){
1392
fprintf(stderr, "No colon in address\n");
1393
exitcode = EXIT_FAILURE;
1403
tmpmax = strtoimax(address+1, &tmp, 10);
1404
if(errno != 0 or tmp == address+1 or *tmp != '\0'
1405
or tmpmax != (uint16_t)tmpmax){
1406
fprintf(stderr, "Bad port number\n");
1407
exitcode = EXIT_FAILURE;
1415
port = (uint16_t)tmpmax;
1417
address = connect_to;
1418
/* Colon in address indicates IPv6 */
1420
if(strchr(address, ':') != NULL){
1430
ret = start_mandos_communication(address, port, if_index, af);
1432
exitcode = EXIT_FAILURE;
1434
exitcode = EXIT_SUCCESS;
1444
AvahiServerConfig config;
1445
/* Do not publish any local Zeroconf records */
742
1446
avahi_server_config_init(&config);
743
1447
config.publish_hinfo = 0;
744
1448
config.publish_addresses = 0;
745
1449
config.publish_workstation = 0;
746
1450
config.publish_domain = 0;
748
1452
/* Allocate a new server */
749
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
750
&config, NULL, NULL, &error);
752
/* Free the configuration data */
1453
mc.server = avahi_server_new(avahi_simple_poll_get
1454
(mc.simple_poll), &config, NULL,
1457
/* Free the Avahi configuration data */
753
1458
avahi_server_config_free(&config);
755
/* Check if creating the server object succeeded */
757
fprintf(stderr, "Failed to create server: %s\n",
758
avahi_strerror(error));
759
returncode = EXIT_FAILURE;
763
/* Create the service browser */
764
sb = avahi_s_service_browser_new(server,
766
if_nametoindex(interface),
768
"_mandos._tcp", NULL, 0,
769
browse_callback, server);
771
fprintf(stderr, "Failed to create service browser: %s\n",
772
avahi_strerror(avahi_server_errno(server)));
773
returncode = EXIT_FAILURE;
777
/* Run the main loop */
780
fprintf(stderr, "Starting avahi loop search\n");
783
avahi_simple_poll_loop(simple_poll);
788
fprintf(stderr, "%s exiting\n", argv[0]);
793
avahi_s_service_browser_free(sb);
796
avahi_server_free(server);
799
avahi_simple_poll_free(simple_poll);
1461
/* Check if creating the Avahi server object succeeded */
1462
if(mc.server == NULL){
1463
fprintf(stderr, "Failed to create Avahi server: %s\n",
1464
avahi_strerror(error));
1465
exitcode = EXIT_FAILURE;
1473
/* Create the Avahi service browser */
1474
sb = avahi_s_service_browser_new(mc.server, if_index,
1475
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
1476
NULL, 0, browse_callback, NULL);
1478
fprintf(stderr, "Failed to create service browser: %s\n",
1479
avahi_strerror(avahi_server_errno(mc.server)));
1480
exitcode = EXIT_FAILURE;
1488
/* Run the main loop */
1491
fprintf(stderr, "Starting Avahi loop search\n");
1494
avahi_simple_poll_loop(mc.simple_poll);
1499
fprintf(stderr, "%s exiting\n", argv[0]);
1502
/* Cleanup things */
1504
avahi_s_service_browser_free(sb);
1506
if(mc.server != NULL)
1507
avahi_server_free(mc.server);
1509
if(mc.simple_poll != NULL)
1510
avahi_simple_poll_free(mc.simple_poll);
1512
if(gnutls_initialized){
1513
gnutls_certificate_free_credentials(mc.cred);
1514
gnutls_global_deinit();
1515
gnutls_dh_params_deinit(mc.dh_params);
1518
if(gpgme_initialized){
1519
gpgme_release(mc.ctx);
1522
/* Take down the network interface */
1523
if(take_down_interface){
1524
/* Re-raise priviliges */
1531
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1533
perror("ioctl SIOCGIFFLAGS");
1534
} else if(network.ifr_flags & IFF_UP) {
1535
network.ifr_flags &= ~IFF_UP; /* clear flag */
1536
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1538
perror("ioctl SIOCSIFFLAGS");
1541
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1545
/* Lower privileges permanently */
1554
/* Removes the temp directory used by GPGME */
1555
if(tempdir_created){
1557
struct dirent *direntry;
1558
d = opendir(tempdir);
1560
if(errno != ENOENT){
1565
direntry = readdir(d);
1566
if(direntry == NULL){
1569
/* Skip "." and ".." */
1570
if(direntry->d_name[0] == '.'
1571
and (direntry->d_name[1] == '\0'
1572
or (direntry->d_name[1] == '.'
1573
and direntry->d_name[2] == '\0'))){
1576
char *fullname = NULL;
1577
ret = asprintf(&fullname, "%s/%s", tempdir,
1583
ret = remove(fullname);
1585
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1592
ret = rmdir(tempdir);
1593
if(ret == -1 and errno != ENOENT){
1599
sigemptyset(&old_sigterm_action.sa_mask);
1600
old_sigterm_action.sa_handler = SIG_DFL;
1601
ret = sigaction(signal_received, &old_sigterm_action, NULL);
1603
perror("sigaction");
1605
raise(signal_received);