33
32
#define _LARGEFILE_SOURCE
34
33
#define _FILE_OFFSET_BITS 64
36
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
38
#include <stdio.h> /* fprintf(), stderr, fwrite(),
40
#include <stdint.h> /* uint16_t, uint32_t */
41
#include <stddef.h> /* NULL, size_t, ssize_t */
42
#include <stdlib.h> /* free(), EXIT_SUCCESS, EXIT_FAILURE,
44
#include <stdbool.h> /* bool, true */
45
#include <string.h> /* memset(), strcmp(), strlen(),
46
strerror(), asprintf(), strcpy() */
47
#include <sys/ioctl.h> /* ioctl */
48
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
49
sockaddr_in6, PF_INET6,
50
SOCK_STREAM, INET6_ADDRSTRLEN,
51
uid_t, gid_t, open(), opendir(), DIR */
52
#include <sys/stat.h> /* open() */
53
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
54
struct in6_addr, inet_pton(),
56
#include <fcntl.h> /* open() */
57
#include <dirent.h> /* opendir(), struct dirent, readdir() */
58
#include <inttypes.h> /* PRIu16 */
59
#include <assert.h> /* assert() */
60
#include <errno.h> /* perror(), errno */
61
#include <time.h> /* time() */
62
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
63
SIOCSIFFLAGS, if_indextoname(),
64
if_nametoindex(), IF_NAMESIZE */
65
#include <netinet/in.h>
66
#include <unistd.h> /* close(), SEEK_SET, off_t, write(),
67
getuid(), getgid(), setuid(),
69
#include <arpa/inet.h> /* inet_pton(), htons */
70
#include <iso646.h> /* not, and */
71
#include <argp.h> /* struct argp_option, error_t, struct
72
argp_state, struct argp,
73
argp_parse(), ARGP_KEY_ARG,
74
ARGP_KEY_END, ARGP_ERR_UNKNOWN */
77
/* All Avahi types, constants and functions
39
#include <net/if.h> /* if_nametoindex */
80
41
#include <avahi-core/core.h>
81
42
#include <avahi-core/lookup.h>
82
43
#include <avahi-core/log.h>
84
45
#include <avahi-common/malloc.h>
85
46
#include <avahi-common/error.h>
88
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
91
init_gnutls_session(),
93
#include <gnutls/openpgp.h> /* gnutls_certificate_set_openpgp_key_file(),
94
GNUTLS_OPENPGP_FMT_BASE64 */
97
#include <gpgme.h> /* All GPGME types, constants and
100
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() */
103
69
#define BUFFER_SIZE 256
105
#define PATHDIR "/conf/conf.d/mandos"
106
#define SECKEY "seckey.txt"
107
#define PUBKEY "pubkey.txt"
72
const char *certdir = "/conf/conf.d/cryptkeyreq/";
73
const char *certfile = "openpgp-client.txt";
74
const char *certkey = "openpgp-client-key.txt";
109
76
bool debug = false;
110
static const char mandos_protocol_version[] = "1";
111
const char *argp_program_version = "mandos-client " VERSION;
112
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
114
/* Used for passing in values through the Avahi callback functions */
116
AvahiSimplePoll *simple_poll;
79
gnutls_session_t session;
118
80
gnutls_certificate_credentials_t cred;
119
unsigned int dh_bits;
120
81
gnutls_dh_params_t dh_params;
121
const char *priority;
85
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
86
char **new_packet, const char *homedir){
87
gpgme_data_t dh_crypto, dh_plain;
126
* Make room in "buffer" for at least BUFFER_SIZE additional bytes.
127
* "buffer_capacity" is how much is currently allocated,
128
* "buffer_length" is how much is already used.
130
size_t adjustbuffer(char **buffer, size_t buffer_length,
131
size_t buffer_capacity){
132
if (buffer_length + BUFFER_SIZE > buffer_capacity){
133
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
137
buffer_capacity += BUFFER_SIZE;
139
return buffer_capacity;
145
static bool init_gpgme(mandos_context *mc, const char *seckey,
146
const char *pubkey, const char *tempdir){
91
ssize_t new_packet_capacity = 0;
92
ssize_t new_packet_length = 0;
149
93
gpgme_engine_info_t engine_info;
153
* Helper function to insert pub and seckey to the enigne keyring.
155
bool import_key(const char *filename){
157
gpgme_data_t pgp_data;
159
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
165
rc = gpgme_data_new_from_fd(&pgp_data, fd);
166
if (rc != GPG_ERR_NO_ERROR){
167
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
168
gpgme_strsource(rc), gpgme_strerror(rc));
172
rc = gpgme_op_import(mc->ctx, pgp_data);
173
if (rc != GPG_ERR_NO_ERROR){
174
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
175
gpgme_strsource(rc), gpgme_strerror(rc));
179
ret = (int)TEMP_FAILURE_RETRY(close(fd));
183
gpgme_data_release(pgp_data);
188
fprintf(stderr, "Initialize gpgme\n");
96
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
264
138
if (rc != GPG_ERR_NO_ERROR){
265
139
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
266
140
gpgme_strsource(rc), gpgme_strerror(rc));
267
gpgme_data_release(dh_crypto);
271
/* Decrypt data from the cryptotext data buffer to the plaintext
273
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
144
/* Create new GPGME "context" */
145
rc = gpgme_new(&ctx);
146
if (rc != GPG_ERR_NO_ERROR){
147
fprintf(stderr, "bad gpgme_new: %s: %s\n",
148
gpgme_strsource(rc), gpgme_strerror(rc));
152
/* Decrypt data from the FILE pointer to the plaintext data
154
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
274
155
if (rc != GPG_ERR_NO_ERROR){
275
156
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
276
157
gpgme_strsource(rc), gpgme_strerror(rc));
277
plaintext_length = -1;
279
gpgme_decrypt_result_t result;
280
result = gpgme_op_decrypt_result(mc->ctx);
282
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
284
fprintf(stderr, "Unsupported algorithm: %s\n",
285
result->unsupported_algorithm);
286
fprintf(stderr, "Wrong key usage: %u\n",
287
result->wrong_key_usage);
288
if(result->file_name != NULL){
289
fprintf(stderr, "File name: %s\n", result->file_name);
291
gpgme_recipient_t recipient;
292
recipient = result->recipients;
294
while(recipient != NULL){
295
fprintf(stderr, "Public key algorithm: %s\n",
296
gpgme_pubkey_algo_name(recipient->pubkey_algo));
297
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
298
fprintf(stderr, "Secret key available: %s\n",
299
recipient->status == GPG_ERR_NO_SECKEY
301
recipient = recipient->next;
162
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
166
gpgme_decrypt_result_t result;
167
result = gpgme_op_decrypt_result(ctx);
169
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
171
fprintf(stderr, "Unsupported algorithm: %s\n",
172
result->unsupported_algorithm);
173
fprintf(stderr, "Wrong key usage: %d\n",
174
result->wrong_key_usage);
175
if(result->file_name != NULL){
176
fprintf(stderr, "File name: %s\n", result->file_name);
178
gpgme_recipient_t recipient;
179
recipient = result->recipients;
181
while(recipient != NULL){
182
fprintf(stderr, "Public key algorithm: %s\n",
183
gpgme_pubkey_algo_name(recipient->pubkey_algo));
184
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
185
fprintf(stderr, "Secret key available: %s\n",
186
recipient->status == GPG_ERR_NO_SECKEY
188
recipient = recipient->next;
310
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
194
/* Delete the GPGME FILE pointer cryptotext data buffer */
195
gpgme_data_release(dh_crypto);
313
197
/* Seek back to the beginning of the GPGME plaintext data buffer */
314
198
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
315
perror("gpgme_data_seek");
316
plaintext_length = -1;
199
perror("pgpme_data_seek");
322
plaintext_capacity = adjustbuffer(plaintext,
323
(size_t)plaintext_length,
325
if (plaintext_capacity == 0){
326
perror("adjustbuffer");
327
plaintext_length = -1;
204
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
205
*new_packet = realloc(*new_packet,
206
(unsigned int)new_packet_capacity
208
if (*new_packet == NULL){
212
new_packet_capacity += BUFFER_SIZE;
331
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
215
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
333
217
/* Print the data, if any */
339
222
perror("gpgme_data_read");
340
plaintext_length = -1;
343
plaintext_length += ret;
347
fprintf(stderr, "Decrypted password is: ");
348
for(ssize_t i = 0; i < plaintext_length; i++){
349
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
351
fprintf(stderr, "\n");
356
/* Delete the GPGME cryptotext data buffer */
357
gpgme_data_release(dh_crypto);
225
new_packet_length += ret;
228
/* FIXME: check characters before printing to screen so to not print
229
terminal control characters */
231
/* fprintf(stderr, "decrypted password is: "); */
232
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
233
/* fprintf(stderr, "\n"); */
359
236
/* Delete the GPGME plaintext data buffer */
360
237
gpgme_data_release(dh_plain);
361
return plaintext_length;
238
return new_packet_length;
364
241
static const char * safer_gnutls_strerror (int value) {
365
const char *ret = gnutls_strerror (value); /* Spurious warning */
242
const char *ret = gnutls_strerror (value);
367
244
ret = "(unknown)";
371
/* GnuTLS log function callback */
372
static void debuggnutls(__attribute__((unused)) int level,
374
fprintf(stderr, "GnuTLS: %s", string);
248
void debuggnutls(__attribute__((unused)) int level,
250
fprintf(stderr, "%s", string);
377
static int init_gnutls_global(mandos_context *mc,
378
const char *pubkeyfilename,
379
const char *seckeyfilename){
253
int initgnutls(encrypted_session *es){
383
258
fprintf(stderr, "Initializing GnuTLS\n");
386
ret = gnutls_global_init();
387
if (ret != GNUTLS_E_SUCCESS) {
388
fprintf (stderr, "GnuTLS global_init: %s\n",
389
safer_gnutls_strerror(ret));
261
if ((ret = gnutls_global_init ())
262
!= GNUTLS_E_SUCCESS) {
263
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
394
/* "Use a log level over 10 to enable all debugging options."
397
268
gnutls_global_set_log_level(11);
398
269
gnutls_global_set_log_function(debuggnutls);
401
/* OpenPGP credentials */
402
gnutls_certificate_allocate_credentials(&mc->cred);
403
if (ret != GNUTLS_E_SUCCESS){
404
fprintf (stderr, "GnuTLS memory error: %s\n", /* Spurious
272
/* openpgp credentials */
273
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
274
!= GNUTLS_E_SUCCESS) {
275
fprintf (stderr, "memory error: %s\n",
406
276
safer_gnutls_strerror(ret));
407
gnutls_global_deinit ();
412
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
413
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
281
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
282
" and keyfile %s as GnuTLS credentials\n", certfile,
417
286
ret = gnutls_certificate_set_openpgp_key_file
418
(mc->cred, pubkeyfilename, seckeyfilename,
419
GNUTLS_OPENPGP_FMT_BASE64);
287
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
420
288
if (ret != GNUTLS_E_SUCCESS) {
422
"Error[%d] while reading the OpenPGP key pair ('%s',"
423
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
424
fprintf(stderr, "The GnuTLS error is: %s\n",
290
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
292
ret, certfile, certkey);
293
fprintf(stdout, "The Error is: %s\n",
425
294
safer_gnutls_strerror(ret));
429
/* GnuTLS server initialization */
430
ret = gnutls_dh_params_init(&mc->dh_params);
431
if (ret != GNUTLS_E_SUCCESS) {
432
fprintf (stderr, "Error in GnuTLS DH parameter initialization:"
433
" %s\n", safer_gnutls_strerror(ret));
436
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
437
if (ret != GNUTLS_E_SUCCESS) {
438
fprintf (stderr, "Error in GnuTLS prime generation: %s\n",
439
safer_gnutls_strerror(ret));
443
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
449
gnutls_certificate_free_credentials(mc->cred);
450
gnutls_global_deinit();
451
gnutls_dh_params_deinit(mc->dh_params);
455
static int init_gnutls_session(mandos_context *mc,
456
gnutls_session_t *session){
458
/* GnuTLS session creation */
459
ret = gnutls_init(session, GNUTLS_SERVER);
460
if (ret != GNUTLS_E_SUCCESS){
298
//GnuTLS server initialization
299
if ((ret = gnutls_dh_params_init (&es->dh_params))
300
!= GNUTLS_E_SUCCESS) {
301
fprintf (stderr, "Error in dh parameter initialization: %s\n",
302
safer_gnutls_strerror(ret));
306
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
307
!= GNUTLS_E_SUCCESS) {
308
fprintf (stderr, "Error in prime generation: %s\n",
309
safer_gnutls_strerror(ret));
313
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
315
// GnuTLS session creation
316
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
317
!= GNUTLS_E_SUCCESS){
461
318
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
462
319
safer_gnutls_strerror(ret));
467
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
468
if (ret != GNUTLS_E_SUCCESS) {
469
fprintf(stderr, "Syntax error at: %s\n", err);
470
fprintf(stderr, "GnuTLS error: %s\n",
471
safer_gnutls_strerror(ret));
472
gnutls_deinit (*session);
322
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
323
!= GNUTLS_E_SUCCESS) {
324
fprintf(stderr, "Syntax error at: %s\n", err);
325
fprintf(stderr, "GnuTLS error: %s\n",
326
safer_gnutls_strerror(ret));
477
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
479
if (ret != GNUTLS_E_SUCCESS) {
480
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
330
if ((ret = gnutls_credentials_set
331
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
332
!= GNUTLS_E_SUCCESS) {
333
fprintf(stderr, "Error setting a credentials set: %s\n",
481
334
safer_gnutls_strerror(ret));
482
gnutls_deinit (*session);
486
338
/* ignore client certificate if any. */
487
gnutls_certificate_server_set_request (*session,
339
gnutls_certificate_server_set_request (es->session,
488
340
GNUTLS_CERT_IGNORE);
490
gnutls_dh_set_prime_bits (*session, mc->dh_bits);
342
gnutls_dh_set_prime_bits (es->session, DH_BITS);
495
/* Avahi log function callback */
496
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
497
__attribute__((unused)) const char *txt){}
347
void empty_log(__attribute__((unused)) AvahiLogLevel level,
348
__attribute__((unused)) const char *txt){}
499
/* Called when a Mandos server is found */
500
static int start_mandos_communication(const char *ip, uint16_t port,
501
AvahiIfIndex if_index,
350
int start_mandos_communication(const char *ip, uint16_t port,
351
AvahiIfIndex if_index){
505
union { struct sockaddr in; struct sockaddr_in6 in6; } to;
353
struct sockaddr_in6 to;
354
encrypted_session es;
506
355
char *buffer = NULL;
507
356
char *decrypted_buffer;
508
357
size_t buffer_length = 0;
509
358
size_t buffer_capacity = 0;
510
359
ssize_t decrypted_buffer_size;
513
362
char interface[IF_NAMESIZE];
514
gnutls_session_t session;
516
ret = init_gnutls_session (mc, &session);
522
fprintf(stderr, "Setting up a tcp connection to %s, port %" PRIu16
365
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
526
369
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
533
if(if_indextoname((unsigned int)if_index, interface) == NULL){
375
if(if_indextoname((unsigned int)if_index, interface) == NULL){
534
377
perror("if_indextoname");
537
383
fprintf(stderr, "Binding to interface %s\n", interface);
540
memset(&to, 0, sizeof(to));
541
to.in6.sin6_family = AF_INET6;
542
/* It would be nice to have a way to detect if we were passed an
543
IPv4 address here. Now we assume an IPv6 address. */
544
ret = inet_pton(AF_INET6, ip, &to.in6.sin6_addr);
386
memset(&to,0,sizeof(to)); /* Spurious warning */
387
to.sin6_family = AF_INET6;
388
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
546
390
perror("inet_pton");
550
394
fprintf(stderr, "Bad address: %s\n", ip);
553
to.in6.sin6_port = htons(port); /* Spurious warning */
397
to.sin6_port = htons(port); /* Spurious warning */
555
to.in6.sin6_scope_id = (uint32_t)if_index;
399
to.sin6_scope_id = (uint32_t)if_index;
558
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
560
char addrstr[INET6_ADDRSTRLEN] = "";
561
if(inet_ntop(to.in6.sin6_family, &(to.in6.sin6_addr), addrstr,
562
sizeof(addrstr)) == NULL){
565
if(strcmp(addrstr, ip) != 0){
566
fprintf(stderr, "Canonical address form: %s\n", addrstr);
402
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
403
/* char addrstr[INET6_ADDRSTRLEN]; */
404
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
405
/* sizeof(addrstr)) == NULL){ */
406
/* perror("inet_ntop"); */
408
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
409
/* addrstr, ntohs(to.sin6_port)); */
571
ret = connect(tcp_sd, &to.in, sizeof(to));
413
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
573
415
perror("connect");
577
const char *out = mandos_protocol_version;
580
size_t out_size = strlen(out);
581
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
582
out_size - written));
588
written += (size_t)ret;
589
if(written < out_size){
592
if (out == mandos_protocol_version){
419
ret = initgnutls (&es);
425
gnutls_transport_set_ptr (es.session,
426
(gnutls_transport_ptr_t) tcp_sd);
602
429
fprintf(stderr, "Establishing TLS session with %s\n", ip);
605
gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
608
ret = gnutls_handshake (session);
609
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
432
ret = gnutls_handshake (es.session);
611
434
if (ret != GNUTLS_E_SUCCESS){
613
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
436
fprintf(stderr, "\n*** Handshake failed ***\n");
614
437
gnutls_perror (ret);
620
/* Read OpenPGP packet that contains the wanted password */
443
//Retrieve OpenPGP packet that contains the wanted password
623
446
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
628
buffer_capacity = adjustbuffer(&buffer, buffer_length,
630
if (buffer_capacity == 0){
631
perror("adjustbuffer");
451
if (buffer_length + BUFFER_SIZE > buffer_capacity){
452
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
457
buffer_capacity += BUFFER_SIZE;
636
sret = gnutls_record_recv(session, buffer+buffer_length,
460
ret = gnutls_record_recv
461
(es.session, buffer+buffer_length, BUFFER_SIZE);
643
467
case GNUTLS_E_INTERRUPTED:
644
468
case GNUTLS_E_AGAIN:
646
470
case GNUTLS_E_REHANDSHAKE:
648
ret = gnutls_handshake (session);
649
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
471
ret = gnutls_handshake (es.session);
651
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
473
fprintf(stderr, "\n*** Handshake failed ***\n");
652
474
gnutls_perror (ret);
658
480
fprintf(stderr, "Unknown error while reading data from"
659
" encrypted session with Mandos server\n");
481
" encrypted session with mandos server\n");
661
gnutls_bye (session, GNUTLS_SHUT_RDWR);
483
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
665
buffer_length += (size_t) sret;
487
buffer_length += (size_t) ret;
670
fprintf(stderr, "Closing TLS session\n");
673
gnutls_bye (session, GNUTLS_SHUT_RDWR);
675
491
if (buffer_length > 0){
676
decrypted_buffer_size = pgp_packet_decrypt(mc, buffer,
492
decrypted_buffer_size = pgp_packet_decrypt(buffer,
679
496
if (decrypted_buffer_size >= 0){
681
497
while(written < (size_t) decrypted_buffer_size){
682
498
ret = (int)fwrite (decrypted_buffer + written, 1,
683
499
(size_t)decrypted_buffer_size - written,
745
566
char ip[AVAHI_ADDRESS_STR_MAX];
746
567
avahi_address_snprint(ip, sizeof(ip), address);
748
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
749
PRIu16 ") on port %d\n", name, host_name, ip,
569
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
570
" port %d\n", name, host_name, ip, port);
752
int ret = start_mandos_communication(ip, port, interface, mc);
572
int ret = start_mandos_communication(ip, port, interface);
754
avahi_simple_poll_quit(mc->simple_poll);
758
578
avahi_s_service_resolver_free(r);
761
static void browse_callback( AvahiSServiceBrowser *b,
762
AvahiIfIndex interface,
763
AvahiProtocol protocol,
764
AvahiBrowserEvent event,
768
AVAHI_GCC_UNUSED AvahiLookupResultFlags
771
mandos_context *mc = userdata;
774
/* Called whenever a new services becomes available on the LAN or
775
is removed from the LAN */
779
case AVAHI_BROWSER_FAILURE:
781
fprintf(stderr, "(Avahi browser) %s\n",
782
avahi_strerror(avahi_server_errno(mc->server)));
783
avahi_simple_poll_quit(mc->simple_poll);
786
case AVAHI_BROWSER_NEW:
787
/* We ignore the returned Avahi resolver object. In the callback
788
function we free it. If the Avahi server is terminated before
789
the callback function is called the Avahi server will free the
792
if (!(avahi_s_service_resolver_new(mc->server, interface,
793
protocol, name, type, domain,
794
AVAHI_PROTO_INET6, 0,
795
resolve_callback, mc)))
796
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
797
name, avahi_strerror(avahi_server_errno(mc->server)));
800
case AVAHI_BROWSER_REMOVE:
803
case AVAHI_BROWSER_ALL_FOR_NOW:
804
case AVAHI_BROWSER_CACHE_EXHAUSTED:
806
fprintf(stderr, "No Mandos server found, still searching...\n");
581
static void browse_callback(
582
AvahiSServiceBrowser *b,
583
AvahiIfIndex interface,
584
AvahiProtocol protocol,
585
AvahiBrowserEvent event,
589
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
592
AvahiServer *s = userdata;
593
assert(b); /* Spurious warning */
595
/* Called whenever a new services becomes available on the LAN or
596
is removed from the LAN */
600
case AVAHI_BROWSER_FAILURE:
602
fprintf(stderr, "(Browser) %s\n",
603
avahi_strerror(avahi_server_errno(server)));
604
avahi_simple_poll_quit(simple_poll);
607
case AVAHI_BROWSER_NEW:
608
/* We ignore the returned resolver object. In the callback
609
function we free it. If the server is terminated before
610
the callback function is called the server will free
611
the resolver for us. */
613
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
615
AVAHI_PROTO_INET6, 0,
616
resolve_callback, s)))
617
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
618
avahi_strerror(avahi_server_errno(s)));
621
case AVAHI_BROWSER_REMOVE:
624
case AVAHI_BROWSER_ALL_FOR_NOW:
625
case AVAHI_BROWSER_CACHE_EXHAUSTED:
812
int main(int argc, char *argv[]){
630
/* combinds file name and path and returns the malloced new string. som sane checks could/should be added */
631
const char *combinepath(const char *first, const char *second){
633
tmp = malloc(strlen(first) + strlen(second) + 2);
639
if (first[0] != '\0' and first[strlen(first) - 1] != '/'){
647
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
648
AvahiServerConfig config;
813
649
AvahiSServiceBrowser *sb = NULL;
816
int exitcode = EXIT_SUCCESS;
817
const char *interface = "eth0";
818
struct ifreq network;
652
int returncode = EXIT_SUCCESS;
653
const char *interface = NULL;
654
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
822
655
char *connect_to = NULL;
823
char tempdir[] = "/tmp/mandosXXXXXX";
824
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
825
const char *seckey = PATHDIR "/" SECKEY;
826
const char *pubkey = PATHDIR "/" PUBKEY;
828
mandos_context mc = { .simple_poll = NULL, .server = NULL,
829
.dh_bits = 1024, .priority = "SECURE256"
830
":!CTYPE-X.509:+CTYPE-OPENPGP" };
831
bool gnutls_initalized = false;
832
bool gpgme_initalized = false;
835
struct argp_option options[] = {
836
{ .name = "debug", .key = 128,
837
.doc = "Debug mode", .group = 3 },
838
{ .name = "connect", .key = 'c',
839
.arg = "ADDRESS:PORT",
840
.doc = "Connect directly to a specific Mandos server",
842
{ .name = "interface", .key = 'i',
844
.doc = "Interface that will be used to search for Mandos"
847
{ .name = "seckey", .key = 's',
849
.doc = "OpenPGP secret key file base name",
851
{ .name = "pubkey", .key = 'p',
853
.doc = "OpenPGP public key file base name",
855
{ .name = "dh-bits", .key = 129,
857
.doc = "Bit length of the prime number used in the"
858
" Diffie-Hellman key exchange",
860
{ .name = "priority", .key = 130,
862
.doc = "GnuTLS priority string for the TLS handshake",
867
error_t parse_opt (int key, char *arg,
868
struct argp_state *state) {
869
/* Get the INPUT argument from `argp_parse', which we know is
870
a pointer to our plugin list pointer. */
872
case 128: /* --debug */
875
case 'c': /* --connect */
878
case 'i': /* --interface */
881
case 's': /* --seckey */
884
case 'p': /* --pubkey */
887
case 129: /* --dh-bits */
889
mc.dh_bits = (unsigned int) strtol(arg, NULL, 10);
895
case 130: /* --priority */
903
return ARGP_ERR_UNKNOWN;
908
struct argp argp = { .options = options, .parser = parse_opt,
910
.doc = "Mandos client -- Get and decrypt"
911
" passwords from a Mandos server" };
912
ret = argp_parse (&argp, argc, argv, 0, 0, NULL);
913
if (ret == ARGP_ERR_UNKNOWN){
914
fprintf(stderr, "Unknown error while parsing arguments\n");
915
exitcode = EXIT_FAILURE;
920
/* If the interface is down, bring it up */
922
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
925
exitcode = EXIT_FAILURE;
928
strcpy(network.ifr_name, interface);
929
ret = ioctl(sd, SIOCGIFFLAGS, &network);
931
perror("ioctl SIOCGIFFLAGS");
932
exitcode = EXIT_FAILURE;
935
if((network.ifr_flags & IFF_UP) == 0){
936
network.ifr_flags |= IFF_UP;
937
ret = ioctl(sd, SIOCSIFFLAGS, &network);
939
perror("ioctl SIOCSIFFLAGS");
940
exitcode = EXIT_FAILURE;
944
ret = (int)TEMP_FAILURE_RETRY(close(sd));
963
ret = init_gnutls_global(&mc, pubkey, seckey);
965
fprintf(stderr, "init_gnutls_global failed\n");
966
exitcode = EXIT_FAILURE;
969
gnutls_initalized = true;
972
if(mkdtemp(tempdir) == NULL){
978
if(not init_gpgme(&mc, pubkey, seckey, tempdir)){
979
fprintf(stderr, "gpgme_initalized failed\n");
980
exitcode = EXIT_FAILURE;
983
gpgme_initalized = true;
986
if_index = (AvahiIfIndex) if_nametoindex(interface);
988
fprintf(stderr, "No such interface: \"%s\"\n", interface);
658
static struct option long_options[] = {
659
{"debug", no_argument, (int *)&debug, 1},
660
{"connect", required_argument, 0, 'C'},
661
{"interface", required_argument, 0, 'i'},
662
{"certdir", required_argument, 0, 'd'},
663
{"certkey", required_argument, 0, 'c'},
664
{"certfile", required_argument, 0, 'k'},
667
int option_index = 0;
668
ret = getopt_long (argc, argv, "i:", long_options,
698
certfile = combinepath(certdir, certfile);
699
if (certfile == NULL){
703
if(interface != NULL){
704
if_index = (AvahiIfIndex) if_nametoindex(interface);
706
fprintf(stderr, "No such interface: \"%s\"\n", interface);
992
711
if(connect_to != NULL){
995
714
char *address = strrchr(connect_to, ':');
996
715
if(address == NULL){
997
716
fprintf(stderr, "No colon in address\n");
998
exitcode = EXIT_FAILURE;
1002
720
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
1004
722
perror("Bad port number");
1005
exitcode = EXIT_FAILURE;
1008
725
*address = '\0';
1009
726
address = connect_to;
1010
ret = start_mandos_communication(address, port, if_index, &mc);
727
ret = start_mandos_communication(address, port, if_index);
1012
exitcode = EXIT_FAILURE;
1014
exitcode = EXIT_SUCCESS;
735
certkey = combinepath(certdir, certkey);
736
if (certkey == NULL){
1020
741
avahi_set_log_function(empty_log);
1023
/* Initialize the pseudo-RNG for Avahi */
744
/* Initialize the psuedo-RNG */
1024
745
srand((unsigned int) time(NULL));
1026
/* Allocate main Avahi loop object */
1027
mc.simple_poll = avahi_simple_poll_new();
1028
if (mc.simple_poll == NULL) {
1029
fprintf(stderr, "Avahi: Failed to create simple poll"
1031
exitcode = EXIT_FAILURE;
1036
AvahiServerConfig config;
1037
/* Do not publish any local Zeroconf records */
1038
avahi_server_config_init(&config);
1039
config.publish_hinfo = 0;
1040
config.publish_addresses = 0;
1041
config.publish_workstation = 0;
1042
config.publish_domain = 0;
1044
/* Allocate a new server */
1045
mc.server = avahi_server_new(avahi_simple_poll_get
1046
(mc.simple_poll), &config, NULL,
1049
/* Free the Avahi configuration data */
1050
avahi_server_config_free(&config);
1053
/* Check if creating the Avahi server object succeeded */
1054
if (mc.server == NULL) {
1055
fprintf(stderr, "Failed to create Avahi server: %s\n",
747
/* Allocate main loop object */
748
if (!(simple_poll = avahi_simple_poll_new())) {
749
fprintf(stderr, "Failed to create simple poll object.\n");
754
/* Do not publish any local records */
755
avahi_server_config_init(&config);
756
config.publish_hinfo = 0;
757
config.publish_addresses = 0;
758
config.publish_workstation = 0;
759
config.publish_domain = 0;
761
/* Allocate a new server */
762
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
763
&config, NULL, NULL, &error);
765
/* Free the configuration data */
766
avahi_server_config_free(&config);
768
/* Check if creating the server object succeeded */
770
fprintf(stderr, "Failed to create server: %s\n",
1056
771
avahi_strerror(error));
1057
exitcode = EXIT_FAILURE;
772
returncode = EXIT_FAILURE;
1061
/* Create the Avahi service browser */
1062
sb = avahi_s_service_browser_new(mc.server, if_index,
776
/* Create the service browser */
777
sb = avahi_s_service_browser_new(server, if_index,
1063
778
AVAHI_PROTO_INET6,
1064
779
"_mandos._tcp", NULL, 0,
1065
browse_callback, &mc);
780
browse_callback, server);
1067
782
fprintf(stderr, "Failed to create service browser: %s\n",
1068
avahi_strerror(avahi_server_errno(mc.server)));
1069
exitcode = EXIT_FAILURE;
783
avahi_strerror(avahi_server_errno(server)));
784
returncode = EXIT_FAILURE;
1073
788
/* Run the main loop */
1076
fprintf(stderr, "Starting Avahi loop search\n");
791
fprintf(stderr, "Starting avahi loop search\n");
1079
avahi_simple_poll_loop(mc.simple_poll);
794
avahi_simple_poll_loop(simple_poll);
1084
799
fprintf(stderr, "%s exiting\n", argv[0]);
1087
802
/* Cleanup things */
1089
804
avahi_s_service_browser_free(sb);
1091
if (mc.server != NULL)
1092
avahi_server_free(mc.server);
1094
if (mc.simple_poll != NULL)
1095
avahi_simple_poll_free(mc.simple_poll);
1097
if (gnutls_initalized){
1098
gnutls_certificate_free_credentials(mc.cred);
1099
gnutls_global_deinit ();
1100
gnutls_dh_params_deinit(mc.dh_params);
1103
if(gpgme_initalized){
1104
gpgme_release(mc.ctx);
1107
/* Removes the temp directory used by GPGME */
1108
if(tempdir[0] != '\0'){
1110
struct dirent *direntry;
1111
d = opendir(tempdir);
1116
direntry = readdir(d);
1117
if(direntry == NULL){
1120
if (direntry->d_type == DT_REG){
1121
char *fullname = NULL;
1122
ret = asprintf(&fullname, "%s/%s", tempdir,
1128
ret = unlink(fullname);
1130
fprintf(stderr, "unlink(\"%s\"): %s",
1131
fullname, strerror(errno));
1138
ret = rmdir(tempdir);
807
avahi_server_free(server);
810
avahi_simple_poll_free(simple_poll);