98
45
#include <avahi-common/malloc.h>
99
46
#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,
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() */
118
69
#define BUFFER_SIZE 256
120
#define PATHDIR "/conf/conf.d/mandos"
121
#define SECKEY "seckey.txt"
122
#define PUBKEY "pubkey.txt"
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";
124
76
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;
79
gnutls_session_t session;
133
80
gnutls_certificate_credentials_t cred;
134
unsigned int dh_bits;
135
81
gnutls_dh_params_t dh_params;
136
const char *priority;
85
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
88
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){
92
ssize_t new_packet_capacity = 0;
93
ssize_t new_packet_length = 0;
168
94
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");
97
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
212
101
gpgme_check_version(NULL);
213
102
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
214
if(rc != GPG_ERR_NO_ERROR){
103
if (rc != GPG_ERR_NO_ERROR){
215
104
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
216
105
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){
109
/* Set GPGME home directory */
110
rc = gpgme_get_engine_info (&engine_info);
111
if (rc != GPG_ERR_NO_ERROR){
223
112
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
224
113
gpgme_strsource(rc), gpgme_strerror(rc));
227
116
while(engine_info != NULL){
228
117
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
229
118
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
230
engine_info->file_name, tempdir);
119
engine_info->file_name, homedir);
233
122
engine_info = engine_info->next;
235
124
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){
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){
276
132
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
277
133
gpgme_strsource(rc), gpgme_strerror(rc));
327
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
195
/* Delete the GPGME FILE pointer cryptotext data buffer */
196
gpgme_data_release(dh_crypto);
330
198
/* 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;
199
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
200
perror("pgpme_data_seek");
339
plaintext_capacity = incbuffer(plaintext,
340
(size_t)plaintext_length,
342
if(plaintext_capacity == 0){
344
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;
348
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
216
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
350
218
/* Print the data, if any */
356
223
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);
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"); */
376
237
/* Delete the GPGME plaintext data buffer */
377
238
gpgme_data_release(dh_plain);
378
return plaintext_length;
239
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 */
242
static const char * safer_gnutls_strerror (int value) {
243
const char *ret = gnutls_strerror (value);
385
245
ret = "(unknown)";
389
/* GnuTLS log function callback */
390
249
static void debuggnutls(__attribute__((unused)) int level,
391
250
const char* string){
392
fprintf(stderr, "GnuTLS: %s", string);
251
fprintf(stderr, "%s", string);
395
static int init_gnutls_global(const char *pubkeyfilename,
396
const char *seckeyfilename){
254
static int initgnutls(encrypted_session *es){
400
259
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));
262
if ((ret = gnutls_global_init ())
263
!= GNUTLS_E_SUCCESS) {
264
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
411
/* "Use a log level over 10 to enable all debugging options."
414
269
gnutls_global_set_log_level(11);
415
270
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();
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));
431
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
432
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
282
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
283
" and keyfile %s as GnuTLS credentials\n", certfile,
436
287
ret = gnutls_certificate_set_openpgp_key_file
437
(mc.cred, pubkeyfilename, seckeyfilename,
438
GNUTLS_OPENPGP_FMT_BASE64);
439
if(ret != GNUTLS_E_SUCCESS){
441
"Error[%d] while reading the OpenPGP key pair ('%s',"
442
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
443
fprintf(stderr, "The GnuTLS error is: %s\n",
444
safer_gnutls_strerror(ret));
448
/* GnuTLS server initialization */
449
ret = gnutls_dh_params_init(&mc.dh_params);
450
if(ret != GNUTLS_E_SUCCESS){
451
fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
452
" %s\n", safer_gnutls_strerror(ret));
455
ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
456
if(ret != GNUTLS_E_SUCCESS){
457
fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
458
safer_gnutls_strerror(ret));
462
gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
468
gnutls_certificate_free_credentials(mc.cred);
469
gnutls_global_deinit();
470
gnutls_dh_params_deinit(mc.dh_params);
474
static int init_gnutls_session(gnutls_session_t *session){
476
/* GnuTLS session creation */
477
ret = gnutls_init(session, GNUTLS_SERVER);
478
if(ret != GNUTLS_E_SUCCESS){
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){
479
319
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
480
320
safer_gnutls_strerror(ret));
485
ret = gnutls_priority_set_direct(*session, mc.priority, &err);
486
if(ret != GNUTLS_E_SUCCESS){
487
fprintf(stderr, "Syntax error at: %s\n", err);
488
fprintf(stderr, "GnuTLS error: %s\n",
489
safer_gnutls_strerror(ret));
490
gnutls_deinit(*session);
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));
495
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
497
if(ret != GNUTLS_E_SUCCESS){
498
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
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",
499
335
safer_gnutls_strerror(ret));
500
gnutls_deinit(*session);
504
339
/* ignore client certificate if any. */
505
gnutls_certificate_server_set_request(*session,
340
gnutls_certificate_server_set_request (es->session,
508
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
343
gnutls_dh_set_prime_bits (es->session, DH_BITS);
513
/* Avahi log function callback */
514
348
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
515
349
__attribute__((unused)) const char *txt){}
517
sig_atomic_t quit_now = 0;
518
int signal_received = 0;
520
/* Called when a Mandos server is found */
521
351
static int start_mandos_communication(const char *ip, uint16_t port,
522
AvahiIfIndex if_index,
524
int ret, tcp_sd = -1;
527
struct sockaddr_in in;
528
struct sockaddr_in6 in6;
352
AvahiIfIndex if_index){
354
struct sockaddr_in6 to;
355
encrypted_session es;
530
356
char *buffer = NULL;
531
357
char *decrypted_buffer;
532
358
size_t buffer_length = 0;
533
359
size_t buffer_capacity = 0;
360
ssize_t decrypted_buffer_size;
536
gnutls_session_t session;
537
int pf; /* Protocol family */
551
fprintf(stderr, "Bad address family: %d\n", af);
555
ret = init_gnutls_session(&session);
363
char interface[IF_NAMESIZE];
561
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
366
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
565
tcp_sd = socket(pf, SOCK_STREAM, 0);
370
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
567
372
perror("socket");
576
memset(&to, 0, sizeof(to));
578
to.in6.sin6_family = (sa_family_t)af;
579
ret = inet_pton(af, ip, &to.in6.sin6_addr);
581
to.in.sin_family = (sa_family_t)af;
582
ret = inet_pton(af, ip, &to.in.sin_addr);
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);
585
391
perror("inet_pton");
590
395
fprintf(stderr, "Bad address: %s\n", ip);
595
to.in6.sin6_port = htons(port); /* Spurious warnings from
597
-Wunreachable-code */
599
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
600
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
602
if(if_index == AVAHI_IF_UNSPEC){
603
fprintf(stderr, "An IPv6 link-local address is incomplete"
604
" without a network interface\n");
608
/* Set the network interface number as scope */
609
to.in6.sin6_scope_id = (uint32_t)if_index;
612
to.in.sin_port = htons(port); /* Spurious warnings from
614
-Wunreachable-code */
398
to.sin6_port = htons(port); /* Spurious warning */
400
to.sin6_scope_id = (uint32_t)if_index;
622
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
623
char interface[IF_NAMESIZE];
624
if(if_indextoname((unsigned int)if_index, interface) == NULL){
625
perror("if_indextoname");
627
fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
628
ip, interface, port);
631
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
634
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
635
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
638
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
641
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
647
if(strcmp(addrstr, ip) != 0){
648
fprintf(stderr, "Canonical address form: %s\n", addrstr);
658
ret = connect(tcp_sd, &to.in6, sizeof(to));
660
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
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));
663
416
perror("connect");
420
ret = initgnutls (&es);
672
const char *out = mandos_protocol_version;
675
size_t out_size = strlen(out);
676
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
677
out_size - written));
683
written += (size_t)ret;
684
if(written < out_size){
687
if(out == mandos_protocol_version){
426
gnutls_transport_set_ptr (es.session,
427
(gnutls_transport_ptr_t) tcp_sd);
701
430
fprintf(stderr, "Establishing TLS session with %s\n", ip);
708
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
715
ret = gnutls_handshake(session);
719
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
721
if(ret != GNUTLS_E_SUCCESS){
433
ret = gnutls_handshake (es.session);
435
if (ret != GNUTLS_E_SUCCESS){
723
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
437
fprintf(stderr, "\n*** Handshake failed ***\n");
730
/* Read OpenPGP packet that contains the wanted password */
444
//Retrieve OpenPGP packet that contains the wanted password
733
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
447
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
743
buffer_capacity = incbuffer(&buffer, buffer_length,
745
if(buffer_capacity == 0){
755
sret = gnutls_record_recv(session, buffer+buffer_length,
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);
762
468
case GNUTLS_E_INTERRUPTED:
763
469
case GNUTLS_E_AGAIN:
765
471
case GNUTLS_E_REHANDSHAKE:
767
ret = gnutls_handshake(session);
772
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
774
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
472
ret = gnutls_handshake (es.session);
474
fprintf(stderr, "\n*** Handshake failed ***\n");
781
481
fprintf(stderr, "Unknown error while reading data from"
782
" encrypted session with Mandos server\n");
482
" encrypted session with mandos server\n");
784
gnutls_bye(session, GNUTLS_SHUT_RDWR);
484
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
788
buffer_length += (size_t) sret;
488
buffer_length += (size_t) ret;
793
fprintf(stderr, "Closing TLS session\n");
800
gnutls_bye(session, GNUTLS_SHUT_RDWR);
806
if(buffer_length > 0){
807
ssize_t decrypted_buffer_size;
492
if (buffer_length > 0){
808
493
decrypted_buffer_size = pgp_packet_decrypt(buffer,
811
if(decrypted_buffer_size >= 0){
497
if (decrypted_buffer_size >= 0){
814
498
while(written < (size_t) decrypted_buffer_size){
819
ret = (int)fwrite(decrypted_buffer + written, 1,
820
(size_t)decrypted_buffer_size - written,
499
ret = (int)fwrite (decrypted_buffer + written, 1,
500
(size_t)decrypted_buffer_size - written,
822
502
if(ret == 0 and ferror(stdout)){
824
504
fprintf(stderr, "Error writing encrypted data: %s\n",
890
567
char ip[AVAHI_ADDRESS_STR_MAX];
891
568
avahi_address_snprint(ip, sizeof(ip), address);
893
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
894
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
895
ip, (intmax_t)interface, port);
570
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
571
" port %d\n", name, host_name, ip, port);
897
int ret = start_mandos_communication(ip, port, interface,
898
avahi_proto_to_af(proto));
900
avahi_simple_poll_quit(mc.simple_poll);
573
int ret = start_mandos_communication(ip, port, interface);
904
579
avahi_s_service_resolver_free(r);
907
static void browse_callback(AvahiSServiceBrowser *b,
908
AvahiIfIndex interface,
909
AvahiProtocol protocol,
910
AvahiBrowserEvent event,
914
AVAHI_GCC_UNUSED AvahiLookupResultFlags
916
AVAHI_GCC_UNUSED void* userdata){
919
/* Called whenever a new services becomes available on the LAN or
920
is removed from the LAN */
928
case AVAHI_BROWSER_FAILURE:
930
fprintf(stderr, "(Avahi browser) %s\n",
931
avahi_strerror(avahi_server_errno(mc.server)));
932
avahi_simple_poll_quit(mc.simple_poll);
935
case AVAHI_BROWSER_NEW:
936
/* We ignore the returned Avahi resolver object. In the callback
937
function we free it. If the Avahi server is terminated before
938
the callback function is called the Avahi server will free the
941
if(avahi_s_service_resolver_new(mc.server, interface, protocol,
942
name, type, domain, protocol, 0,
943
resolve_callback, NULL) == NULL)
944
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
945
name, avahi_strerror(avahi_server_errno(mc.server)));
948
case AVAHI_BROWSER_REMOVE:
951
case AVAHI_BROWSER_ALL_FOR_NOW:
952
case AVAHI_BROWSER_CACHE_EXHAUSTED:
954
fprintf(stderr, "No Mandos server found, still searching...\n");
960
/* stop main loop after sigterm has been called */
961
static void handle_sigterm(int sig){
966
signal_received = sig;
967
int old_errno = errno;
968
if(mc.simple_poll != NULL){
969
avahi_simple_poll_quit(mc.simple_poll);
974
int main(int argc, char *argv[]){
975
AvahiSServiceBrowser *sb = NULL;
980
int exitcode = EXIT_SUCCESS;
981
const char *interface = "eth0";
982
struct ifreq network;
984
bool take_down_interface = false;
987
char *connect_to = NULL;
988
char tempdir[] = "/tmp/mandosXXXXXX";
989
bool tempdir_created = false;
990
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
991
const char *seckey = PATHDIR "/" SECKEY;
992
const char *pubkey = PATHDIR "/" PUBKEY;
994
bool gnutls_initialized = false;
995
bool gpgme_initialized = false;
998
struct sigaction old_sigterm_action;
999
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
1002
struct argp_option options[] = {
1003
{ .name = "debug", .key = 128,
1004
.doc = "Debug mode", .group = 3 },
1005
{ .name = "connect", .key = 'c',
1006
.arg = "ADDRESS:PORT",
1007
.doc = "Connect directly to a specific Mandos server",
1009
{ .name = "interface", .key = 'i',
1011
.doc = "Network interface that will be used to search for"
1014
{ .name = "seckey", .key = 's',
1016
.doc = "OpenPGP secret key file base name",
1018
{ .name = "pubkey", .key = 'p',
1020
.doc = "OpenPGP public key file base name",
1022
{ .name = "dh-bits", .key = 129,
1024
.doc = "Bit length of the prime number used in the"
1025
" Diffie-Hellman key exchange",
1027
{ .name = "priority", .key = 130,
1029
.doc = "GnuTLS priority string for the TLS handshake",
1031
{ .name = "delay", .key = 131,
1033
.doc = "Maximum delay to wait for interface startup",
1038
error_t parse_opt(int key, char *arg,
1039
struct argp_state *state){
1041
case 128: /* --debug */
1044
case 'c': /* --connect */
1047
case 'i': /* --interface */
1050
case 's': /* --seckey */
1053
case 'p': /* --pubkey */
1056
case 129: /* --dh-bits */
1058
tmpmax = strtoimax(arg, &tmp, 10);
1059
if(errno != 0 or tmp == arg or *tmp != '\0'
1060
or tmpmax != (typeof(mc.dh_bits))tmpmax){
1061
fprintf(stderr, "Bad number of DH bits\n");
1064
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
1066
case 130: /* --priority */
1069
case 131: /* --delay */
1071
delay = strtof(arg, &tmp);
1072
if(errno != 0 or tmp == arg or *tmp != '\0'){
1073
fprintf(stderr, "Bad delay\n");
1082
return ARGP_ERR_UNKNOWN;
1087
struct argp argp = { .options = options, .parser = parse_opt,
1089
.doc = "Mandos client -- Get and decrypt"
1090
" passwords from a Mandos server" };
1091
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
1092
if(ret == ARGP_ERR_UNKNOWN){
1093
fprintf(stderr, "Unknown error while parsing arguments\n");
1094
exitcode = EXIT_FAILURE;
1100
avahi_set_log_function(empty_log);
1103
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1104
from the signal handler */
1105
/* Initialize the pseudo-RNG for Avahi */
1106
srand((unsigned int) time(NULL));
1107
mc.simple_poll = avahi_simple_poll_new();
1108
if(mc.simple_poll == NULL){
1109
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1110
exitcode = EXIT_FAILURE;
1114
sigemptyset(&sigterm_action.sa_mask);
1115
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1117
perror("sigaddset");
1118
exitcode = EXIT_FAILURE;
1121
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1123
perror("sigaddset");
1124
exitcode = EXIT_FAILURE;
1127
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1129
perror("sigaddset");
1130
exitcode = EXIT_FAILURE;
1133
/* Need to check if the handler is SIG_IGN before handling:
1134
| [[info:libc:Initial Signal Actions]] |
1135
| [[info:libc:Basic Signal Handling]] |
1137
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
1139
perror("sigaction");
1140
return EXIT_FAILURE;
1142
if(old_sigterm_action.sa_handler != SIG_IGN){
1143
ret = sigaction(SIGINT, &sigterm_action, NULL);
1145
perror("sigaction");
1146
exitcode = EXIT_FAILURE;
1150
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
1152
perror("sigaction");
1153
return EXIT_FAILURE;
1155
if(old_sigterm_action.sa_handler != SIG_IGN){
1156
ret = sigaction(SIGHUP, &sigterm_action, NULL);
1158
perror("sigaction");
1159
exitcode = EXIT_FAILURE;
1163
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
1165
perror("sigaction");
1166
return EXIT_FAILURE;
1168
if(old_sigterm_action.sa_handler != SIG_IGN){
1169
ret = sigaction(SIGTERM, &sigterm_action, NULL);
1171
perror("sigaction");
1172
exitcode = EXIT_FAILURE;
1177
/* If the interface is down, bring it up */
1178
if(interface[0] != '\0'){
1179
if_index = (AvahiIfIndex) if_nametoindex(interface);
1181
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1182
exitcode = EXIT_FAILURE;
1191
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1192
messages to mess up the prompt */
1193
ret = klogctl(8, NULL, 5);
1194
bool restore_loglevel = true;
1196
restore_loglevel = false;
1199
#endif /* __linux__ */
1201
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1204
exitcode = EXIT_FAILURE;
1206
if(restore_loglevel){
1207
ret = klogctl(7, NULL, 0);
1212
#endif /* __linux__ */
1215
strcpy(network.ifr_name, interface);
1216
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1218
perror("ioctl SIOCGIFFLAGS");
1220
if(restore_loglevel){
1221
ret = klogctl(7, NULL, 0);
1226
#endif /* __linux__ */
1227
exitcode = EXIT_FAILURE;
1230
if((network.ifr_flags & IFF_UP) == 0){
1231
network.ifr_flags |= IFF_UP;
1232
take_down_interface = true;
1233
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1235
take_down_interface = false;
1236
perror("ioctl SIOCSIFFLAGS");
1237
exitcode = EXIT_FAILURE;
1239
if(restore_loglevel){
1240
ret = klogctl(7, NULL, 0);
1245
#endif /* __linux__ */
1249
/* sleep checking until interface is running */
1250
for(int i=0; i < delay * 4; i++){
1251
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1253
perror("ioctl SIOCGIFFLAGS");
1254
} else if(network.ifr_flags & IFF_RUNNING){
1257
struct timespec sleeptime = { .tv_nsec = 250000000 };
1258
ret = nanosleep(&sleeptime, NULL);
1259
if(ret == -1 and errno != EINTR){
1260
perror("nanosleep");
1263
if(not take_down_interface){
1264
/* We won't need the socket anymore */
1265
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1271
if(restore_loglevel){
1272
/* Restores kernel loglevel to default */
1273
ret = klogctl(7, NULL, 0);
1278
#endif /* __linux__ */
1288
/* Drop any group privileges we might have, just to be safe */
1295
/* Drop user privileges */
1297
/* Will we need privileges later? */
1298
if(take_down_interface){
1299
/* Drop user privileges temporarily */
1305
/* Drop user privileges permanently */
1316
ret = init_gnutls_global(pubkey, seckey);
1318
fprintf(stderr, "init_gnutls_global failed\n");
1319
exitcode = EXIT_FAILURE;
1322
gnutls_initialized = true;
1329
tempdir_created = true;
1330
if(mkdtemp(tempdir) == NULL){
1331
tempdir_created = false;
1340
if(not init_gpgme(pubkey, seckey, tempdir)){
1341
fprintf(stderr, "init_gpgme failed\n");
1342
exitcode = EXIT_FAILURE;
1345
gpgme_initialized = true;
1352
if(connect_to != NULL){
1353
/* Connect directly, do not use Zeroconf */
1354
/* (Mainly meant for debugging) */
1355
char *address = strrchr(connect_to, ':');
1356
if(address == NULL){
1357
fprintf(stderr, "No colon in address\n");
1358
exitcode = EXIT_FAILURE;
1368
tmpmax = strtoimax(address+1, &tmp, 10);
1369
if(errno != 0 or tmp == address+1 or *tmp != '\0'
1370
or tmpmax != (uint16_t)tmpmax){
1371
fprintf(stderr, "Bad port number\n");
1372
exitcode = EXIT_FAILURE;
1380
port = (uint16_t)tmpmax;
1382
address = connect_to;
1383
/* Colon in address indicates IPv6 */
1385
if(strchr(address, ':') != NULL){
1395
ret = start_mandos_communication(address, port, if_index, af);
1397
exitcode = EXIT_FAILURE;
1399
exitcode = EXIT_SUCCESS;
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[]) {
1409
653
AvahiServerConfig config;
1410
/* Do not publish any local Zeroconf records */
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 */
1411
762
avahi_server_config_init(&config);
1412
763
config.publish_hinfo = 0;
1413
764
config.publish_addresses = 0;
1414
765
config.publish_workstation = 0;
1415
766
config.publish_domain = 0;
1417
768
/* Allocate a new server */
1418
mc.server = avahi_server_new(avahi_simple_poll_get
1419
(mc.simple_poll), &config, NULL,
1422
/* Free the Avahi configuration data */
769
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
770
&config, NULL, NULL, &error);
772
/* Free the configuration data */
1423
773
avahi_server_config_free(&config);
1426
/* Check if creating the Avahi server object succeeded */
1427
if(mc.server == NULL){
1428
fprintf(stderr, "Failed to create Avahi server: %s\n",
1429
avahi_strerror(error));
1430
exitcode = EXIT_FAILURE;
1438
/* Create the Avahi service browser */
1439
sb = avahi_s_service_browser_new(mc.server, if_index,
1440
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
1441
NULL, 0, browse_callback, NULL);
1443
fprintf(stderr, "Failed to create service browser: %s\n",
1444
avahi_strerror(avahi_server_errno(mc.server)));
1445
exitcode = EXIT_FAILURE;
1453
/* Run the main loop */
1456
fprintf(stderr, "Starting Avahi loop search\n");
1459
avahi_simple_poll_loop(mc.simple_poll);
1464
fprintf(stderr, "%s exiting\n", argv[0]);
1467
/* Cleanup things */
1469
avahi_s_service_browser_free(sb);
1471
if(mc.server != NULL)
1472
avahi_server_free(mc.server);
1474
if(mc.simple_poll != NULL)
1475
avahi_simple_poll_free(mc.simple_poll);
1477
if(gnutls_initialized){
1478
gnutls_certificate_free_credentials(mc.cred);
1479
gnutls_global_deinit();
1480
gnutls_dh_params_deinit(mc.dh_params);
1483
if(gpgme_initialized){
1484
gpgme_release(mc.ctx);
1487
/* Take down the network interface */
1488
if(take_down_interface){
1489
/* Re-raise priviliges */
1496
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1498
perror("ioctl SIOCGIFFLAGS");
1499
} else if(network.ifr_flags & IFF_UP) {
1500
network.ifr_flags &= ~IFF_UP; /* clear flag */
1501
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1503
perror("ioctl SIOCSIFFLAGS");
1506
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1510
/* Lower privileges, permanently this time */
1519
/* Removes the temp directory used by GPGME */
1520
if(tempdir_created){
1522
struct dirent *direntry;
1523
d = opendir(tempdir);
1525
if(errno != ENOENT){
1530
direntry = readdir(d);
1531
if(direntry == NULL){
1534
/* Skip "." and ".." */
1535
if(direntry->d_name[0] == '.'
1536
and (direntry->d_name[1] == '\0'
1537
or (direntry->d_name[1] == '.'
1538
and direntry->d_name[2] == '\0'))){
1541
char *fullname = NULL;
1542
ret = asprintf(&fullname, "%s/%s", tempdir,
1548
ret = remove(fullname);
1550
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1557
ret = rmdir(tempdir);
1558
if(ret == -1 and errno != ENOENT){
1564
sigemptyset(&old_sigterm_action.sa_mask);
1565
old_sigterm_action.sa_handler = SIG_DFL;
1566
ret = sigaction(signal_received, &old_sigterm_action, NULL);
1568
perror("sigaction");
1570
raise(signal_received);
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);