2
This file is part of avahi.
4
avahi is free software; you can redistribute it and/or modify it
5
under the terms of the GNU Lesser General Public License as
6
published by the Free Software Foundation; either version 2.1 of the
7
License, or (at your option) any later version.
9
avahi is distributed in the hope that it will be useful, but WITHOUT
10
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12
Public License for more details.
14
You should have received a copy of the GNU Lesser General Public
15
License along with avahi; if not, write to the Free Software
16
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20
#define _LARGEFILE_SOURCE
21
#define _FILE_OFFSET_BITS 64
27
#include <net/if.h> /* if_nametoindex */
29
#include <avahi-core/core.h>
30
#include <avahi-core/lookup.h>
31
#include <avahi-core/log.h>
32
#include <avahi-common/simple-watch.h>
33
#include <avahi-common/malloc.h>
34
#include <avahi-common/error.h>
37
#include <sys/types.h> /* socket(), setsockopt(), inet_pton() */
38
#include <sys/socket.h> /* socket(), setsockopt(), struct sockaddr_in6, struct in6_addr, inet_pton() */
39
#include <gnutls/gnutls.h> /* ALL GNUTLS STUFF */
40
#include <gnutls/openpgp.h> /* gnutls with openpgp stuff */
42
#include <unistd.h> /* close() */
43
#include <netinet/in.h>
44
#include <stdbool.h> /* true */
45
#include <string.h> /* memset */
46
#include <arpa/inet.h> /* inet_pton() */
47
#include <iso646.h> /* not */
50
#include <errno.h> /* perror() */
57
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
59
#define CERTFILE CERT_ROOT "openpgp-client.txt"
60
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
61
#define BUFFER_SIZE 256
65
char *interface = "eth0";
68
gnutls_session_t session;
69
gnutls_certificate_credentials_t cred;
70
gnutls_dh_params_t dh_params;
74
ssize_t gpg_packet_decrypt (char *packet, size_t packet_size, char **new_packet, char *homedir){
75
gpgme_data_t dh_crypto, dh_plain;
79
size_t new_packet_capacity = 0;
80
size_t new_packet_length = 0;
81
gpgme_engine_info_t engine_info;
84
fprintf(stderr, "Attempting to decrypt password from gpg packet\n");
88
gpgme_check_version(NULL);
89
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
91
/* Set GPGME home directory */
92
rc = gpgme_get_engine_info (&engine_info);
93
if (rc != GPG_ERR_NO_ERROR){
94
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
95
gpgme_strsource(rc), gpgme_strerror(rc));
98
while(engine_info != NULL){
99
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
100
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
101
engine_info->file_name, homedir);
104
engine_info = engine_info->next;
106
if(engine_info == NULL){
107
fprintf(stderr, "Could not set home dir to %s\n", homedir);
111
/* Create new GPGME data buffer from packet buffer */
112
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
113
if (rc != GPG_ERR_NO_ERROR){
114
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
115
gpgme_strsource(rc), gpgme_strerror(rc));
119
/* Create new empty GPGME data buffer for the plaintext */
120
rc = gpgme_data_new(&dh_plain);
121
if (rc != GPG_ERR_NO_ERROR){
122
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
123
gpgme_strsource(rc), gpgme_strerror(rc));
127
/* Create new GPGME "context" */
128
rc = gpgme_new(&ctx);
129
if (rc != GPG_ERR_NO_ERROR){
130
fprintf(stderr, "bad gpgme_new: %s: %s\n",
131
gpgme_strsource(rc), gpgme_strerror(rc));
135
/* Decrypt data from the FILE pointer to the plaintext data buffer */
136
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
137
if (rc != GPG_ERR_NO_ERROR){
138
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
139
gpgme_strsource(rc), gpgme_strerror(rc));
144
fprintf(stderr, "decryption of gpg packet succeeded\n");
148
gpgme_decrypt_result_t result;
149
result = gpgme_op_decrypt_result(ctx);
151
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
153
fprintf(stderr, "Unsupported algorithm: %s\n", result->unsupported_algorithm);
154
fprintf(stderr, "Wrong key usage: %d\n", result->wrong_key_usage);
155
if(result->file_name != NULL){
156
fprintf(stderr, "File name: %s\n", result->file_name);
158
gpgme_recipient_t recipient;
159
recipient = result->recipients;
161
while(recipient != NULL){
162
fprintf(stderr, "Public key algorithm: %s\n",
163
gpgme_pubkey_algo_name(recipient->pubkey_algo));
164
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
165
fprintf(stderr, "Secret key available: %s\n",
166
recipient->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
167
recipient = recipient->next;
173
/* Delete the GPGME FILE pointer cryptotext data buffer */
174
gpgme_data_release(dh_crypto);
176
/* Seek back to the beginning of the GPGME plaintext data buffer */
177
gpgme_data_seek(dh_plain, 0, SEEK_SET);
181
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
182
*new_packet = realloc(*new_packet, new_packet_capacity + BUFFER_SIZE);
183
if (*new_packet == NULL){
187
new_packet_capacity += BUFFER_SIZE;
190
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length, BUFFER_SIZE);
191
/* Print the data, if any */
193
/* If password is empty, then a incorrect error will be printed */
197
perror("gpgme_data_read");
200
new_packet_length += ret;
203
/* FIXME: check characters before printing to screen so to not print
204
terminal control characters */
206
/* fprintf(stderr, "decrypted password is: "); */
207
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
208
/* fprintf(stderr, "\n"); */
211
/* Delete the GPGME plaintext data buffer */
212
gpgme_data_release(dh_plain);
213
return new_packet_length;
216
static const char * safer_gnutls_strerror (int value) {
217
const char *ret = gnutls_strerror (value);
223
void debuggnutls(int level, const char* string){
224
fprintf(stderr, "%s", string);
227
int initgnutls(encrypted_session *es){
232
fprintf(stderr, "Initializing gnutls\n");
236
if ((ret = gnutls_global_init ())
237
!= GNUTLS_E_SUCCESS) {
238
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
243
gnutls_global_set_log_level(11);
244
gnutls_global_set_log_function(debuggnutls);
248
/* openpgp credentials */
249
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
250
!= GNUTLS_E_SUCCESS) {
251
fprintf (stderr, "memory error: %s\n", safer_gnutls_strerror(ret));
256
fprintf(stderr, "Attempting to use openpgp certificate %s"
257
" and keyfile %s as gnutls credentials\n", CERTFILE, KEYFILE);
260
ret = gnutls_certificate_set_openpgp_key_file
261
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
262
if (ret != GNUTLS_E_SUCCESS) {
264
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s', '%s')\n",
265
ret, CERTFILE, KEYFILE);
266
fprintf(stdout, "The Error is: %s\n",
267
safer_gnutls_strerror(ret));
271
//Gnutls server initialization
272
if ((ret = gnutls_dh_params_init (&es->dh_params))
273
!= GNUTLS_E_SUCCESS) {
274
fprintf (stderr, "Error in dh parameter initialization: %s\n",
275
safer_gnutls_strerror(ret));
279
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
280
!= GNUTLS_E_SUCCESS) {
281
fprintf (stderr, "Error in prime generation: %s\n",
282
safer_gnutls_strerror(ret));
286
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
288
// Gnutls session creation
289
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
290
!= GNUTLS_E_SUCCESS){
291
fprintf(stderr, "Error in gnutls session initialization: %s\n",
292
safer_gnutls_strerror(ret));
295
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
296
!= GNUTLS_E_SUCCESS) {
297
fprintf(stderr, "Syntax error at: %s\n", err);
298
fprintf(stderr, "Gnutls error: %s\n",
299
safer_gnutls_strerror(ret));
303
if ((ret = gnutls_credentials_set
304
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
305
!= GNUTLS_E_SUCCESS) {
306
fprintf(stderr, "Error setting a credentials set: %s\n",
307
safer_gnutls_strerror(ret));
311
/* ignore client certificate if any. */
312
gnutls_certificate_server_set_request (es->session, GNUTLS_CERT_IGNORE);
314
gnutls_dh_set_prime_bits (es->session, DH_BITS);
319
void empty_log(AvahiLogLevel level, const char *txt){}
321
int start_mandos_communcation(char *ip, uint16_t port){
323
struct sockaddr_in6 to;
324
struct in6_addr ip_addr;
325
encrypted_session es;
327
char *decrypted_buffer;
328
size_t buffer_length = 0;
329
size_t buffer_capacity = 0;
330
ssize_t decrypted_buffer_size;
334
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
337
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
344
fprintf(stderr, "Binding to interface %s\n", interface);
347
ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, interface, 5);
349
perror("setsockopt bindtodevice");
353
memset(&to,0,sizeof(to));
354
to.sin6_family = AF_INET6;
355
ret = inet_pton(AF_INET6, ip, &ip_addr);
361
fprintf(stderr, "Bad address: %s\n", ip);
364
to.sin6_port = htons(port);
365
to.sin6_scope_id = if_nametoindex(interface);
368
fprintf(stderr, "Connection to: %s\n", ip);
371
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
377
ret = initgnutls (&es);
384
gnutls_transport_set_ptr (es.session, (gnutls_transport_ptr_t) tcp_sd);
387
fprintf(stderr, "Establishing tls session with %s\n", ip);
391
ret = gnutls_handshake (es.session);
393
if (ret != GNUTLS_E_SUCCESS){
394
fprintf(stderr, "\n*** Handshake failed ***\n");
400
//Retrieve gpg packet that contains the wanted password
403
fprintf(stderr, "Retrieving pgp encrypted password from %s\n", ip);
407
if (buffer_length + BUFFER_SIZE > buffer_capacity){
408
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
413
buffer_capacity += BUFFER_SIZE;
416
ret = gnutls_record_recv
417
(es.session, buffer+buffer_length, BUFFER_SIZE);
423
case GNUTLS_E_INTERRUPTED:
426
case GNUTLS_E_REHANDSHAKE:
427
ret = gnutls_handshake (es.session);
429
fprintf(stderr, "\n*** Handshake failed ***\n");
436
fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
438
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
442
buffer_length += ret;
446
if (buffer_length > 0){
447
if ((decrypted_buffer_size = gpg_packet_decrypt(buffer, buffer_length, &decrypted_buffer, CERT_ROOT)) >= 0){
448
fwrite (decrypted_buffer, 1, decrypted_buffer_size, stdout);
449
free(decrypted_buffer);
458
fprintf(stderr, "Closing tls session\n");
462
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
465
gnutls_deinit (es.session);
466
gnutls_certificate_free_credentials (es.cred);
467
gnutls_global_deinit ();
471
static AvahiSimplePoll *simple_poll = NULL;
472
static AvahiServer *server = NULL;
474
static void resolve_callback(
475
AvahiSServiceResolver *r,
476
AVAHI_GCC_UNUSED AvahiIfIndex interface,
477
AVAHI_GCC_UNUSED AvahiProtocol protocol,
478
AvahiResolverEvent event,
482
const char *host_name,
483
const AvahiAddress *address,
485
AvahiStringList *txt,
486
AvahiLookupResultFlags flags,
487
AVAHI_GCC_UNUSED void* userdata) {
491
/* Called whenever a service has been resolved successfully or timed out */
494
case AVAHI_RESOLVER_FAILURE:
495
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_server_errno(server)));
498
case AVAHI_RESOLVER_FOUND: {
499
char ip[AVAHI_ADDRESS_STR_MAX];
500
avahi_address_snprint(ip, sizeof(ip), address);
502
fprintf(stderr, "Mandos server found at %s on port %d\n", ip, port);
504
int ret = start_mandos_communcation(ip, port);
512
avahi_s_service_resolver_free(r);
515
static void browse_callback(
516
AvahiSServiceBrowser *b,
517
AvahiIfIndex interface,
518
AvahiProtocol protocol,
519
AvahiBrowserEvent event,
523
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
526
AvahiServer *s = userdata;
529
/* Called whenever a new services becomes available on the LAN or is removed from the LAN */
533
case AVAHI_BROWSER_FAILURE:
535
fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(server)));
536
avahi_simple_poll_quit(simple_poll);
539
case AVAHI_BROWSER_NEW:
540
/* We ignore the returned resolver object. In the callback
541
function we free it. If the server is terminated before
542
the callback function is called the server will free
543
the resolver for us. */
545
if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_INET6, 0, resolve_callback, s)))
546
fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(s)));
550
case AVAHI_BROWSER_REMOVE:
553
case AVAHI_BROWSER_ALL_FOR_NOW:
554
case AVAHI_BROWSER_CACHE_EXHAUSTED:
559
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
560
AvahiServerConfig config;
561
AvahiSServiceBrowser *sb = NULL;
564
int returncode = EXIT_SUCCESS;
567
static struct option long_options[] = {
568
{"debug", no_argument, (int *)&debug, 1},
569
{"interface", required_argument, 0, 'i'},
572
int option_index = 0;
573
ret = getopt_long (argc, argv, "i:", long_options, &option_index);
591
avahi_set_log_function(empty_log);
594
/* Initialize the psuedo-RNG */
597
/* Allocate main loop object */
598
if (!(simple_poll = avahi_simple_poll_new())) {
599
fprintf(stderr, "Failed to create simple poll object.\n");
604
/* Do not publish any local records */
605
avahi_server_config_init(&config);
606
config.publish_hinfo = 0;
607
config.publish_addresses = 0;
608
config.publish_workstation = 0;
609
config.publish_domain = 0;
611
/* Allocate a new server */
612
server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
614
/* Free the configuration data */
615
avahi_server_config_free(&config);
617
/* Check if creating the server object succeeded */
619
fprintf(stderr, "Failed to create server: %s\n", avahi_strerror(error));
620
returncode = EXIT_FAILURE;
624
/* Create the service browser */
625
if (!(sb = avahi_s_service_browser_new(server, if_nametoindex("eth0"), AVAHI_PROTO_INET6, "_mandos._tcp", NULL, 0, browse_callback, server))) {
626
fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_server_errno(server)));
627
returncode = EXIT_FAILURE;
631
/* Run the main loop */
634
fprintf(stderr, "Starting avahi loop search\n");
637
avahi_simple_poll_loop(simple_poll);
642
fprintf(stderr, "%s exiting\n", argv[0]);
647
avahi_s_service_browser_free(sb);
650
avahi_server_free(server);
653
avahi_simple_poll_free(simple_poll);