26
25
* along with this program. If not, see
27
26
* <http://www.gnu.org/licenses/>.
29
* Contact the authors at <mandos@fukt.bsnet.se>.
28
* Contact the authors at <https://www.fukt.bsnet.se/~belorn/> and
29
* <https://www.fukt.bsnet.se/~teddy/>.
32
/* Needed by GPGME, specifically gpgme_data_seek() */
33
#ifndef _LARGEFILE_SOURCE
32
#define _FORTIFY_SOURCE 2
34
34
#define _LARGEFILE_SOURCE
36
#ifndef _FILE_OFFSET_BITS
37
35
#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(), 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
41
#include <net/if.h> /* if_nametoindex */
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
const char *certdir = "/conf/conf.d/cryptkeyreq/";
75
const char *certfile = "openpgp-client.txt";
76
const char *certkey = "openpgp-client-key.txt";
124
78
bool debug = false;
125
static const char mandos_protocol_version[] = "1";
126
const char *argp_program_version = "mandos-client " VERSION;
127
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
129
/* Used for passing in values through the Avahi callback functions */
131
AvahiSimplePoll *simple_poll;
81
gnutls_session_t session;
133
82
gnutls_certificate_credentials_t cred;
134
unsigned int dh_bits;
135
83
gnutls_dh_params_t dh_params;
136
const char *priority;
87
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
88
char **new_packet, const char *homedir){
89
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){
93
ssize_t new_packet_capacity = 0;
94
ssize_t new_packet_length = 0;
168
95
gpgme_engine_info_t engine_info;
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");
98
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
212
102
gpgme_check_version(NULL);
213
103
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
214
if(rc != GPG_ERR_NO_ERROR){
104
if (rc != GPG_ERR_NO_ERROR){
215
105
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
216
106
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){
110
/* Set GPGME home directory */
111
rc = gpgme_get_engine_info (&engine_info);
112
if (rc != GPG_ERR_NO_ERROR){
223
113
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
224
114
gpgme_strsource(rc), gpgme_strerror(rc));
227
117
while(engine_info != NULL){
228
118
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
229
119
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
230
engine_info->file_name, tempdir);
120
engine_info->file_name, homedir);
233
123
engine_info = engine_info->next;
235
125
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){
126
fprintf(stderr, "Could not set home dir to %s\n", homedir);
130
/* Create new GPGME data buffer from packet buffer */
131
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
132
if (rc != GPG_ERR_NO_ERROR){
276
133
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
277
134
gpgme_strsource(rc), gpgme_strerror(rc));
327
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
196
/* Delete the GPGME FILE pointer cryptotext data buffer */
197
gpgme_data_release(dh_crypto);
330
199
/* 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;
200
gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET);
339
plaintext_capacity = incbuffer(plaintext,
340
(size_t)plaintext_length,
342
if(plaintext_capacity == 0){
344
plaintext_length = -1;
204
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
205
*new_packet = realloc(*new_packet,
206
(unsigned int)new_packet_capacity
208
if (*new_packet == NULL){
212
new_packet_capacity += BUFFER_SIZE;
348
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
215
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
350
217
/* Print the data, if any */
356
222
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);
225
new_packet_length += ret;
228
/* FIXME: check characters before printing to screen so to not print
229
terminal control characters */
231
/* fprintf(stderr, "decrypted password is: "); */
232
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
233
/* fprintf(stderr, "\n"); */
376
236
/* Delete the GPGME plaintext data buffer */
377
237
gpgme_data_release(dh_plain);
378
return plaintext_length;
238
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 */
241
static const char * safer_gnutls_strerror (int value) {
242
const char *ret = gnutls_strerror (value);
385
244
ret = "(unknown)";
389
/* GnuTLS log function callback */
390
static void debuggnutls(__attribute__((unused)) int level,
392
fprintf(stderr, "GnuTLS: %s", string);
248
void debuggnutls(__attribute__((unused)) int level,
250
fprintf(stderr, "%s", string);
395
static int init_gnutls_global(const char *pubkeyfilename,
396
const char *seckeyfilename){
253
int initgnutls(encrypted_session *es){
400
258
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));
261
if ((ret = gnutls_global_init ())
262
!= GNUTLS_E_SUCCESS) {
263
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
411
/* "Use a log level over 10 to enable all debugging options."
414
268
gnutls_global_set_log_level(11);
415
269
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();
272
/* openpgp credentials */
273
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
274
!= GNUTLS_E_SUCCESS) {
275
fprintf (stderr, "memory error: %s\n",
276
safer_gnutls_strerror(ret));
431
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
432
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
281
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
282
" and keyfile %s as GnuTLS credentials\n", certfile,
436
286
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 */
478
ret = gnutls_init(session, GNUTLS_SERVER);
479
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
480
if(ret != GNUTLS_E_SUCCESS){
287
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
288
if (ret != GNUTLS_E_SUCCESS) {
290
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
292
ret, certfile, certkey);
293
fprintf(stdout, "The Error is: %s\n",
294
safer_gnutls_strerror(ret));
298
//GnuTLS server initialization
299
if ((ret = gnutls_dh_params_init (&es->dh_params))
300
!= GNUTLS_E_SUCCESS) {
301
fprintf (stderr, "Error in dh parameter initialization: %s\n",
302
safer_gnutls_strerror(ret));
306
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
307
!= GNUTLS_E_SUCCESS) {
308
fprintf (stderr, "Error in prime generation: %s\n",
309
safer_gnutls_strerror(ret));
313
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
315
// GnuTLS session creation
316
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
317
!= GNUTLS_E_SUCCESS){
481
318
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
482
319
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);
322
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
323
!= GNUTLS_E_SUCCESS) {
324
fprintf(stderr, "Syntax error at: %s\n", err);
325
fprintf(stderr, "GnuTLS error: %s\n",
326
safer_gnutls_strerror(ret));
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",
330
if ((ret = gnutls_credentials_set
331
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
332
!= GNUTLS_E_SUCCESS) {
333
fprintf(stderr, "Error setting a credentials set: %s\n",
505
334
safer_gnutls_strerror(ret));
506
gnutls_deinit(*session);
510
338
/* ignore client certificate if any. */
511
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
339
gnutls_certificate_server_set_request (es->session,
513
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
342
gnutls_dh_set_prime_bits (es->session, DH_BITS);
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;
347
void empty_log(__attribute__((unused)) AvahiLogLevel level,
348
__attribute__((unused)) const char *txt){}
350
int start_mandos_communication(const char *ip, uint16_t port,
351
unsigned int if_index){
353
struct sockaddr_in6 to;
354
encrypted_session es;
535
355
char *buffer = NULL;
536
356
char *decrypted_buffer;
537
357
size_t buffer_length = 0;
538
358
size_t buffer_capacity = 0;
359
ssize_t decrypted_buffer_size;
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);
362
char interface[IF_NAMESIZE];
566
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
365
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
570
tcp_sd = socket(pf, SOCK_STREAM, 0);
368
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
572
370
perror("socket");
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);
374
if(if_indextoname(if_index, interface) == NULL){
376
perror("if_indextoname");
382
fprintf(stderr, "Binding to interface %s\n", interface);
385
memset(&to,0,sizeof(to)); /* Spurious warning */
386
to.sin6_family = AF_INET6;
387
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
590
389
perror("inet_pton");
595
393
fprintf(stderr, "Bad address: %s\n", ip);
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 */
396
to.sin6_port = htons(port); /* Spurious warning */
398
to.sin6_scope_id = (uint32_t)if_index;
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 */
401
fprintf(stderr, "Connection to: %s\n", ip);
404
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
668
406
perror("connect");
410
ret = initgnutls (&es);
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){
416
gnutls_transport_set_ptr (es.session,
417
(gnutls_transport_ptr_t) tcp_sd);
706
420
fprintf(stderr, "Establishing TLS session with %s\n", ip);
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){
423
ret = gnutls_handshake (es.session);
425
if (ret != GNUTLS_E_SUCCESS){
728
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
427
fprintf(stderr, "\n*** Handshake failed ***\n");
735
/* Read OpenPGP packet that contains the wanted password */
434
//Retrieve OpenPGP packet that contains the wanted password
738
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
437
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
748
buffer_capacity = incbuffer(&buffer, buffer_length,
750
if(buffer_capacity == 0){
760
sret = gnutls_record_recv(session, buffer+buffer_length,
442
if (buffer_length + BUFFER_SIZE > buffer_capacity){
443
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
448
buffer_capacity += BUFFER_SIZE;
451
ret = gnutls_record_recv
452
(es.session, buffer+buffer_length, BUFFER_SIZE);
767
458
case GNUTLS_E_INTERRUPTED:
768
459
case GNUTLS_E_AGAIN:
770
461
case GNUTLS_E_REHANDSHAKE:
772
ret = gnutls_handshake(session);
777
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
779
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
462
ret = gnutls_handshake (es.session);
464
fprintf(stderr, "\n*** Handshake failed ***\n");
786
471
fprintf(stderr, "Unknown error while reading data from"
787
" encrypted session with Mandos server\n");
472
" encrypted session with mandos server\n");
789
gnutls_bye(session, GNUTLS_SHUT_RDWR);
474
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
793
buffer_length += (size_t) sret;
478
buffer_length += (size_t) ret;
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;
482
if (buffer_length > 0){
813
483
decrypted_buffer_size = pgp_packet_decrypt(buffer,
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,
487
if (decrypted_buffer_size >= 0){
488
while(written < decrypted_buffer_size){
489
ret = (int)fwrite (decrypted_buffer + written, 1,
490
(size_t)decrypted_buffer_size - written,
827
492
if(ret == 0 and ferror(stdout)){
829
494
fprintf(stderr, "Error writing encrypted data: %s\n",
895
557
char ip[AVAHI_ADDRESS_STR_MAX];
896
558
avahi_address_snprint(ip, sizeof(ip), address);
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);
560
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
561
" port %d\n", name, host_name, ip, port);
902
int ret = start_mandos_communication(ip, port, interface,
903
avahi_proto_to_af(proto));
905
avahi_simple_poll_quit(mc.simple_poll);
563
int ret = start_mandos_communication(ip, port,
564
(unsigned int) interface);
909
570
avahi_s_service_resolver_free(r);
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");
573
static void browse_callback(
574
AvahiSServiceBrowser *b,
575
AvahiIfIndex interface,
576
AvahiProtocol protocol,
577
AvahiBrowserEvent event,
581
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
584
AvahiServer *s = userdata;
585
assert(b); /* Spurious warning */
587
/* Called whenever a new services becomes available on the LAN or
588
is removed from the LAN */
592
case AVAHI_BROWSER_FAILURE:
594
fprintf(stderr, "(Browser) %s\n",
595
avahi_strerror(avahi_server_errno(server)));
596
avahi_simple_poll_quit(simple_poll);
599
case AVAHI_BROWSER_NEW:
600
/* We ignore the returned resolver object. In the callback
601
function we free it. If the server is terminated before
602
the callback function is called the server will free
603
the resolver for us. */
605
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
607
AVAHI_PROTO_INET6, 0,
608
resolve_callback, s)))
609
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
610
avahi_strerror(avahi_server_errno(s)));
613
case AVAHI_BROWSER_REMOVE:
616
case AVAHI_BROWSER_ALL_FOR_NOW:
617
case AVAHI_BROWSER_CACHE_EXHAUSTED:
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;
622
/* combinds two strings and returns the malloced new string. som sane checks could/should be added */
623
const char *combinestrings(const char *first, const char *second){
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;
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",
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;
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__ */
1234
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1237
exitcode = EXIT_FAILURE;
1239
if(restore_loglevel){
1240
ret = klogctl(7, NULL, 0);
1245
#endif /* __linux__ */
1246
/* Lower privileges */
1254
strcpy(network.ifr_name, interface);
1255
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1257
perror("ioctl SIOCGIFFLAGS");
1259
if(restore_loglevel){
1260
ret = klogctl(7, NULL, 0);
1265
#endif /* __linux__ */
1266
exitcode = EXIT_FAILURE;
1267
/* Lower privileges */
1275
if((network.ifr_flags & IFF_UP) == 0){
1276
network.ifr_flags |= IFF_UP;
1277
take_down_interface = true;
1278
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1280
take_down_interface = false;
1281
perror("ioctl SIOCSIFFLAGS");
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;
625
tmp = malloc(strlen(first) + strlen(second));
636
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
1444
637
AvahiServerConfig config;
1445
/* Do not publish any local Zeroconf records */
638
AvahiSServiceBrowser *sb = NULL;
641
int returncode = EXIT_SUCCESS;
642
const char *interface = "eth0";
645
static struct option long_options[] = {
646
{"debug", no_argument, (int *)&debug, 1},
647
{"interface", required_argument, 0, 'i'},
648
{"certdir", required_argument, 0, 'd'},
649
{"certkey", required_argument, 0, 'c'},
650
{"certfile", required_argument, 0, 'k'},
653
int option_index = 0;
654
ret = getopt_long (argc, argv, "i:", long_options,
681
certfile = combinestrings(certdir, certfile);
682
if (certfile == NULL){
686
certkey = combinestrings(certdir, certkey);
687
if (certkey == NULL){
692
avahi_set_log_function(empty_log);
695
/* Initialize the psuedo-RNG */
696
srand((unsigned int) time(NULL));
698
/* Allocate main loop object */
699
if (!(simple_poll = avahi_simple_poll_new())) {
700
fprintf(stderr, "Failed to create simple poll object.\n");
705
/* Do not publish any local records */
1446
706
avahi_server_config_init(&config);
1447
707
config.publish_hinfo = 0;
1448
708
config.publish_addresses = 0;
1449
709
config.publish_workstation = 0;
1450
710
config.publish_domain = 0;
1452
712
/* Allocate a new server */
1453
mc.server = avahi_server_new(avahi_simple_poll_get
1454
(mc.simple_poll), &config, NULL,
1457
/* Free the Avahi configuration data */
713
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
714
&config, NULL, NULL, &error);
716
/* Free the configuration data */
1458
717
avahi_server_config_free(&config);
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);
719
/* Check if creating the server object succeeded */
721
fprintf(stderr, "Failed to create server: %s\n",
722
avahi_strerror(error));
723
returncode = EXIT_FAILURE;
727
/* Create the service browser */
728
sb = avahi_s_service_browser_new(server,
730
if_nametoindex(interface),
732
"_mandos._tcp", NULL, 0,
733
browse_callback, server);
735
fprintf(stderr, "Failed to create service browser: %s\n",
736
avahi_strerror(avahi_server_errno(server)));
737
returncode = EXIT_FAILURE;
741
/* Run the main loop */
744
fprintf(stderr, "Starting avahi loop search\n");
747
avahi_simple_poll_loop(simple_poll);
752
fprintf(stderr, "%s exiting\n", argv[0]);
757
avahi_s_service_browser_free(sb);
760
avahi_server_free(server);
763
avahi_simple_poll_free(simple_poll);