45
98
#include <avahi-common/malloc.h>
46
99
#include <avahi-common/error.h>
49
#include <sys/types.h> /* socket(), inet_pton() */
50
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
51
struct in6_addr, inet_pton() */
52
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
53
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
55
#include <unistd.h> /* close() */
56
#include <netinet/in.h>
57
#include <stdbool.h> /* true */
58
#include <string.h> /* memset */
59
#include <arpa/inet.h> /* inet_pton() */
60
#include <iso646.h> /* not */
63
#include <errno.h> /* perror() */
102
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
105
init_gnutls_session(),
107
#include <gnutls/openpgp.h>
108
/* gnutls_certificate_set_openpgp_key_file(),
109
GNUTLS_OPENPGP_FMT_BASE64 */
112
#include <gpgme.h> /* All GPGME types, constants and
115
GPGME_PROTOCOL_OpenPGP,
69
118
#define BUFFER_SIZE 256
72
static const char *certdir = "/conf/conf.d/mandos";
73
static const char *certfile = "openpgp-client.txt";
74
static const char *certkey = "openpgp-client-key.txt";
120
#define PATHDIR "/conf/conf.d/mandos"
121
#define SECKEY "seckey.txt"
122
#define PUBKEY "pubkey.txt"
76
124
bool debug = false;
125
static const char mandos_protocol_version[] = "1";
126
const char *argp_program_version = "mandos-client " VERSION;
127
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
129
/* Used for passing in values through the Avahi callback functions */
79
gnutls_session_t session;
131
AvahiSimplePoll *simple_poll;
80
133
gnutls_certificate_credentials_t cred;
134
unsigned int dh_bits;
81
135
gnutls_dh_params_t dh_params;
85
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
88
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){
92
ssize_t new_packet_capacity = 0;
93
ssize_t new_packet_length = 0;
94
168
gpgme_engine_info_t engine_info;
97
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");
101
212
gpgme_check_version(NULL);
102
213
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
103
if (rc != GPG_ERR_NO_ERROR){
214
if(rc != GPG_ERR_NO_ERROR){
104
215
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
105
216
gpgme_strsource(rc), gpgme_strerror(rc));
109
/* Set GPGME home directory */
110
rc = gpgme_get_engine_info (&engine_info);
111
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){
112
223
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
113
224
gpgme_strsource(rc), gpgme_strerror(rc));
116
227
while(engine_info != NULL){
117
228
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
118
229
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
119
engine_info->file_name, homedir);
230
engine_info->file_name, tempdir);
122
233
engine_info = engine_info->next;
124
235
if(engine_info == NULL){
125
fprintf(stderr, "Could not set home dir to %s\n", homedir);
129
/* Create new GPGME data buffer from packet buffer */
130
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
131
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){
132
276
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
133
277
gpgme_strsource(rc), gpgme_strerror(rc));
195
/* Delete the GPGME FILE pointer cryptotext data buffer */
196
gpgme_data_release(dh_crypto);
327
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
198
330
/* Seek back to the beginning of the GPGME plaintext data buffer */
199
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
200
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;
205
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
206
*new_packet = realloc(*new_packet,
207
(unsigned int)new_packet_capacity
209
if (*new_packet == NULL){
213
new_packet_capacity += BUFFER_SIZE;
339
plaintext_capacity = incbuffer(plaintext,
340
(size_t)plaintext_length,
342
if(plaintext_capacity == 0){
344
plaintext_length = -1;
216
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
348
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
218
350
/* Print the data, if any */
223
356
perror("gpgme_data_read");
226
new_packet_length += ret;
229
/* FIXME: check characters before printing to screen so to not print
230
terminal control characters */
232
/* fprintf(stderr, "decrypted password is: "); */
233
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
234
/* 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);
237
376
/* Delete the GPGME plaintext data buffer */
238
377
gpgme_data_release(dh_plain);
239
return new_packet_length;
378
return plaintext_length;
242
static const char * safer_gnutls_strerror (int value) {
243
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 */
245
385
ret = "(unknown)";
389
/* GnuTLS log function callback */
249
390
static void debuggnutls(__attribute__((unused)) int level,
250
391
const char* string){
251
fprintf(stderr, "%s", string);
392
fprintf(stderr, "GnuTLS: %s", string);
254
static int initgnutls(encrypted_session *es){
395
static int init_gnutls_global(const char *pubkeyfilename,
396
const char *seckeyfilename){
259
400
fprintf(stderr, "Initializing GnuTLS\n");
262
if ((ret = gnutls_global_init ())
263
!= GNUTLS_E_SUCCESS) {
264
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."
269
414
gnutls_global_set_log_level(11);
270
415
gnutls_global_set_log_function(debuggnutls);
273
/* openpgp credentials */
274
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
275
!= GNUTLS_E_SUCCESS) {
276
fprintf (stderr, "memory error: %s\n",
277
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();
282
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
283
" 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,
287
436
ret = gnutls_certificate_set_openpgp_key_file
288
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
289
if (ret != GNUTLS_E_SUCCESS) {
291
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
293
ret, certfile, certkey);
294
fprintf(stdout, "The Error is: %s\n",
295
safer_gnutls_strerror(ret));
299
//GnuTLS server initialization
300
if ((ret = gnutls_dh_params_init (&es->dh_params))
301
!= GNUTLS_E_SUCCESS) {
302
fprintf (stderr, "Error in dh parameter initialization: %s\n",
303
safer_gnutls_strerror(ret));
307
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
308
!= GNUTLS_E_SUCCESS) {
309
fprintf (stderr, "Error in prime generation: %s\n",
310
safer_gnutls_strerror(ret));
314
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
316
// GnuTLS session creation
317
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
318
!= 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){
319
481
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
320
482
safer_gnutls_strerror(ret));
323
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
324
!= GNUTLS_E_SUCCESS) {
325
fprintf(stderr, "Syntax error at: %s\n", err);
326
fprintf(stderr, "GnuTLS error: %s\n",
327
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);
331
if ((ret = gnutls_credentials_set
332
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
333
!= GNUTLS_E_SUCCESS) {
334
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",
335
505
safer_gnutls_strerror(ret));
506
gnutls_deinit(*session);
339
510
/* ignore client certificate if any. */
340
gnutls_certificate_server_set_request (es->session,
511
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
343
gnutls_dh_set_prime_bits (es->session, DH_BITS);
513
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
518
/* Avahi log function callback */
348
519
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
349
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 */
351
526
static int start_mandos_communication(const char *ip, uint16_t port,
352
AvahiIfIndex if_index){
354
struct sockaddr_in6 to;
355
encrypted_session es;
527
AvahiIfIndex if_index,
529
int ret, tcp_sd = -1;
532
struct sockaddr_in in;
533
struct sockaddr_in6 in6;
356
535
char *buffer = NULL;
357
536
char *decrypted_buffer;
358
537
size_t buffer_length = 0;
359
538
size_t buffer_capacity = 0;
360
ssize_t decrypted_buffer_size;
363
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);
366
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
566
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
370
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
570
tcp_sd = socket(pf, SOCK_STREAM, 0);
372
572
perror("socket");
376
if(if_indextoname((unsigned int)if_index, interface) == NULL){
378
perror("if_indextoname");
384
fprintf(stderr, "Binding to interface %s\n", interface);
387
memset(&to,0,sizeof(to)); /* Spurious warning */
388
to.sin6_family = AF_INET6;
389
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);
391
590
perror("inet_pton");
395
595
fprintf(stderr, "Bad address: %s\n", ip);
398
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 */
400
to.sin6_scope_id = (uint32_t)if_index;
403
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
404
/* char addrstr[INET6_ADDRSTRLEN]; */
405
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
406
/* sizeof(addrstr)) == NULL){ */
407
/* perror("inet_ntop"); */
409
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
410
/* addrstr, ntohs(to.sin6_port)); */
414
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 */
416
668
perror("connect");
420
ret = initgnutls (&es);
426
gnutls_transport_set_ptr (es.session,
427
(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){
430
706
fprintf(stderr, "Establishing TLS session with %s\n", ip);
433
ret = gnutls_handshake (es.session);
435
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){
437
fprintf(stderr, "\n*** Handshake failed ***\n");
728
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
444
//Retrieve OpenPGP packet that contains the wanted password
735
/* Read OpenPGP packet that contains the wanted password */
447
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
738
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
452
if (buffer_length + BUFFER_SIZE > buffer_capacity){
453
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
458
buffer_capacity += BUFFER_SIZE;
461
ret = gnutls_record_recv
462
(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,
468
767
case GNUTLS_E_INTERRUPTED:
469
768
case GNUTLS_E_AGAIN:
471
770
case GNUTLS_E_REHANDSHAKE:
472
ret = gnutls_handshake (es.session);
474
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");
481
786
fprintf(stderr, "Unknown error while reading data from"
482
" encrypted session with mandos server\n");
787
" encrypted session with Mandos server\n");
484
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
789
gnutls_bye(session, GNUTLS_SHUT_RDWR);
488
buffer_length += (size_t) ret;
793
buffer_length += (size_t) sret;
492
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;
493
813
decrypted_buffer_size = pgp_packet_decrypt(buffer,
497
if (decrypted_buffer_size >= 0){
816
if(decrypted_buffer_size >= 0){
498
819
while(written < (size_t) decrypted_buffer_size){
499
ret = (int)fwrite (decrypted_buffer + written, 1,
500
(size_t)decrypted_buffer_size - written,
824
ret = (int)fwrite(decrypted_buffer + written, 1,
825
(size_t)decrypted_buffer_size - written,
502
827
if(ret == 0 and ferror(stdout)){
504
829
fprintf(stderr, "Error writing encrypted data: %s\n",
567
895
char ip[AVAHI_ADDRESS_STR_MAX];
568
896
avahi_address_snprint(ip, sizeof(ip), address);
570
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
571
" port %d\n", name, host_name, ip, port);
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);
573
int ret = start_mandos_communication(ip, port, interface);
902
int ret = start_mandos_communication(ip, port, interface,
903
avahi_proto_to_af(proto));
905
avahi_simple_poll_quit(mc.simple_poll);
579
909
avahi_s_service_resolver_free(r);
582
static void browse_callback(
583
AvahiSServiceBrowser *b,
584
AvahiIfIndex interface,
585
AvahiProtocol protocol,
586
AvahiBrowserEvent event,
590
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
593
AvahiServer *s = userdata;
594
assert(b); /* Spurious warning */
596
/* Called whenever a new services becomes available on the LAN or
597
is removed from the LAN */
601
case AVAHI_BROWSER_FAILURE:
603
fprintf(stderr, "(Browser) %s\n",
604
avahi_strerror(avahi_server_errno(server)));
605
avahi_simple_poll_quit(simple_poll);
608
case AVAHI_BROWSER_NEW:
609
/* We ignore the returned resolver object. In the callback
610
function we free it. If the server is terminated before
611
the callback function is called the server will free
612
the resolver for us. */
614
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
616
AVAHI_PROTO_INET6, 0,
617
resolve_callback, s)))
618
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
619
avahi_strerror(avahi_server_errno(s)));
622
case AVAHI_BROWSER_REMOVE:
625
case AVAHI_BROWSER_ALL_FOR_NOW:
626
case AVAHI_BROWSER_CACHE_EXHAUSTED:
631
/* Combines file name and path and returns the malloced new
632
string. some sane checks could/should be added */
633
static const char *combinepath(const char *first, const char *second){
634
size_t f_len = strlen(first);
635
size_t s_len = strlen(second);
636
char *tmp = malloc(f_len + s_len + 2);
641
memcpy(tmp, first, f_len);
645
memcpy(tmp + f_len + 1, second, s_len);
647
tmp[f_len + 1 + s_len] = '\0';
652
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
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");
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;
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",
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;
653
1444
AvahiServerConfig config;
654
AvahiSServiceBrowser *sb = NULL;
657
int returncode = EXIT_SUCCESS;
658
const char *interface = NULL;
659
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
660
char *connect_to = NULL;
663
static struct option long_options[] = {
664
{"debug", no_argument, (int *)&debug, 1},
665
{"connect", required_argument, 0, 'C'},
666
{"interface", required_argument, 0, 'i'},
667
{"certdir", required_argument, 0, 'd'},
668
{"certkey", required_argument, 0, 'c'},
669
{"certfile", required_argument, 0, 'k'},
672
int option_index = 0;
673
ret = getopt_long (argc, argv, "i:", long_options,
703
certfile = combinepath(certdir, certfile);
704
if (certfile == NULL){
705
perror("combinepath");
709
if(interface != NULL){
710
if_index = (AvahiIfIndex) if_nametoindex(interface);
712
fprintf(stderr, "No such interface: \"%s\"\n", interface);
717
if(connect_to != NULL){
718
/* Connect directly, do not use Zeroconf */
719
/* (Mainly meant for debugging) */
720
char *address = strrchr(connect_to, ':');
722
fprintf(stderr, "No colon in address\n");
726
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
728
perror("Bad port number");
732
address = connect_to;
733
ret = start_mandos_communication(address, port, if_index);
741
certkey = combinepath(certdir, certkey);
742
if (certkey == NULL){
743
perror("combinepath");
748
avahi_set_log_function(empty_log);
751
/* Initialize the psuedo-RNG */
752
srand((unsigned int) time(NULL));
754
/* Allocate main loop object */
755
if (!(simple_poll = avahi_simple_poll_new())) {
756
fprintf(stderr, "Failed to create simple poll object.\n");
761
/* Do not publish any local records */
1445
/* Do not publish any local Zeroconf records */
762
1446
avahi_server_config_init(&config);
763
1447
config.publish_hinfo = 0;
764
1448
config.publish_addresses = 0;
765
1449
config.publish_workstation = 0;
766
1450
config.publish_domain = 0;
768
1452
/* Allocate a new server */
769
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
770
&config, NULL, NULL, &error);
772
/* 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 */
773
1458
avahi_server_config_free(&config);
775
/* Check if creating the server object succeeded */
777
fprintf(stderr, "Failed to create server: %s\n",
778
avahi_strerror(error));
779
returncode = EXIT_FAILURE;
783
/* Create the service browser */
784
sb = avahi_s_service_browser_new(server, if_index,
786
"_mandos._tcp", NULL, 0,
787
browse_callback, server);
789
fprintf(stderr, "Failed to create service browser: %s\n",
790
avahi_strerror(avahi_server_errno(server)));
791
returncode = EXIT_FAILURE;
795
/* Run the main loop */
798
fprintf(stderr, "Starting avahi loop search\n");
801
avahi_simple_poll_loop(simple_poll);
806
fprintf(stderr, "%s exiting\n", argv[0]);
811
avahi_s_service_browser_free(sb);
814
avahi_server_free(server);
817
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);