100
46
#include <avahi-common/malloc.h>
101
47
#include <avahi-common/error.h>
104
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
107
init_gnutls_session(),
109
#include <gnutls/openpgp.h>
110
/* gnutls_certificate_set_openpgp_key_file(),
111
GNUTLS_OPENPGP_FMT_BASE64 */
114
#include <gpgme.h> /* All GPGME types, constants and
117
GPGME_PROTOCOL_OpenPGP,
50
#include <sys/types.h> /* socket(), inet_pton() */
51
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
52
struct in6_addr, inet_pton() */
53
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
54
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
56
#include <unistd.h> /* close() */
57
#include <netinet/in.h>
58
#include <stdbool.h> /* true */
59
#include <string.h> /* memset */
60
#include <arpa/inet.h> /* inet_pton() */
61
#include <iso646.h> /* not */
64
#include <errno.h> /* perror() */
71
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
73
#define CERTFILE CERT_ROOT "openpgp-client.txt"
74
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
120
75
#define BUFFER_SIZE 256
122
#define PATHDIR "/conf/conf.d/mandos"
123
#define SECKEY "seckey.txt"
124
#define PUBKEY "pubkey.txt"
126
78
bool debug = false;
127
static const char mandos_protocol_version[] = "1";
128
const char *argp_program_version = "mandos-client " VERSION;
129
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
130
static const char sys_class_net[] = "/sys/class/net";
131
char *connect_to = NULL;
133
/* Used for passing in values through the Avahi callback functions */
135
AvahiSimplePoll *simple_poll;
81
gnutls_session_t session;
137
82
gnutls_certificate_credentials_t cred;
138
unsigned int dh_bits;
139
83
gnutls_dh_params_t dh_params;
140
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;
144
/* global context so signal handler can reach it*/
145
mandos_context mc = { .simple_poll = NULL, .server = NULL,
146
.dh_bits = 1024, .priority = "SECURE256"
147
":!CTYPE-X.509:+CTYPE-OPENPGP" };
149
sig_atomic_t quit_now = 0;
150
int signal_received = 0;
153
* Make additional room in "buffer" for at least BUFFER_SIZE more
154
* bytes. "buffer_capacity" is how much is currently allocated,
155
* "buffer_length" is how much is already used.
157
size_t incbuffer(char **buffer, size_t buffer_length,
158
size_t buffer_capacity){
159
if(buffer_length + BUFFER_SIZE > buffer_capacity){
160
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
164
buffer_capacity += BUFFER_SIZE;
166
return buffer_capacity;
172
static bool init_gpgme(const char *seckey,
173
const char *pubkey, const char *tempdir){
93
ssize_t new_packet_capacity = 0;
94
ssize_t new_packet_length = 0;
175
95
gpgme_engine_info_t engine_info;
179
* Helper function to insert pub and seckey to the engine keyring.
181
bool import_key(const char *filename){
184
gpgme_data_t pgp_data;
186
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
192
rc = gpgme_data_new_from_fd(&pgp_data, fd);
193
if(rc != GPG_ERR_NO_ERROR){
194
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
195
gpgme_strsource(rc), gpgme_strerror(rc));
199
rc = gpgme_op_import(mc.ctx, pgp_data);
200
if(rc != GPG_ERR_NO_ERROR){
201
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
202
gpgme_strsource(rc), gpgme_strerror(rc));
206
ret = (int)TEMP_FAILURE_RETRY(close(fd));
210
gpgme_data_release(pgp_data);
215
fprintf(stderr, "Initializing GPGME\n");
98
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
219
102
gpgme_check_version(NULL);
220
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
221
if(rc != GPG_ERR_NO_ERROR){
222
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
223
gpgme_strsource(rc), gpgme_strerror(rc));
103
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
227
/* Set GPGME home directory for the OpenPGP engine only */
228
rc = gpgme_get_engine_info(&engine_info);
229
if(rc != GPG_ERR_NO_ERROR){
105
/* Set GPGME home directory */
106
rc = gpgme_get_engine_info (&engine_info);
107
if (rc != GPG_ERR_NO_ERROR){
230
108
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
231
109
gpgme_strsource(rc), gpgme_strerror(rc));
234
112
while(engine_info != NULL){
235
113
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
236
114
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
237
engine_info->file_name, tempdir);
115
engine_info->file_name, homedir);
240
118
engine_info = engine_info->next;
242
120
if(engine_info == NULL){
243
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
247
/* Create new GPGME "context" */
248
rc = gpgme_new(&(mc.ctx));
249
if(rc != GPG_ERR_NO_ERROR){
250
fprintf(stderr, "bad gpgme_new: %s: %s\n",
251
gpgme_strsource(rc), gpgme_strerror(rc));
255
if(not import_key(pubkey) or not import_key(seckey)){
263
* Decrypt OpenPGP data.
264
* Returns -1 on error
266
static ssize_t pgp_packet_decrypt(const char *cryptotext,
269
gpgme_data_t dh_crypto, dh_plain;
272
size_t plaintext_capacity = 0;
273
ssize_t plaintext_length = 0;
276
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
279
/* Create new GPGME data buffer from memory cryptotext */
280
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
282
if(rc != GPG_ERR_NO_ERROR){
121
fprintf(stderr, "Could not set home dir to %s\n", homedir);
125
/* Create new GPGME data buffer from packet buffer */
126
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
127
if (rc != GPG_ERR_NO_ERROR){
283
128
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
284
129
gpgme_strsource(rc), gpgme_strerror(rc));
334
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
191
/* Delete the GPGME FILE pointer cryptotext data buffer */
192
gpgme_data_release(dh_crypto);
337
194
/* Seek back to the beginning of the GPGME plaintext data buffer */
338
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
339
perror("gpgme_data_seek");
340
plaintext_length = -1;
195
gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET);
346
plaintext_capacity = incbuffer(plaintext,
347
(size_t)plaintext_length,
349
if(plaintext_capacity == 0){
351
plaintext_length = -1;
199
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
200
*new_packet = realloc(*new_packet,
201
(unsigned int)new_packet_capacity
203
if (*new_packet == NULL){
207
new_packet_capacity += BUFFER_SIZE;
355
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
210
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
357
212
/* Print the data, if any */
363
217
perror("gpgme_data_read");
364
plaintext_length = -1;
367
plaintext_length += ret;
371
fprintf(stderr, "Decrypted password is: ");
372
for(ssize_t i = 0; i < plaintext_length; i++){
373
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
375
fprintf(stderr, "\n");
380
/* Delete the GPGME cryptotext data buffer */
381
gpgme_data_release(dh_crypto);
220
new_packet_length += ret;
223
/* FIXME: check characters before printing to screen so to not print
224
terminal control characters */
226
/* fprintf(stderr, "decrypted password is: "); */
227
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
228
/* fprintf(stderr, "\n"); */
383
231
/* Delete the GPGME plaintext data buffer */
384
232
gpgme_data_release(dh_plain);
385
return plaintext_length;
233
return new_packet_length;
388
static const char * safer_gnutls_strerror(int value){
389
const char *ret = gnutls_strerror(value); /* Spurious warning from
390
-Wunreachable-code */
236
static const char * safer_gnutls_strerror (int value) {
237
const char *ret = gnutls_strerror (value);
392
239
ret = "(unknown)";
396
/* GnuTLS log function callback */
397
static void debuggnutls(__attribute__((unused)) int level,
399
fprintf(stderr, "GnuTLS: %s", string);
243
void debuggnutls(__attribute__((unused)) int level,
245
fprintf(stderr, "%s", string);
402
static int init_gnutls_global(const char *pubkeyfilename,
403
const char *seckeyfilename){
248
int initgnutls(encrypted_session *es){
407
253
fprintf(stderr, "Initializing GnuTLS\n");
410
ret = gnutls_global_init();
411
if(ret != GNUTLS_E_SUCCESS){
412
fprintf(stderr, "GnuTLS global_init: %s\n",
413
safer_gnutls_strerror(ret));
256
if ((ret = gnutls_global_init ())
257
!= GNUTLS_E_SUCCESS) {
258
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
418
/* "Use a log level over 10 to enable all debugging options."
421
263
gnutls_global_set_log_level(11);
422
264
gnutls_global_set_log_function(debuggnutls);
425
/* OpenPGP credentials */
426
ret = gnutls_certificate_allocate_credentials(&mc.cred);
427
if(ret != GNUTLS_E_SUCCESS){
428
fprintf(stderr, "GnuTLS memory error: %s\n",
429
safer_gnutls_strerror(ret));
430
gnutls_global_deinit();
267
/* openpgp credentials */
268
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
269
!= GNUTLS_E_SUCCESS) {
270
fprintf (stderr, "memory error: %s\n",
271
safer_gnutls_strerror(ret));
435
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
436
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
276
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
277
" and keyfile %s as GnuTLS credentials\n", CERTFILE,
440
281
ret = gnutls_certificate_set_openpgp_key_file
441
(mc.cred, pubkeyfilename, seckeyfilename,
442
GNUTLS_OPENPGP_FMT_BASE64);
443
if(ret != GNUTLS_E_SUCCESS){
445
"Error[%d] while reading the OpenPGP key pair ('%s',"
446
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
447
fprintf(stderr, "The GnuTLS error is: %s\n",
448
safer_gnutls_strerror(ret));
452
/* GnuTLS server initialization */
453
ret = gnutls_dh_params_init(&mc.dh_params);
454
if(ret != GNUTLS_E_SUCCESS){
455
fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
456
" %s\n", safer_gnutls_strerror(ret));
459
ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
460
if(ret != GNUTLS_E_SUCCESS){
461
fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
462
safer_gnutls_strerror(ret));
466
gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
472
gnutls_certificate_free_credentials(mc.cred);
473
gnutls_global_deinit();
474
gnutls_dh_params_deinit(mc.dh_params);
478
static int init_gnutls_session(gnutls_session_t *session){
480
/* GnuTLS session creation */
482
ret = gnutls_init(session, GNUTLS_SERVER);
486
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
487
if(ret != GNUTLS_E_SUCCESS){
282
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
283
if (ret != GNUTLS_E_SUCCESS) {
285
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
287
ret, CERTFILE, KEYFILE);
288
fprintf(stdout, "The Error is: %s\n",
289
safer_gnutls_strerror(ret));
293
//GnuTLS server initialization
294
if ((ret = gnutls_dh_params_init (&es->dh_params))
295
!= GNUTLS_E_SUCCESS) {
296
fprintf (stderr, "Error in dh parameter initialization: %s\n",
297
safer_gnutls_strerror(ret));
301
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
302
!= GNUTLS_E_SUCCESS) {
303
fprintf (stderr, "Error in prime generation: %s\n",
304
safer_gnutls_strerror(ret));
308
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
310
// GnuTLS session creation
311
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
312
!= GNUTLS_E_SUCCESS){
488
313
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
489
314
safer_gnutls_strerror(ret));
495
ret = gnutls_priority_set_direct(*session, mc.priority, &err);
497
gnutls_deinit(*session);
500
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
501
if(ret != GNUTLS_E_SUCCESS){
502
fprintf(stderr, "Syntax error at: %s\n", err);
503
fprintf(stderr, "GnuTLS error: %s\n",
504
safer_gnutls_strerror(ret));
505
gnutls_deinit(*session);
317
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
318
!= GNUTLS_E_SUCCESS) {
319
fprintf(stderr, "Syntax error at: %s\n", err);
320
fprintf(stderr, "GnuTLS error: %s\n",
321
safer_gnutls_strerror(ret));
511
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
514
gnutls_deinit(*session);
517
} while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
518
if(ret != GNUTLS_E_SUCCESS){
519
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
325
if ((ret = gnutls_credentials_set
326
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
327
!= GNUTLS_E_SUCCESS) {
328
fprintf(stderr, "Error setting a credentials set: %s\n",
520
329
safer_gnutls_strerror(ret));
521
gnutls_deinit(*session);
525
333
/* ignore client certificate if any. */
526
gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
334
gnutls_certificate_server_set_request (es->session,
528
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
337
gnutls_dh_set_prime_bits (es->session, DH_BITS);
533
/* Avahi log function callback */
534
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
535
__attribute__((unused)) const char *txt){}
342
void empty_log(__attribute__((unused)) AvahiLogLevel level,
343
__attribute__((unused)) const char *txt){}
537
/* Called when a Mandos server is found */
538
static int start_mandos_communication(const char *ip, uint16_t port,
539
AvahiIfIndex if_index,
541
int ret, tcp_sd = -1;
544
struct sockaddr_in in;
545
struct sockaddr_in6 in6;
345
int start_mandos_communication(const char *ip, uint16_t port,
346
unsigned int if_index){
348
struct sockaddr_in6 to;
349
encrypted_session es;
547
350
char *buffer = NULL;
548
char *decrypted_buffer = NULL;
351
char *decrypted_buffer;
549
352
size_t buffer_length = 0;
550
353
size_t buffer_capacity = 0;
553
gnutls_session_t session;
554
int pf; /* Protocol family */
571
fprintf(stderr, "Bad address family: %d\n", af);
576
ret = init_gnutls_session(&session);
354
ssize_t decrypted_buffer_size;
357
char interface[IF_NAMESIZE];
582
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
360
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
586
tcp_sd = socket(pf, SOCK_STREAM, 0);
364
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
589
366
perror("socket");
599
memset(&to, 0, sizeof(to));
601
to.in6.sin6_family = (sa_family_t)af;
602
ret = inet_pton(af, ip, &to.in6.sin6_addr);
604
to.in.sin_family = (sa_family_t)af;
605
ret = inet_pton(af, ip, &to.in.sin_addr);
370
if(if_indextoname(if_index, interface) == NULL){
372
perror("if_indextoname");
378
fprintf(stderr, "Binding to interface %s\n", interface);
381
memset(&to,0,sizeof(to)); /* Spurious warning */
382
to.sin6_family = AF_INET6;
383
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
609
385
perror("inet_pton");
615
389
fprintf(stderr, "Bad address: %s\n", ip);
620
to.in6.sin6_port = htons(port); /* Spurious warnings from
622
-Wunreachable-code */
624
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
625
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
627
if(if_index == AVAHI_IF_UNSPEC){
628
fprintf(stderr, "An IPv6 link-local address is incomplete"
629
" without a network interface\n");
633
/* Set the network interface number as scope */
634
to.in6.sin6_scope_id = (uint32_t)if_index;
637
to.in.sin_port = htons(port); /* Spurious warnings from
639
-Wunreachable-code */
392
to.sin6_port = htons(port); /* Spurious warning */
394
to.sin6_scope_id = (uint32_t)if_index;
648
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
649
char interface[IF_NAMESIZE];
650
if(if_indextoname((unsigned int)if_index, interface) == NULL){
651
perror("if_indextoname");
653
fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
654
ip, interface, port);
657
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
660
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
661
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
664
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
667
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
673
if(strcmp(addrstr, ip) != 0){
674
fprintf(stderr, "Canonical address form: %s\n", addrstr);
685
ret = connect(tcp_sd, &to.in6, sizeof(to));
687
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
690
if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
703
const char *out = mandos_protocol_version;
706
size_t out_size = strlen(out);
707
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
708
out_size - written));
715
written += (size_t)ret;
716
if(written < out_size){
719
if(out == mandos_protocol_version){
397
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
398
/* char addrstr[INET6_ADDRSTRLEN]; */
399
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
400
/* sizeof(addrstr)) == NULL){ */
401
/* perror("inet_ntop"); */
403
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
404
/* addrstr, ntohs(to.sin6_port)); */
408
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
414
ret = initgnutls (&es);
420
gnutls_transport_set_ptr (es.session,
421
(gnutls_transport_ptr_t) tcp_sd);
734
424
fprintf(stderr, "Establishing TLS session with %s\n", ip);
742
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
750
ret = gnutls_handshake(session);
755
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
757
if(ret != GNUTLS_E_SUCCESS){
427
ret = gnutls_handshake (es.session);
429
if (ret != GNUTLS_E_SUCCESS){
759
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
431
fprintf(stderr, "\n*** Handshake failed ***\n");
766
/* Read OpenPGP packet that contains the wanted password */
438
//Retrieve OpenPGP packet that contains the wanted password
769
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
441
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
780
buffer_capacity = incbuffer(&buffer, buffer_length,
782
if(buffer_capacity == 0){
794
sret = gnutls_record_recv(session, buffer+buffer_length,
446
if (buffer_length + BUFFER_SIZE > buffer_capacity){
447
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
452
buffer_capacity += BUFFER_SIZE;
455
ret = gnutls_record_recv
456
(es.session, buffer+buffer_length, BUFFER_SIZE);
801
462
case GNUTLS_E_INTERRUPTED:
802
463
case GNUTLS_E_AGAIN:
804
465
case GNUTLS_E_REHANDSHAKE:
806
ret = gnutls_handshake(session);
812
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
814
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
466
ret = gnutls_handshake (es.session);
468
fprintf(stderr, "\n*** Handshake failed ***\n");
821
475
fprintf(stderr, "Unknown error while reading data from"
822
" encrypted session with Mandos server\n");
823
gnutls_bye(session, GNUTLS_SHUT_RDWR);
476
" encrypted session with mandos server\n");
478
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
828
buffer_length += (size_t) sret;
833
fprintf(stderr, "Closing TLS session\n");
842
ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
847
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
849
if(buffer_length > 0){
850
ssize_t decrypted_buffer_size;
482
buffer_length += (size_t) ret;
486
if (buffer_length > 0){
851
487
decrypted_buffer_size = pgp_packet_decrypt(buffer,
854
if(decrypted_buffer_size >= 0){
491
if (decrypted_buffer_size >= 0){
857
492
while(written < (size_t) decrypted_buffer_size){
863
ret = (int)fwrite(decrypted_buffer + written, 1,
864
(size_t)decrypted_buffer_size - written,
493
ret = (int)fwrite (decrypted_buffer + written, 1,
494
(size_t)decrypted_buffer_size - written,
866
496
if(ret == 0 and ferror(stdout)){
869
498
fprintf(stderr, "Error writing encrypted data: %s\n",
870
499
strerror(errno));
875
504
written += (size_t)ret;
881
/* Shutdown procedure */
886
free(decrypted_buffer);
889
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
897
gnutls_deinit(session);
506
free(decrypted_buffer);
515
fprintf(stderr, "Closing TLS session\n");
519
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
522
gnutls_deinit (es.session);
523
gnutls_certificate_free_credentials (es.cred);
524
gnutls_global_deinit ();
907
static void resolve_callback(AvahiSServiceResolver *r,
908
AvahiIfIndex interface,
910
AvahiResolverEvent event,
914
const char *host_name,
915
const AvahiAddress *address,
917
AVAHI_GCC_UNUSED AvahiStringList *txt,
918
AVAHI_GCC_UNUSED AvahiLookupResultFlags
920
AVAHI_GCC_UNUSED void* userdata){
528
static AvahiSimplePoll *simple_poll = NULL;
529
static AvahiServer *server = NULL;
531
static void resolve_callback(
532
AvahiSServiceResolver *r,
533
AvahiIfIndex interface,
534
AVAHI_GCC_UNUSED AvahiProtocol protocol,
535
AvahiResolverEvent event,
539
const char *host_name,
540
const AvahiAddress *address,
542
AVAHI_GCC_UNUSED AvahiStringList *txt,
543
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
544
AVAHI_GCC_UNUSED void* userdata) {
546
assert(r); /* Spurious warning */
923
548
/* Called whenever a service has been resolved successfully or
932
553
case AVAHI_RESOLVER_FAILURE:
933
fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
934
" of type '%s' in domain '%s': %s\n", name, type, domain,
935
avahi_strerror(avahi_server_errno(mc.server)));
554
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
555
" type '%s' in domain '%s': %s\n", name, type, domain,
556
avahi_strerror(avahi_server_errno(server)));
938
559
case AVAHI_RESOLVER_FOUND:
940
561
char ip[AVAHI_ADDRESS_STR_MAX];
941
562
avahi_address_snprint(ip, sizeof(ip), address);
943
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
944
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
945
ip, (intmax_t)interface, port);
564
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
565
" port %d\n", name, host_name, ip, port);
947
int ret = start_mandos_communication(ip, port, interface,
948
avahi_proto_to_af(proto));
950
avahi_simple_poll_quit(mc.simple_poll);
567
int ret = start_mandos_communication(ip, port,
568
(unsigned int) interface);
954
574
avahi_s_service_resolver_free(r);
957
static void browse_callback(AvahiSServiceBrowser *b,
958
AvahiIfIndex interface,
959
AvahiProtocol protocol,
960
AvahiBrowserEvent event,
964
AVAHI_GCC_UNUSED AvahiLookupResultFlags
966
AVAHI_GCC_UNUSED void* userdata){
969
/* Called whenever a new services becomes available on the LAN or
970
is removed from the LAN */
978
case AVAHI_BROWSER_FAILURE:
980
fprintf(stderr, "(Avahi browser) %s\n",
981
avahi_strerror(avahi_server_errno(mc.server)));
982
avahi_simple_poll_quit(mc.simple_poll);
985
case AVAHI_BROWSER_NEW:
986
/* We ignore the returned Avahi resolver object. In the callback
987
function we free it. If the Avahi server is terminated before
988
the callback function is called the Avahi server will free the
991
if(avahi_s_service_resolver_new(mc.server, interface, protocol,
992
name, type, domain, protocol, 0,
993
resolve_callback, NULL) == NULL)
994
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
995
name, avahi_strerror(avahi_server_errno(mc.server)));
998
case AVAHI_BROWSER_REMOVE:
1001
case AVAHI_BROWSER_ALL_FOR_NOW:
1002
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1004
fprintf(stderr, "No Mandos server found, still searching...\n");
1010
/* stop main loop after sigterm has been called */
1011
static void handle_sigterm(int sig){
1016
signal_received = sig;
1017
int old_errno = errno;
1018
if(mc.simple_poll != NULL){
1019
avahi_simple_poll_quit(mc.simple_poll);
1025
* This function determines if a directory entry in /sys/class/net
1026
* corresponds to an acceptable network device.
1027
* (This function is passed to scandir(3) as a filter function.)
1029
int good_interface(const struct dirent *if_entry){
1031
char *flagname = NULL;
1032
if(if_entry->d_name[0] == '.'){
1035
int ret = asprintf(&flagname, "%s/%s/flags", sys_class_net,
1041
int flags_fd = (int)TEMP_FAILURE_RETRY(open(flagname, O_RDONLY));
1048
typedef short ifreq_flags; /* ifreq.ifr_flags in netdevice(7) */
1049
/* read line from flags_fd */
1050
ssize_t to_read = 2+(sizeof(ifreq_flags)*2)+1; /* "0x1003\n" */
1051
char *flagstring = malloc((size_t)to_read+1); /* +1 for final \0 */
1052
flagstring[(size_t)to_read] = '\0';
1053
if(flagstring == NULL){
1059
ssret = (ssize_t)TEMP_FAILURE_RETRY(read(flags_fd, flagstring,
1076
tmpmax = strtoimax(flagstring, &tmp, 0);
1077
if(errno != 0 or tmp == flagstring or (*tmp != '\0'
1078
and not (isspace(*tmp)))
1079
or tmpmax != (ifreq_flags)tmpmax){
1081
fprintf(stderr, "Invalid flags \"%s\" for interface \"%s\"\n",
1082
flagstring, if_entry->d_name);
1088
ifreq_flags flags = (ifreq_flags)tmpmax;
1089
/* Reject the loopback device */
1090
if(flags & IFF_LOOPBACK){
1092
fprintf(stderr, "Rejecting loopback interface \"%s\"\n",
1097
/* Accept point-to-point devices only if connect_to is specified */
1098
if(connect_to != NULL and (flags & IFF_POINTOPOINT)){
1100
fprintf(stderr, "Accepting point-to-point interface \"%s\"\n",
1105
/* Otherwise, reject non-broadcast-capable devices */
1106
if(not (flags & IFF_BROADCAST)){
1108
fprintf(stderr, "Rejecting non-broadcast interface \"%s\"\n",
1113
/* Reject non-ARP interfaces (including dummy interfaces) */
1114
if(flags & IFF_NOARP){
1116
fprintf(stderr, "Rejecting non-ARP interface \"%s\"\n",
1121
/* Accept this device */
1123
fprintf(stderr, "Interface \"%s\" is acceptable\n",
1129
int main(int argc, char *argv[]){
1130
AvahiSServiceBrowser *sb = NULL;
1135
int exitcode = EXIT_SUCCESS;
1136
const char *interface = "";
1137
struct ifreq network;
1139
bool take_down_interface = false;
1142
char tempdir[] = "/tmp/mandosXXXXXX";
1143
bool tempdir_created = false;
1144
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1145
const char *seckey = PATHDIR "/" SECKEY;
1146
const char *pubkey = PATHDIR "/" PUBKEY;
1148
bool gnutls_initialized = false;
1149
bool gpgme_initialized = false;
1152
struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
1153
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
1158
/* Lower any group privileges we might have, just to be safe */
1165
/* Lower user privileges (temporarily) */
1177
struct argp_option options[] = {
1178
{ .name = "debug", .key = 128,
1179
.doc = "Debug mode", .group = 3 },
1180
{ .name = "connect", .key = 'c',
1181
.arg = "ADDRESS:PORT",
1182
.doc = "Connect directly to a specific Mandos server",
1184
{ .name = "interface", .key = 'i',
1186
.doc = "Network interface that will be used to search for"
1189
{ .name = "seckey", .key = 's',
1191
.doc = "OpenPGP secret key file base name",
1193
{ .name = "pubkey", .key = 'p',
1195
.doc = "OpenPGP public key file base name",
1197
{ .name = "dh-bits", .key = 129,
1199
.doc = "Bit length of the prime number used in the"
1200
" Diffie-Hellman key exchange",
1202
{ .name = "priority", .key = 130,
1204
.doc = "GnuTLS priority string for the TLS handshake",
1206
{ .name = "delay", .key = 131,
1208
.doc = "Maximum delay to wait for interface startup",
1211
* These reproduce what we would get without ARGP_NO_HELP
1213
{ .name = "help", .key = '?',
1214
.doc = "Give this help list", .group = -1 },
1215
{ .name = "usage", .key = -3,
1216
.doc = "Give a short usage message", .group = -1 },
1217
{ .name = "version", .key = 'V',
1218
.doc = "Print program version", .group = -1 },
1222
error_t parse_opt(int key, char *arg,
1223
struct argp_state *state){
1226
case 128: /* --debug */
1229
case 'c': /* --connect */
1232
case 'i': /* --interface */
1235
case 's': /* --seckey */
1238
case 'p': /* --pubkey */
1241
case 129: /* --dh-bits */
1243
tmpmax = strtoimax(arg, &tmp, 10);
1244
if(errno != 0 or tmp == arg or *tmp != '\0'
1245
or tmpmax != (typeof(mc.dh_bits))tmpmax){
1246
argp_error(state, "Bad number of DH bits");
1248
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
1250
case 130: /* --priority */
1253
case 131: /* --delay */
1255
delay = strtof(arg, &tmp);
1256
if(errno != 0 or tmp == arg or *tmp != '\0'){
1257
argp_error(state, "Bad delay");
1261
* These reproduce what we would get without ARGP_NO_HELP
1263
case '?': /* --help */
1264
argp_state_help(state, state->out_stream,
1265
(ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
1266
& ~(unsigned int)ARGP_HELP_EXIT_OK);
1267
case -3: /* --usage */
1268
argp_state_help(state, state->out_stream,
1269
ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
1270
case 'V': /* --version */
1271
fprintf(state->out_stream, "%s\n", argp_program_version);
1272
exit(argp_err_exit_status);
1275
return ARGP_ERR_UNKNOWN;
1280
struct argp argp = { .options = options, .parser = parse_opt,
1282
.doc = "Mandos client -- Get and decrypt"
1283
" passwords from a Mandos server" };
1284
ret = argp_parse(&argp, argc, argv,
1285
ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
577
static void browse_callback(
578
AvahiSServiceBrowser *b,
579
AvahiIfIndex interface,
580
AvahiProtocol protocol,
581
AvahiBrowserEvent event,
585
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
588
AvahiServer *s = userdata;
589
assert(b); /* Spurious warning */
591
/* Called whenever a new services becomes available on the LAN or
592
is removed from the LAN */
1292
perror("argp_parse");
1293
exitcode = EX_OSERR;
1296
exitcode = EX_USAGE;
596
case AVAHI_BROWSER_FAILURE:
598
fprintf(stderr, "(Browser) %s\n",
599
avahi_strerror(avahi_server_errno(server)));
600
avahi_simple_poll_quit(simple_poll);
603
case AVAHI_BROWSER_NEW:
604
/* We ignore the returned resolver object. In the callback
605
function we free it. If the server is terminated before
606
the callback function is called the server will free
607
the resolver for us. */
609
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
611
AVAHI_PROTO_INET6, 0,
612
resolve_callback, s)))
613
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
614
avahi_strerror(avahi_server_errno(s)));
617
case AVAHI_BROWSER_REMOVE:
620
case AVAHI_BROWSER_ALL_FOR_NOW:
621
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1302
avahi_set_log_function(empty_log);
1305
if(interface[0] == '\0'){
1306
struct dirent **direntries;
1307
ret = scandir(sys_class_net, &direntries, good_interface,
1310
/* Pick the first good interface */
1311
interface = strdup(direntries[0]->d_name);
1313
fprintf(stderr, "Using interface \"%s\"\n", interface);
1315
if(interface == NULL){
1318
exitcode = EXIT_FAILURE;
1324
fprintf(stderr, "Could not find a network interface\n");
1325
exitcode = EXIT_FAILURE;
1330
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1331
from the signal handler */
1332
/* Initialize the pseudo-RNG for Avahi */
1333
srand((unsigned int) time(NULL));
1334
mc.simple_poll = avahi_simple_poll_new();
1335
if(mc.simple_poll == NULL){
1336
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1337
exitcode = EX_UNAVAILABLE;
1341
sigemptyset(&sigterm_action.sa_mask);
1342
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1344
perror("sigaddset");
1345
exitcode = EX_OSERR;
1348
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1350
perror("sigaddset");
1351
exitcode = EX_OSERR;
1354
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1356
perror("sigaddset");
1357
exitcode = EX_OSERR;
1360
/* Need to check if the handler is SIG_IGN before handling:
1361
| [[info:libc:Initial Signal Actions]] |
1362
| [[info:libc:Basic Signal Handling]] |
1364
ret = sigaction(SIGINT, NULL, &old_sigterm_action);
1366
perror("sigaction");
1369
if(old_sigterm_action.sa_handler != SIG_IGN){
1370
ret = sigaction(SIGINT, &sigterm_action, NULL);
1372
perror("sigaction");
1373
exitcode = EX_OSERR;
1377
ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
1379
perror("sigaction");
1382
if(old_sigterm_action.sa_handler != SIG_IGN){
1383
ret = sigaction(SIGHUP, &sigterm_action, NULL);
1385
perror("sigaction");
1386
exitcode = EX_OSERR;
1390
ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
1392
perror("sigaction");
1395
if(old_sigterm_action.sa_handler != SIG_IGN){
1396
ret = sigaction(SIGTERM, &sigterm_action, NULL);
1398
perror("sigaction");
1399
exitcode = EX_OSERR;
1404
/* If the interface is down, bring it up */
1405
if(strcmp(interface, "none") != 0){
1406
if_index = (AvahiIfIndex) if_nametoindex(interface);
626
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
627
AvahiServerConfig config;
628
AvahiSServiceBrowser *sb = NULL;
631
int returncode = EXIT_SUCCESS;
632
const char *interface = "eth0";
633
unsigned int if_index;
634
char *connect_to = NULL;
637
static struct option long_options[] = {
638
{"debug", no_argument, (int *)&debug, 1},
639
{"connect", required_argument, 0, 'c'},
640
{"interface", required_argument, 0, 'i'},
643
int option_index = 0;
644
ret = getopt_long (argc, argv, "i:", long_options,
665
if_index = if_nametoindex(interface);
1407
666
if(if_index == 0){
1408
667
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1409
exitcode = EX_UNAVAILABLE;
1417
/* Re-raise priviliges */
1425
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1426
messages about the network interface to mess up the prompt */
1427
ret = klogctl(8, NULL, 5);
1428
bool restore_loglevel = true;
1430
restore_loglevel = false;
1433
#endif /* __linux__ */
1435
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1438
exitcode = EX_OSERR;
1440
if(restore_loglevel){
1441
ret = klogctl(7, NULL, 0);
1446
#endif /* __linux__ */
1447
/* Lower privileges */
1455
strcpy(network.ifr_name, interface);
1456
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1458
perror("ioctl SIOCGIFFLAGS");
1460
if(restore_loglevel){
1461
ret = klogctl(7, NULL, 0);
1466
#endif /* __linux__ */
1467
exitcode = EX_OSERR;
1468
/* Lower privileges */
1476
if((network.ifr_flags & IFF_UP) == 0){
1477
network.ifr_flags |= IFF_UP;
1478
take_down_interface = true;
1479
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1481
take_down_interface = false;
1482
perror("ioctl SIOCSIFFLAGS +IFF_UP");
1483
exitcode = EX_OSERR;
1485
if(restore_loglevel){
1486
ret = klogctl(7, NULL, 0);
1491
#endif /* __linux__ */
1492
/* Lower privileges */
1501
/* sleep checking until interface is running */
1502
for(int i=0; i < delay * 4; i++){
1503
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1505
perror("ioctl SIOCGIFFLAGS");
1506
} else if(network.ifr_flags & IFF_RUNNING){
1509
struct timespec sleeptime = { .tv_nsec = 250000000 };
1510
ret = nanosleep(&sleeptime, NULL);
1511
if(ret == -1 and errno != EINTR){
1512
perror("nanosleep");
1515
if(not take_down_interface){
1516
/* We won't need the socket anymore */
1517
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1523
if(restore_loglevel){
1524
/* Restores kernel loglevel to default */
1525
ret = klogctl(7, NULL, 0);
1530
#endif /* __linux__ */
1531
/* Lower privileges */
1533
if(take_down_interface){
1534
/* Lower privileges */
1540
/* Lower privileges permanently */
1552
ret = init_gnutls_global(pubkey, seckey);
1554
fprintf(stderr, "init_gnutls_global failed\n");
1555
exitcode = EX_UNAVAILABLE;
1558
gnutls_initialized = true;
1565
tempdir_created = true;
1566
if(mkdtemp(tempdir) == NULL){
1567
tempdir_created = false;
1576
if(not init_gpgme(pubkey, seckey, tempdir)){
1577
fprintf(stderr, "init_gpgme failed\n");
1578
exitcode = EX_UNAVAILABLE;
1581
gpgme_initialized = true;
1588
if(connect_to != NULL){
1589
/* Connect directly, do not use Zeroconf */
1590
/* (Mainly meant for debugging) */
1591
char *address = strrchr(connect_to, ':');
1592
if(address == NULL){
1593
fprintf(stderr, "No colon in address\n");
1594
exitcode = EX_USAGE;
1604
tmpmax = strtoimax(address+1, &tmp, 10);
1605
if(errno != 0 or tmp == address+1 or *tmp != '\0'
1606
or tmpmax != (uint16_t)tmpmax){
1607
fprintf(stderr, "Bad port number\n");
1608
exitcode = EX_USAGE;
1616
port = (uint16_t)tmpmax;
1618
address = connect_to;
1619
/* Colon in address indicates IPv6 */
1621
if(strchr(address, ':') != NULL){
1631
while(not quit_now){
1632
ret = start_mandos_communication(address, port, if_index, af);
1633
if(quit_now or ret == 0){
1640
exitcode = EXIT_SUCCESS;
1651
AvahiServerConfig config;
1652
/* Do not publish any local Zeroconf records */
671
if(connect_to != NULL){
672
/* Connect directly, do not use Zeroconf */
673
/* (Mainly meant for debugging) */
674
char *address = strrchr(connect_to, ':');
676
fprintf(stderr, "No colon in address\n");
680
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
682
perror("Bad port number");
686
address = connect_to;
687
ret = start_mandos_communication(address, port, if_index);
696
avahi_set_log_function(empty_log);
699
/* Initialize the psuedo-RNG */
700
srand((unsigned int) time(NULL));
702
/* Allocate main loop object */
703
if (!(simple_poll = avahi_simple_poll_new())) {
704
fprintf(stderr, "Failed to create simple poll object.\n");
709
/* Do not publish any local records */
1653
710
avahi_server_config_init(&config);
1654
711
config.publish_hinfo = 0;
1655
712
config.publish_addresses = 0;
1656
713
config.publish_workstation = 0;
1657
714
config.publish_domain = 0;
1659
716
/* Allocate a new server */
1660
mc.server = avahi_server_new(avahi_simple_poll_get
1661
(mc.simple_poll), &config, NULL,
1664
/* Free the Avahi configuration data */
717
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
718
&config, NULL, NULL, &error);
720
/* Free the configuration data */
1665
721
avahi_server_config_free(&config);
1668
/* Check if creating the Avahi server object succeeded */
1669
if(mc.server == NULL){
1670
fprintf(stderr, "Failed to create Avahi server: %s\n",
1671
avahi_strerror(error));
1672
exitcode = EX_UNAVAILABLE;
1680
/* Create the Avahi service browser */
1681
sb = avahi_s_service_browser_new(mc.server, if_index,
1682
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
1683
NULL, 0, browse_callback, NULL);
1685
fprintf(stderr, "Failed to create service browser: %s\n",
1686
avahi_strerror(avahi_server_errno(mc.server)));
1687
exitcode = EX_UNAVAILABLE;
1695
/* Run the main loop */
1698
fprintf(stderr, "Starting Avahi loop search\n");
1701
avahi_simple_poll_loop(mc.simple_poll);
1706
fprintf(stderr, "%s exiting\n", argv[0]);
1709
/* Cleanup things */
1711
avahi_s_service_browser_free(sb);
1713
if(mc.server != NULL)
1714
avahi_server_free(mc.server);
1716
if(mc.simple_poll != NULL)
1717
avahi_simple_poll_free(mc.simple_poll);
1719
if(gnutls_initialized){
1720
gnutls_certificate_free_credentials(mc.cred);
1721
gnutls_global_deinit();
1722
gnutls_dh_params_deinit(mc.dh_params);
1725
if(gpgme_initialized){
1726
gpgme_release(mc.ctx);
1729
/* Take down the network interface */
1730
if(take_down_interface){
1731
/* Re-raise priviliges */
1738
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1740
perror("ioctl SIOCGIFFLAGS");
1741
} else if(network.ifr_flags & IFF_UP) {
1742
network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
1743
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1745
perror("ioctl SIOCSIFFLAGS -IFF_UP");
1748
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1752
/* Lower privileges permanently */
1761
/* Removes the temp directory used by GPGME */
1762
if(tempdir_created){
1764
struct dirent *direntry;
1765
d = opendir(tempdir);
1767
if(errno != ENOENT){
1772
direntry = readdir(d);
1773
if(direntry == NULL){
1776
/* Skip "." and ".." */
1777
if(direntry->d_name[0] == '.'
1778
and (direntry->d_name[1] == '\0'
1779
or (direntry->d_name[1] == '.'
1780
and direntry->d_name[2] == '\0'))){
1783
char *fullname = NULL;
1784
ret = asprintf(&fullname, "%s/%s", tempdir,
1790
ret = remove(fullname);
1792
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1799
ret = rmdir(tempdir);
1800
if(ret == -1 and errno != ENOENT){
1806
sigemptyset(&old_sigterm_action.sa_mask);
1807
old_sigterm_action.sa_handler = SIG_DFL;
1808
ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
1809
&old_sigterm_action,
1812
perror("sigaction");
1815
ret = raise(signal_received);
1816
} while(ret != 0 and errno == EINTR);
1821
TEMP_FAILURE_RETRY(pause());
723
/* Check if creating the server object succeeded */
725
fprintf(stderr, "Failed to create server: %s\n",
726
avahi_strerror(error));
727
returncode = EXIT_FAILURE;
731
/* Create the service browser */
732
sb = avahi_s_service_browser_new(server, (AvahiIfIndex)if_index,
734
"_mandos._tcp", NULL, 0,
735
browse_callback, server);
737
fprintf(stderr, "Failed to create service browser: %s\n",
738
avahi_strerror(avahi_server_errno(server)));
739
returncode = EXIT_FAILURE;
743
/* Run the main loop */
746
fprintf(stderr, "Starting avahi loop search\n");
749
avahi_simple_poll_loop(simple_poll);
754
fprintf(stderr, "%s exiting\n", argv[0]);
759
avahi_s_service_browser_free(sb);
762
avahi_server_free(server);
765
avahi_simple_poll_free(simple_poll);