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_communication(char *ip, uint16_t port){
323
struct sockaddr_in6 to;
324
encrypted_session es;
326
char *decrypted_buffer;
327
size_t buffer_length = 0;
328
size_t buffer_capacity = 0;
329
ssize_t decrypted_buffer_size;
333
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
336
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
343
fprintf(stderr, "Binding to interface %s\n", interface);
346
ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, interface, 5);
348
perror("setsockopt bindtodevice");
352
memset(&to,0,sizeof(to));
353
to.sin6_family = AF_INET6;
354
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
360
fprintf(stderr, "Bad address: %s\n", ip);
363
to.sin6_port = htons(port);
364
to.sin6_scope_id = if_nametoindex(interface);
367
fprintf(stderr, "Connection to: %s\n", ip);
370
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
376
ret = initgnutls (&es);
383
gnutls_transport_set_ptr (es.session, (gnutls_transport_ptr_t) tcp_sd);
386
fprintf(stderr, "Establishing tls session with %s\n", ip);
390
ret = gnutls_handshake (es.session);
392
if (ret != GNUTLS_E_SUCCESS){
393
fprintf(stderr, "\n*** Handshake failed ***\n");
399
//Retrieve gpg packet that contains the wanted password
402
fprintf(stderr, "Retrieving pgp encrypted password from %s\n", ip);
406
if (buffer_length + BUFFER_SIZE > buffer_capacity){
407
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
412
buffer_capacity += BUFFER_SIZE;
415
ret = gnutls_record_recv
416
(es.session, buffer+buffer_length, BUFFER_SIZE);
422
case GNUTLS_E_INTERRUPTED:
425
case GNUTLS_E_REHANDSHAKE:
426
ret = gnutls_handshake (es.session);
428
fprintf(stderr, "\n*** Handshake failed ***\n");
435
fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
437
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
441
buffer_length += ret;
445
if (buffer_length > 0){
446
if ((decrypted_buffer_size = gpg_packet_decrypt(buffer, buffer_length, &decrypted_buffer, CERT_ROOT)) >= 0){
447
fwrite (decrypted_buffer, 1, decrypted_buffer_size, stdout);
448
free(decrypted_buffer);
457
fprintf(stderr, "Closing tls session\n");
461
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
464
gnutls_deinit (es.session);
465
gnutls_certificate_free_credentials (es.cred);
466
gnutls_global_deinit ();
470
static AvahiSimplePoll *simple_poll = NULL;
471
static AvahiServer *server = NULL;
473
static void resolve_callback(
474
AvahiSServiceResolver *r,
475
AVAHI_GCC_UNUSED AvahiIfIndex interface,
476
AVAHI_GCC_UNUSED AvahiProtocol protocol,
477
AvahiResolverEvent event,
481
const char *host_name,
482
const AvahiAddress *address,
484
AvahiStringList *txt,
485
AvahiLookupResultFlags flags,
486
AVAHI_GCC_UNUSED void* userdata) {
490
/* Called whenever a service has been resolved successfully or timed out */
493
case AVAHI_RESOLVER_FAILURE:
494
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)));
497
case AVAHI_RESOLVER_FOUND: {
498
char ip[AVAHI_ADDRESS_STR_MAX];
499
avahi_address_snprint(ip, sizeof(ip), address);
501
fprintf(stderr, "Mandos server found at %s on port %d\n", ip, port);
503
int ret = start_mandos_communication(ip, port);
511
avahi_s_service_resolver_free(r);
514
static void browse_callback(
515
AvahiSServiceBrowser *b,
516
AvahiIfIndex interface,
517
AvahiProtocol protocol,
518
AvahiBrowserEvent event,
522
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
525
AvahiServer *s = userdata;
528
/* Called whenever a new services becomes available on the LAN or is removed from the LAN */
532
case AVAHI_BROWSER_FAILURE:
534
fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(server)));
535
avahi_simple_poll_quit(simple_poll);
538
case AVAHI_BROWSER_NEW:
539
/* We ignore the returned resolver object. In the callback
540
function we free it. If the server is terminated before
541
the callback function is called the server will free
542
the resolver for us. */
544
if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_INET6, 0, resolve_callback, s)))
545
fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(s)));
549
case AVAHI_BROWSER_REMOVE:
552
case AVAHI_BROWSER_ALL_FOR_NOW:
553
case AVAHI_BROWSER_CACHE_EXHAUSTED:
558
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
559
AvahiServerConfig config;
560
AvahiSServiceBrowser *sb = NULL;
563
int returncode = EXIT_SUCCESS;
566
static struct option long_options[] = {
567
{"debug", no_argument, (int *)&debug, 1},
568
{"interface", required_argument, 0, 'i'},
571
int option_index = 0;
572
ret = getopt_long (argc, argv, "i:", long_options, &option_index);
590
avahi_set_log_function(empty_log);
593
/* Initialize the psuedo-RNG */
596
/* Allocate main loop object */
597
if (!(simple_poll = avahi_simple_poll_new())) {
598
fprintf(stderr, "Failed to create simple poll object.\n");
603
/* Do not publish any local records */
604
avahi_server_config_init(&config);
605
config.publish_hinfo = 0;
606
config.publish_addresses = 0;
607
config.publish_workstation = 0;
608
config.publish_domain = 0;
610
/* Allocate a new server */
611
server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
613
/* Free the configuration data */
614
avahi_server_config_free(&config);
616
/* Check if creating the server object succeeded */
618
fprintf(stderr, "Failed to create server: %s\n", avahi_strerror(error));
619
returncode = EXIT_FAILURE;
623
/* Create the service browser */
624
if (!(sb = avahi_s_service_browser_new(server, if_nametoindex("eth0"), AVAHI_PROTO_INET6, "_mandos._tcp", NULL, 0, browse_callback, server))) {
625
fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_server_errno(server)));
626
returncode = EXIT_FAILURE;
630
/* Run the main loop */
633
fprintf(stderr, "Starting avahi loop search\n");
636
avahi_simple_poll_loop(simple_poll);
641
fprintf(stderr, "%s exiting\n", argv[0]);
646
avahi_s_service_browser_free(sb);
649
avahi_server_free(server);
652
avahi_simple_poll_free(simple_poll);